diff --git a/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from pull_request_template.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..bf61b260b --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,36 @@ +{ + "default": false, + "heading-increment": "atx", + "ul-style": { + "style": "asterisk" + }, + "list-indent": true, + "ul-start-left": true, + "ul-indent": { + "indent": 4 + }, + "no-trailing-spaces": true, + "no-hard-tabs": true, + "no-reversed-links": true, + "no-multiple-blanks": true, + "no-missing-space-atx": true, + "no-multiple-space-atx": true, + "blanks-around-headings": true, + "heading-start-left": true, + "no-trailing-punctuation": { + "punctuation": ".,;:!" + }, + "no-multiple-space-blockquote": true, + "no-blanks-blockquote": true, + "ol-prefix": { + "style": "ordered" + }, + "list-marker-space": true, + "blanks-around-fences": true, + "blanks-around-lists": true, + "no-bare-urls": true, + "hr-style": "---", + "no-space-in-emphasis": true, + "no-space-in-links": true, + "fenced-code-language": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9dc31fb03..fd4da98e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,6 @@ TypeScript-Handbook is accepting contributions. If you've submitted a PR for an Your pull request should: * Include a description of what your change intends to do. -* Have a clear commit messages +* Have clear commit messages * e.g. "New Topic", "Fix typo", "Add code samples" * For code samples, please make sure they compile against the latest released TypeScript compiler version. diff --git a/README.md b/README.md index 539e83cfb..cbab9ddfd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ +# TypeScript-Handbook Repo Deprecated + +The handbook has moved into the new TypeScript website repo, you can find the revised and updated handbook pages in [`/packages/documentation`](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/documentation) in that repo. + +--- + # TypeScript-Handbook [![Build Status](https://travis-ci.org/Microsoft/TypeScript-Handbook.svg)](https://travis-ci.org/Microsoft/TypeScript-Handbook) -The TypeScript Handbook is a comprehensive guide to the TypeScript language +The TypeScript Handbook is a comprehensive guide to the TypeScript language. +It is meant to be read online at [the TypeScript website](https://www.typescriptlang.org/docs/home.html) or [directly from this repository](./pages/Basic%20Types.md). + +For a more formal description of the language, see the [latest TypeScript Language Specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md). + + +## How To Contribute -Please see the [latest TypeScript Language Specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md) for more details. +TypeScript-Handbook is accepting contributions. If you've submitted a PR for an existing issue, please post a comment in the issue to avoid duplication of effort. See our [CONTRIBUTING](/CONTRIBUTING.md) file for more information - it also contains guidelines for how to submit a PR. diff --git a/assets/images/tutorials/aspnet/choosedependencies.png b/assets/images/tutorials/aspnet/choosedependencies.png new file mode 100644 index 000000000..1eb564311 Binary files /dev/null and b/assets/images/tutorials/aspnet/choosedependencies.png differ diff --git a/assets/images/tutorials/aspnet/createwebapp.png b/assets/images/tutorials/aspnet/createwebapp.png new file mode 100644 index 000000000..f48cfa7ad Binary files /dev/null and b/assets/images/tutorials/aspnet/createwebapp.png differ diff --git a/assets/images/tutorials/aspnet/debugger.png b/assets/images/tutorials/aspnet/debugger.png new file mode 100644 index 000000000..3b04bae92 Binary files /dev/null and b/assets/images/tutorials/aspnet/debugger.png differ diff --git a/assets/images/tutorials/aspnet/downloaddependency.png b/assets/images/tutorials/aspnet/downloaddependency.png new file mode 100644 index 000000000..fbca850aa Binary files /dev/null and b/assets/images/tutorials/aspnet/downloaddependency.png differ diff --git a/assets/images/tutorials/aspnet/emptytemplate.png b/assets/images/tutorials/aspnet/emptytemplate.png new file mode 100644 index 000000000..f35c04f63 Binary files /dev/null and b/assets/images/tutorials/aspnet/emptytemplate.png differ diff --git a/assets/images/tutorials/aspnet/namewebapp.png b/assets/images/tutorials/aspnet/namewebapp.png new file mode 100644 index 000000000..d32d1a5c1 Binary files /dev/null and b/assets/images/tutorials/aspnet/namewebapp.png differ diff --git a/assets/images/tutorials/aspnet/new-asp-project-empty.png b/assets/images/tutorials/aspnet/new-asp-project-empty.png deleted file mode 100644 index 70f5b44db..000000000 Binary files a/assets/images/tutorials/aspnet/new-asp-project-empty.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/new-asp-project-template.png b/assets/images/tutorials/aspnet/new-asp-project-template.png deleted file mode 100644 index 80c5e6dd0..000000000 Binary files a/assets/images/tutorials/aspnet/new-asp-project-template.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/new-asp-project.png b/assets/images/tutorials/aspnet/new-asp-project.png deleted file mode 100644 index 3a5381775..000000000 Binary files a/assets/images/tutorials/aspnet/new-asp-project.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/new-folder.png b/assets/images/tutorials/aspnet/new-folder.png deleted file mode 100644 index 0c307e695..000000000 Binary files a/assets/images/tutorials/aspnet/new-folder.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/new-item.png b/assets/images/tutorials/aspnet/new-item.png deleted file mode 100644 index 880a4ceb9..000000000 Binary files a/assets/images/tutorials/aspnet/new-item.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/new-tsconfig.png b/assets/images/tutorials/aspnet/new-tsconfig.png deleted file mode 100644 index 7b4117524..000000000 Binary files a/assets/images/tutorials/aspnet/new-tsconfig.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/newfolder.png b/assets/images/tutorials/aspnet/newfolder.png new file mode 100644 index 000000000..9b09ce840 Binary files /dev/null and b/assets/images/tutorials/aspnet/newfolder.png differ diff --git a/assets/images/tutorials/aspnet/npm.png b/assets/images/tutorials/aspnet/npm.png new file mode 100644 index 000000000..082bff04d Binary files /dev/null and b/assets/images/tutorials/aspnet/npm.png differ diff --git a/assets/images/tutorials/aspnet/open-index.png b/assets/images/tutorials/aspnet/open-index.png deleted file mode 100644 index f3ff7e02e..000000000 Binary files a/assets/images/tutorials/aspnet/open-index.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/packageinstaller-angular2.png b/assets/images/tutorials/aspnet/packageinstaller-angular2.png deleted file mode 100644 index 194a665a0..000000000 Binary files a/assets/images/tutorials/aspnet/packageinstaller-angular2.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/packageinstaller-es6-shim.png b/assets/images/tutorials/aspnet/packageinstaller-es6-shim.png deleted file mode 100644 index 1729bd012..000000000 Binary files a/assets/images/tutorials/aspnet/packageinstaller-es6-shim.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/packageinstaller-systemjs.png b/assets/images/tutorials/aspnet/packageinstaller-systemjs.png deleted file mode 100644 index 2d5b85cc3..000000000 Binary files a/assets/images/tutorials/aspnet/packageinstaller-systemjs.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/packageinstaller-typings.png b/assets/images/tutorials/aspnet/packageinstaller-typings.png deleted file mode 100644 index 5c1e1e04e..000000000 Binary files a/assets/images/tutorials/aspnet/packageinstaller-typings.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/packagejson.png b/assets/images/tutorials/aspnet/packagejson.png new file mode 100644 index 000000000..4f0e985c6 Binary files /dev/null and b/assets/images/tutorials/aspnet/packagejson.png differ diff --git a/assets/images/tutorials/aspnet/paused-demo.png b/assets/images/tutorials/aspnet/paused-demo.png deleted file mode 100644 index e5e5cbbc1..000000000 Binary files a/assets/images/tutorials/aspnet/paused-demo.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/running-demo.png b/assets/images/tutorials/aspnet/running-demo.png deleted file mode 100644 index 70eee0cf7..000000000 Binary files a/assets/images/tutorials/aspnet/running-demo.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/scripts-folder.png b/assets/images/tutorials/aspnet/scripts-folder.png deleted file mode 100644 index 934e25b42..000000000 Binary files a/assets/images/tutorials/aspnet/scripts-folder.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/scripts.png b/assets/images/tutorials/aspnet/scripts.png new file mode 100644 index 000000000..84158170d Binary files /dev/null and b/assets/images/tutorials/aspnet/scripts.png differ diff --git a/assets/images/tutorials/aspnet/src-folder.png b/assets/images/tutorials/aspnet/src-folder.png deleted file mode 100644 index e25af24c4..000000000 Binary files a/assets/images/tutorials/aspnet/src-folder.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/task-runner-explorer.png b/assets/images/tutorials/aspnet/task-runner-explorer.png deleted file mode 100644 index 417f52620..000000000 Binary files a/assets/images/tutorials/aspnet/task-runner-explorer.png and /dev/null differ diff --git a/assets/images/tutorials/aspnet/taskrunner.png b/assets/images/tutorials/aspnet/taskrunner.png new file mode 100644 index 000000000..2ff7b6eaf Binary files /dev/null and b/assets/images/tutorials/aspnet/taskrunner.png differ diff --git a/assets/images/tutorials/aspnet/taskrunnerrefresh.png b/assets/images/tutorials/aspnet/taskrunnerrefresh.png new file mode 100644 index 000000000..727295d81 Binary files /dev/null and b/assets/images/tutorials/aspnet/taskrunnerrefresh.png differ diff --git a/assets/images/tutorials/aspnet/tsconfig.png b/assets/images/tutorials/aspnet/tsconfig.png new file mode 100644 index 000000000..fad4a0058 Binary files /dev/null and b/assets/images/tutorials/aspnet/tsconfig.png differ diff --git a/assets/images/tutorials/aspnet/tsfile.png b/assets/images/tutorials/aspnet/tsfile.png new file mode 100644 index 000000000..ee79517ba Binary files /dev/null and b/assets/images/tutorials/aspnet/tsfile.png differ diff --git a/assets/images/tutorials/aspnet/tsgif.mov b/assets/images/tutorials/aspnet/tsgif.mov new file mode 100644 index 000000000..dbb0c437e Binary files /dev/null and b/assets/images/tutorials/aspnet/tsgif.mov differ diff --git a/assets/images/tutorials/aspnet/workingsite.png b/assets/images/tutorials/aspnet/workingsite.png new file mode 100644 index 000000000..7974c1953 Binary files /dev/null and b/assets/images/tutorials/aspnet/workingsite.png differ diff --git a/attribution.json b/attribution.json new file mode 100644 index 000000000..23dd161fe --- /dev/null +++ b/attribution.json @@ -0,0 +1 @@ +{"pages/Advanced Types.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":65},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":33},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":9},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":6},{"name":"fyodore82","gravatar":"f087ae54318360ef46aa4915eee5bc1b","count":4}],"total":41},"pages/Basic Types.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":53},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":25},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":4},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":4},{"name":"Martin Veith","gravatar":"c14e40955314d925ddde906ee48fc437","count":3}],"total":32},"pages/Classes.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":53},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":27},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":8},{"name":"Brice Wilson","gravatar":"e1dfaa389348fc04d0807cc3d6252491","count":5},{"name":"AbubakerB","gravatar":"0ee4dca5013b6cc64b743efd60427648","count":3}],"total":20},"pages/Compiler Options in MSBuild.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":62},{"name":"Yui","gravatar":"c43ddeea6c2575b4f28e8e8107222501","count":4},{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":4},{"name":"Nathan Shively-Sanders","gravatar":"8c5596e6ef2b41132cee585c9b146116","count":1},{"name":"Nico Sommi","gravatar":"09e3a35ab8733bf18c44b7bfe4095a9d","count":1}],"total":9},"pages/Compiler Options.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":96},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":19},{"name":"Axel D","gravatar":"bdf735cd1dd693d2608a997927cb7e7d","count":14},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":7},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":7}],"total":43},"pages/Configuring Watch.md":{"top":[{"name":"Sheetal Nandi","gravatar":"eaddbe18695ecd437e1fb16b9eca1922","count":52},{"name":"0xflotus","gravatar":"f9d24e528b0f500d275ca815ec471097","count":1},{"name":"Philip Kirkbride","gravatar":"e2581b53db32340d7e1b5be6b99c2bb1","count":1}],"total":3},"pages/Declaration Merging.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":53},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":20},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":10},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":1},{"name":"Oliver THEBAULT","gravatar":"2d68f6160c2d4eef854c155532213102","count":1}],"total":13},"pages/Decorators.md":{"top":[{"name":"Ron Buckton","gravatar":"745f702d55c379fb824f5a03e0651e78","count":54},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":3},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":1},{"name":"John Spurlock","gravatar":"9196b648a4f24597e0423bda1db0e50b","count":1},{"name":"Keeley Hammond","gravatar":"dc7a3bb00dc978dd734f86b3c0636335","count":1}],"total":13},"pages/Enums.md":{"top":[{"name":"Vladimir Matveev","gravatar":"5aee15af22b78f239d28323e4194ca43","count":53},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":5},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":3},{"name":"Steven","gravatar":"e281254dfd07bc4571d2cc8910812494","count":3},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":2}],"total":15},"pages/Functions.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":55},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":23},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":4},{"name":"Evan Carroll","gravatar":"605442f85418d858e2ce1e1aea2092bb","count":1},{"name":"Nathan Brown","gravatar":"4a7bdcca720edf514af0aa48e9be5983","count":1}],"total":19},"pages/Generics.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"b9d274ea3df40f132936da582f71b18f","count":51},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":19},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":5},{"name":"Rick Carlino","gravatar":"f8b1ebd488deae76d970e7944fbae8e5","count":4},{"name":"Omar Boukli-Hacene","gravatar":"f0a423bd49afd126e1ae2f3b5d271ba7","count":1}],"total":12},"pages/Integrating with Build Tools.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Maurice de Beijer","gravatar":"974aa89116fa46b6895f67236546f417","count":6},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":3},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":3},{"name":"Dan Marshall","gravatar":"f4b952649f8547c67b43d41985b90892","count":2}],"total":10},"pages/Interfaces.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":55},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":25},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":4},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":3},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":3}],"total":35},"pages/Iterators and Generators.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":57},{"name":"Gabriel Burdeti","gravatar":"28ff7ada33ca80bfae575ae1321176ca","count":3},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":1},{"name":"Kevin Sanders","gravatar":"21bb691b777fca0080f345a41c6c38c4","count":1},{"name":"Misaka","gravatar":"93d34858ab51a2ac9614e3777fb3766e","count":1}],"total":6},"pages/JSDoc Supported Types.md":{"top":[{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":52}],"total":1},"pages/JSX.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":55},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":6},{"name":"David Zulaica","gravatar":"da0a821c09edbd8369777d622b446cc2","count":3},{"name":"Kanchalai Tanglertsampan","gravatar":"c8404f96910ba93ddc3e881b4bff24a4","count":3},{"name":"Jun Wan Goh","gravatar":"66910086db379e07e6bf44eea7af3e4c","count":2}],"total":24},"pages/Mixins.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":52},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":7},{"name":"Vlad Balin","gravatar":"3fb10669d3d7ca93943af430039517e7","count":3},{"name":"Shingo Yamazaki","gravatar":"50fc0fdc2327ecbe7350645812de52e3","count":1},{"name":"rizalwildan","gravatar":"c8ad161319a0128539ba65ed898392cb","count":1}],"total":9},"pages/Module Resolution.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":70},{"name":"Andy Hanson","gravatar":"53f3bf1fab05451f834d0995fa07e47a","count":6},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":6},{"name":"Dan Marshall","gravatar":"f4b952649f8547c67b43d41985b90892","count":3},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":2}],"total":23},"pages/Modules.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":73},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":22},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":6},{"name":"Andy Hanson","gravatar":"53f3bf1fab05451f834d0995fa07e47a","count":5},{"name":"Gabriel Burdeti","gravatar":"28ff7ada33ca80bfae575ae1321176ca","count":4}],"total":30},"pages/Namespaces and Modules.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":63},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":19},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"William Shepherd","gravatar":"376fd9f7baa5237d630d53fbf40963c9","count":1},{"name":"Lukas Tetzlaff","gravatar":"dcaaa546f4d6e99652c39c1fc69442e4","count":1}],"total":11},"pages/Namespaces.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":56},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":3},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Shingo Yamazaki","gravatar":"50fc0fdc2327ecbe7350645812de52e3","count":1},{"name":"Julian Gong","gravatar":"fa260063391b9c0adda0d3451bc91799","count":1}],"total":12},"pages/Nightly Builds.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"StefanRein","gravatar":"078daaad2c37d829a53e4db1827c0961","count":2},{"name":"macdja38","gravatar":"80a6ce7abf27e4f9eced990853689b4d","count":1}],"total":3},"pages/Project References.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":3},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":2},{"name":"Jake Ginnivan","gravatar":"49bd7a628b1d66a3e70c6550cecc92a3","count":1},{"name":"reduckted","gravatar":"8c2e5181520e296f3c832ecd11485169","count":1}],"total":11},"pages/Symbols.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Gabriel Burdeti","gravatar":"28ff7ada33ca80bfae575ae1321176ca","count":2},{"name":"f","gravatar":"c329e72c364be1b3279f538bce4b025f","count":1},{"name":"user135711","gravatar":"b2861419ffde55d6b31a7da6980e0b9a","count":1},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":1}],"total":5},"pages/Triple-Slash Directives.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":58},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":1},{"name":"Kevin Sanders","gravatar":"21bb691b777fca0080f345a41c6c38c4","count":1},{"name":"Marc Kassay","gravatar":"365e118c75387fcbb180c5f41c4e1314","count":1},{"name":"dennispg","gravatar":"36f7c310063cc77cbd099763f3347885","count":1}],"total":6},"pages/Type Checking JavaScript Files.md":{"top":[{"name":"Harry Nguyen","gravatar":"386e27141a29f5c1076619afc140ffb4","count":51},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":3},{"name":"Nathan Shively-Sanders","gravatar":"8c5596e6ef2b41132cee585c9b146116","count":2},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":2},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":13},"pages/Type Compatibility.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"b9d274ea3df40f132936da582f71b18f","count":51},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":19},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":4},{"name":"Omar Boukli-Hacene","gravatar":"f0a423bd49afd126e1ae2f3b5d271ba7","count":3},{"name":"Lifu Huang","gravatar":"7a059c64ee0fef1c596016f137b29fb7","count":2}],"total":14},"pages/Type Inference.md":{"top":[{"name":"Ryan Cavanaugh","gravatar":"b9d274ea3df40f132936da582f71b18f","count":51},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":10},{"name":"Martin Hanzel","gravatar":"306526665628a202cb974484101d3bcf","count":2},{"name":"John Jago","gravatar":"302329c4365a60e401442b1001adaf39","count":1},{"name":"Daniel Karp","gravatar":"4de779c6c2aa36c83ab9277b8be6ade2","count":1}],"total":7},"pages/Typings for NPM Packages.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":55},{"name":"Daniel Rosenwasser","gravatar":"8499bf678149d617cc71a23afb377736","count":3},{"name":"Wesley Wigham","gravatar":"b07f8059b2727c6b084bd0dc921118b9","count":1}],"total":3},"pages/Utility Types.md":{"top":[{"name":"christian","gravatar":"1f58f226873fbbebcb8d4741b56fc99c","count":54},{"name":"Ravshan Samandarov","gravatar":"a988ffa9b684e82b27d452373e5b6196","count":1},{"name":"David Sommerich","gravatar":"8c053f60eb5c32330a514ddda351259a","count":1},{"name":"David","gravatar":"26995695b6337ebf150734249dd45ee8","count":1},{"name":"Gerrit Birkeland","gravatar":"0dce420a6e88cb3f4a8aafd874123cd2","count":1}],"total":8},"pages/Variable Declarations.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":58},{"name":"Nathan Shively-Sanders","gravatar":"f2d3b194d100bd25842ca048ab101408","count":9},{"name":"Vimal Raghubir","gravatar":"9bd70673d4a0c3ab8461871af1896215","count":3},{"name":"Brett Cannon","gravatar":"962cb4064811fd8c78dfc01eb27d4871","count":3},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":2}],"total":19},"pages/Writing Declaration Files.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":54},{"name":"0xflotus","gravatar":"f9d24e528b0f500d275ca815ec471097","count":1},{"name":"Andy Hanson","gravatar":"53f3bf1fab05451f834d0995fa07e47a","count":1},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":1},{"name":"wanderer06","gravatar":"1dce2f9e605ce3114edeaa2670065d0a","count":1}],"total":6},"pages/declaration files/By Example.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":57},{"name":"ydz-one","gravatar":"a1cf7e5d7e4c2f73a2789d9d1d3fe001","count":1},{"name":"Pylyp Borysov","gravatar":"a89473e7a895bc87515a5b3e9fbe207d","count":1},{"name":"Rafał Krupiński","gravatar":"20c66d3b08e6e3d858973b2a1c66d9a8","count":1},{"name":"Krzysztof Piasecki","gravatar":"42ee56a548ee6259f9e44a66d1c3aa61","count":1}],"total":6},"pages/declaration files/Consumption.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"noymer","gravatar":"5b83a7075a76f5eef367c73d8b268a79","count":1},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":1}],"total":3},"pages/declaration files/Deep Dive.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":54}],"total":1},"pages/declaration files/Do's and Don'ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":55},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":1},{"name":"Veniamin Krol","gravatar":"e05a27c24badea56b8a2110ed0028c58","count":1},{"name":"Sean","gravatar":"9a3132e47b67a9bbe01a23dcdaf7b1a1","count":1},{"name":"jaceclowdus","gravatar":"f11cc04b135c54e90ed4cb9b94d8def1","count":1}],"total":9},"pages/declaration files/Introduction.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":61},{"name":"Crhistian Ramirez","gravatar":"8e1b864790dd753f3474527c64201618","count":1},{"name":"Valera Rozuvan","gravatar":"937a84db1c29347e9c165db225c1ed79","count":1},{"name":"Jeremy Foster","gravatar":"c2726bd2cd11f19f5a75fb4f8aae8524","count":1},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":1}],"total":5},"pages/declaration files/Library Structures.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":57},{"name":"Daniel Rose","gravatar":"a818e7f65a790fe4ba3da95110749a30","count":3},{"name":"Kenrick","gravatar":"ae94bfe2904e0c4ecf8fab1f05092ed1","count":1},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":1}],"total":4},"pages/declaration files/Publishing.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":55},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":2},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":2},{"name":"Wolfgang Ederer","gravatar":"b50d03f76efe75003adb63c5c7649842","count":1},{"name":"Wesley Wigham","gravatar":"b07f8059b2727c6b084bd0dc921118b9","count":1}],"total":9},"pages/declaration files/Templates.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53}],"total":1},"pages/declaration files/templates/global-modifying-module.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53}],"total":1},"pages/declaration files/templates/global-plugin.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Johnny","gravatar":"d33dd700ab180cf167ec9b6a5c510510","count":1}],"total":2},"pages/declaration files/templates/global.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53}],"total":1},"pages/declaration files/templates/module-class.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Daniel Rose","gravatar":"a818e7f65a790fe4ba3da95110749a30","count":1}],"total":2},"pages/declaration files/templates/module-function.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Daniel Rose","gravatar":"a818e7f65a790fe4ba3da95110749a30","count":1}],"total":2},"pages/declaration files/templates/module-plugin.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53}],"total":1},"pages/declaration files/templates/module.d.ts.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53}],"total":1},"pages/release notes/Overview.md":{"top":[{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":54},{"name":"Jake","gravatar":"31e55836947e29445f1d7bc9588268c7","count":1}],"total":2},"pages/release notes/TypeScript 1.1.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 1.3.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 1.4.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 1.5.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Shawn Choi","gravatar":"6d91293b9dbb67211758818877635560","count":1},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":1},{"name":"ferhat elmas","gravatar":"3e970e0e5d130ae179f5cfa9b3079b76","count":1}],"total":6},"pages/release notes/TypeScript 1.6.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"neverRare","gravatar":"76f1fcc4b1158d1f3fe24b286d3c2867","count":1},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":3},"pages/release notes/TypeScript 1.7.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"SlurpTheo","gravatar":"5acec65c2e9d1fb43f158bab22f79418","count":1}],"total":3},"pages/release notes/TypeScript 1.8.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Achim Weimert","gravatar":"b8f0329cbb6cc9c7c6b08b2c48d5662f","count":1},{"name":"Volodymyr Pantasenko","gravatar":"63c9580b21ccec0970ee322fb9cd5360","count":1}],"total":4},"pages/release notes/TypeScript 2.0.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Vallentin","gravatar":"cd86a6f79d7328127ae03645d14d0e64","count":1}],"total":3},"pages/release notes/TypeScript 2.1.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":58},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Jaime","gravatar":"1dfad8107453e0f493ff1a6a8e48d503","count":1},{"name":"Steven","gravatar":"e281254dfd07bc4571d2cc8910812494","count":1},{"name":"Omar Gaayeb","gravatar":"03f75fa297ff893cfcfc8deb1e83d220","count":1}],"total":6},"pages/release notes/TypeScript 2.2.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 2.3.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":51},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Lars Reimann","gravatar":"d75b9305d0763a19dc0305a8c4f49546","count":1},{"name":"Oblosys","gravatar":"c08d37535f8b1f2d7c5785cfd21067b9","count":1},{"name":"Per Lundberg","gravatar":"e3b31c81a5f8c5dff8298a5e51f7dc29","count":1}],"total":5},"pages/release notes/TypeScript 2.4.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 2.5.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":51},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 2.6.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":51},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 2.7.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Cameron Tacklind","gravatar":"719f14270bfe54a8a9cf6e831fde6aa2","count":1},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":1},{"name":"ferhat elmas","gravatar":"3e970e0e5d130ae179f5cfa9b3079b76","count":1}],"total":7},"pages/release notes/TypeScript 2.8.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":55},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":1},{"name":"Wesley Wigham","gravatar":"b07f8059b2727c6b084bd0dc921118b9","count":1}],"total":4},"pages/release notes/TypeScript 2.9.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":51},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 3.0.md":{"top":[{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":53},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":2},{"name":"Ruslan Iusupov","gravatar":"e4eb89fda2c3d3f54efa810a9ba9b0da","count":2},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Achim Weimert","gravatar":"b8f0329cbb6cc9c7c6b08b2c48d5662f","count":1}],"total":8},"pages/release notes/TypeScript 3.1.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":51},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":3},{"name":"Sebastian Silbermann","gravatar":"8909c1e1d9b64fd5db83ca8e0d7e16a5","count":1},{"name":"Veniamin Krol","gravatar":"e05a27c24badea56b8a2110ed0028c58","count":1},{"name":"Achim Weimert","gravatar":"b8f0329cbb6cc9c7c6b08b2c48d5662f","count":1}],"total":8},"pages/release notes/TypeScript 3.2.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":52},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1}],"total":2},"pages/release notes/TypeScript 3.3.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":51},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Riley Avron","gravatar":"ed9a8af14efbb5b912454280c1419fc3","count":1}],"total":3},"pages/release notes/TypeScript 3.4.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":51},{"name":"ExE Boss","gravatar":"c6742bf17f729d9ec2f9e3cdddeb4bfa","count":1}],"total":2},"pages/release notes/TypeScript 3.5.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":51}],"total":1},"pages/release notes/TypeScript 3.6.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":51},{"name":"Sergei Osipov","gravatar":"bff06c5f4b1be669e2c0241fd04a5c5b","count":1}],"total":2},"pages/release notes/TypeScript 3.7.md":{"top":[{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":55},{"name":"Jake","gravatar":"31e55836947e29445f1d7bc9588268c7","count":3},{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":1},{"name":"Greg Murray","gravatar":"f01ffd250231ff6b0b79d0d400d58780","count":1},{"name":"Jamie B","gravatar":"3fd341dda3105fdfdb4590574c3e0d51","count":1}],"total":5},"pages/release notes/TypeScript 3.8.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":51}],"total":1},"pages/release notes/index.html":{"top":[{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":51}],"total":1},"pages/tutorials/ASP.NET Core.md":{"top":[{"name":"Bowden Kelly","gravatar":"e87ecf3e2da8b667dd3a8005ac610d4a","count":56},{"name":"Gabrielle Crevecoeur","gravatar":"1d1c3d0dff776fedc6a4f10cd7824314","count":11},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":3},{"name":"Limin Zhu","gravatar":"6ff09ff871238f23c607356e5eee9dd1","count":2},{"name":"Vadim","gravatar":"ff86e0bf36b4276cf463309db564486f","count":1}],"total":8},"pages/tutorials/Angular.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":52}],"total":1},"pages/tutorials/Gulp.md":{"top":[{"name":"Bowden Kelly","gravatar":"e87ecf3e2da8b667dd3a8005ac610d4a","count":51},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":3},{"name":"Ryan Cavanaugh","gravatar":"2484d99c8a58bc51ae587e07a05ba6e2","count":2},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":1},{"name":"Brian","gravatar":"cd0bd0fb8907d12cdf894db4f25fa8ec","count":1}],"total":16},"pages/tutorials/Migrating from JavaScript.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":57},{"name":"Maayan Glikser","gravatar":"3a615b34ef2060face8fcd481c6377e1","count":3},{"name":"Sean","gravatar":"9a3132e47b67a9bbe01a23dcdaf7b1a1","count":1},{"name":"William Sun","gravatar":"2ad06c6816518e3738f323eda1aabb96","count":1},{"name":"Bowden Kelly","gravatar":"e87ecf3e2da8b667dd3a8005ac610d4a","count":1}],"total":10},"pages/tutorials/React & Webpack.md":{"top":[{"name":"Bowden Kelly","gravatar":"e87ecf3e2da8b667dd3a8005ac610d4a","count":52},{"name":"Daniel Rosenwasser","gravatar":"cd1cc3769958ccc22b86d6a87badfe31","count":13},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":3},{"name":"Jason Jarrett","gravatar":"b92a22c70f03a3218b358cfeeb566ac4","count":3},{"name":"Matthias Bertram","gravatar":"a98073b1d569bc19e55a748ab0e8a61b","count":2}],"total":26},"pages/tutorials/React.md":{"top":[{"name":"Daniel Rosenwasser","gravatar":"96588baac26f6b833dfd1ed0cd084287","count":60},{"name":"Brooks Becton","gravatar":"adc0bb4710a7ca48ed5e30a5679af3b3","count":1},{"name":"Orta Therox","gravatar":"28e997da43c10d99aa99273b48efe8cf","count":1},{"name":"Achim Weimert","gravatar":"b8f0329cbb6cc9c7c6b08b2c48d5662f","count":1},{"name":"Sean","gravatar":"9a3132e47b67a9bbe01a23dcdaf7b1a1","count":1}],"total":10},"pages/tutorials/TypeScript in 5 minutes.md":{"top":[{"name":"Bowden Kelly","gravatar":"e87ecf3e2da8b667dd3a8005ac610d4a","count":53},{"name":"Lucas Garron","gravatar":"301e7905e37fa2fc88b1f42b38c026d9","count":1},{"name":"ferhat elmas","gravatar":"3e970e0e5d130ae179f5cfa9b3079b76","count":1},{"name":"prateekgoel","gravatar":"a1bf4353a470b765cadc852b1e718315","count":1},{"name":"Mohamed Hegazy","gravatar":"17e2da9785d45119a4c4cfed99e40d9c","count":1}],"total":6},"pages/tutorials/tsconfig.json.md":{"top":[{"name":"dumblole","gravatar":"4c1e32a3cf6acd5d71065cbce44b6607","count":51},{"name":"Jon Surrell","gravatar":"c925b2538b1f594a81f6ef2922c15e1d","count":2},{"name":"Orta Therox","gravatar":"f116cb3be23153ec08b94e8bd4dbcfeb","count":1},{"name":"Sean","gravatar":"9a3132e47b67a9bbe01a23dcdaf7b1a1","count":1}],"total":4}} \ No newline at end of file diff --git a/createAttributionJSON.js b/createAttributionJSON.js new file mode 100644 index 000000000..c652ba3c0 --- /dev/null +++ b/createAttributionJSON.js @@ -0,0 +1,100 @@ +const { execSync } = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const crypto = require('crypto'); + +/** + Updates the JSON file `attribution.json` with contributors based on commits to files, to run: + + node createAttributionJSON.js +*/ +const handleDupeNames = (name) => { + if (name === "Orta") return "Orta Therox" + return name +} + +// Being first gets you a free x commits +const getOriginalAuthor = filepath => { + const creator = execSync(`git log --format='%an | %aE' --diff-filter=A -- "${filepath}"`) + .toString() + .trim(); + return { + name: creator.split(" | ")[0], + email: creator.split(" | ")[1] + }; +}; + +// Gets the rest of the authors for a file +const getAuthorsForFile = filepath => { + const cmd = `git log --format='%an | %aE' -- "${filepath}"` + const contributors = execSync(cmd).toString().trim() + + const allContributions = contributors.split("\n").map(c => { + return { + name: handleDupeNames(c.split(" | ")[0]), + email: c.split(" | ")[1] + }; + }); + + // Keep a map of all found authors, + const objs = new Map() + allContributions.forEach(c => { + const id = c.name.toLowerCase().replace(/\s/g, "") + const existing = objs.get(id) + if (existing) { + objs.set(id, { name: c.name, gravatar: existing.gravatar, count: existing.count + 1 }) + } else { + const email = c.email || "NOOP" + objs.set(id, { name: c.name, gravatar: crypto.createHash('md5').update(email).digest('hex'), count: 1 }) + } + }) + + return [...objs.values()] +}; + +const allFiles = recursiveReadDirSync("pages") +// const allFiles = ["pages/JSDoc Supported Types.md"]; + +const json = {} + +allFiles.forEach(f => { + const first = getOriginalAuthor(f); + const rest = getAuthorsForFile(f) + + const firstInRest = rest.find(a => a.name === first.name) + firstInRest.count += 50 + + rest.sort((l, r) => r.count - l.count) + console.log(" - " + f + " (" + rest.length + ")") + json[f] = { top: rest.slice(0, 5), total: rest.length } +}); + +fs.writeFileSync("attribution.json", JSON.stringify(json)) + + + + +/** Recursively retrieve file paths from a given folder and its subfolders. */ +// https://gist.github.com/kethinov/6658166#gistcomment-2936675 +/** @returns {string[]} */ +function recursiveReadDirSync(folderPath) { + if (!fs.existsSync(folderPath)) return [] + + const entryPaths = fs + .readdirSync(folderPath) + .map(entry => path.join(folderPath, entry)) + + const filePaths = entryPaths.filter(entryPath => + fs.statSync(entryPath).isFile() + ) + const dirPaths = entryPaths.filter( + entryPath => !filePaths.includes(entryPath) + ) + const dirFiles = dirPaths.reduce( + (prev, curr) => prev.concat(recursiveReadDirSync(curr)), + [] + ) + + return [...filePaths, ...dirFiles] + .filter(f => !f.endsWith(".DS_Store") && !f.endsWith("README.md")) +} diff --git a/lint.js b/lint.js index e1ba19674..ca50738a2 100644 --- a/lint.js +++ b/lint.js @@ -2,48 +2,12 @@ var markdownlint = require("markdownlint"); var glob = require("glob"); var fs = require("fs"); +var markdownConfig = require("./.markdownlint.json"); + var inputFiles = glob.sync("**/*.md", { ignore: "node_modules/**/*" }); var options = { files: inputFiles, - config: { - MD001: false, // Header levels should only increment by one level at a time - MD002: false, // First header should be a h1 header - MD003: "atx", // Header style - MD004: { style: "asterisk" }, // Unordered list style - MD005: true, // Inconsistent indentation for list items at the same level - MD006: true, // Consider starting bulleted lists at the beginning of the line - MD007: { indent: 4 }, // Unordered list indentation - MD009: true, // Trailing spaces - MD010: true, // Hard tabs - MD011: true, // Reversed link syntax - MD012: true, // Multiple consecutive blank lines - MD013: false, // Line length - MD014: false, // Dollar signs used before commands without showing output - MD018: true, // No space after hash on atx style header - MD019: true, // Multiple spaces after hash on atx style header - MD020: false, // No space inside hashes on closed atx style header - MD021: false, // Multiple spaces inside hashes on closed atx style header - MD022: true, // Headers should be surrounded by blank lines - MD023: true, // Headers must start at the beginning of the line - MD024: false, // Multiple headers with the same content - MD025: false, // Multiple top level headers in the same document - MD026: { punctuation: ".,;:!" }, // Trailing punctuation in header - MD027: true, // Multiple spaces after blockquote symbol - MD028: true, // Blank line inside blockquote - MD029: { style: "ordered" }, // Ordered list item prefix - MD030: true, // Spaces after list markers - MD031: true, // Fenced code blocks should be surrounded by blank lines - MD032: true, // Lists should be surrounded by blank lines - MD033: false, // Inline HTML - MD034: true, // Bare URL used - MD035: "---", // Horizontal rule style - MD036: false, // Emphasis used instead of a header - MD037: true, // Spaces inside emphasis markers - MD038: false, // Spaces inside code span elements - MD039: true, // Spaces inside link text - MD040: true, // Fenced code blocks should have a language specified - MD041: false, // First line in file should be a top level header - } + config: markdownConfig, }; var result = markdownlint.sync(options); @@ -61,7 +25,7 @@ Object.keys(result).forEach(function (file) { inputFiles.forEach(function(fileName) { var text = fs.readFileSync(fileName, "utf8") exitCode += checkForImproperlyIndentedFencedCodeBlocks(fileName, text); -}) +}); process.exit(exitCode); @@ -114,4 +78,4 @@ function getCorrectStartingColumnForLine(lines, lineIndex) { } return 0; -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..3e661c19b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,160 @@ +{ + "name": "typescript-handbook", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "linkify-it": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz", + "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdownlint": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.11.0.tgz", + "integrity": "sha512-wE5WdKD6zW2DQaPQ5TFBTXh5j76DnWd/IFffnDQgHmi6Y61DJXBDfLftZ/suJHuv6cwPjM6gKw2GaRLJMOR+Mg==", + "dev": true, + "requires": { + "markdown-it": "8.4.2" + } + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "uc.micro": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz", + "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json index dfc5fc610..979286251 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "homepage": "https://github.com/Microsoft/TypeScript-Handbook#readme", "devDependencies": { - "glob": "^5.0.15", - "markdownlint": "0.0.8" + "glob": "7.1.3", + "markdownlint": "0.11.0" } } diff --git a/pages/Advanced Types.md b/pages/Advanced Types.md index 932c3934e..10c96566e 100644 --- a/pages/Advanced Types.md +++ b/pages/Advanced Types.md @@ -1,5 +1,93 @@ +# Table of contents + +[Intersection Types](#intersection-types) + +[Union Types](#union-types) + +[Type Guards and Differentiating Types](#type-guards-and-differentiating-types) +* [User-Defined Type Guards](#user-defined-type-guards) + * [Using type predicates](#using-type-predicates) + * [Using the `in` operator](#using-the-in-operator) +* [`typeof` type guards](#typeof-type-guards) +* [`instanceof` type guards](#instanceof-type-guards) + +[Nullable types](#nullable-types) +* [Optional parameters and properties](#optional-parameters-and-properties) +* [Type guards and type assertions](#type-guards-and-type-assertions) + +[Type Aliases](#type-aliases) +* [Interfaces vs. Type Aliases](#interfaces-vs-type-aliases) + +[String Literal Types](#string-literal-types) + +[Numeric Literal Types](#numeric-literal-types) + +[Enum Member Types](#enum-member-types) + +[Discriminated Unions](#discriminated-unions) +* [Exhaustiveness checking](#exhaustiveness-checking) + +[Polymorphic `this` types](#polymorphic-this-types) + +[Index types](#index-types) +* [Index types and index signatures](#index-types-and-index-signatures) + +[Mapped types](#mapped-types) +* [Inference from mapped types](#inference-from-mapped-types) + +[Conditional Types](#conditional-types) +* [Distributive conditional types](#distributive-conditional-types) +* [Type inference in conditional types](#type-inference-in-conditional-types) +* [Predefined conditional types](#predefined-conditional-types) + +# Intersection Types + +An intersection type combines multiple types into one. +This allows you to add together existing types to get a single type that has all the features you need. +For example, `Person & Serializable & Loggable` is a `Person` *and* `Serializable` *and* `Loggable`. +That means an object of this type will have all members of all three types. + +You will mostly see intersection types used for mixins and other concepts that don't fit in the classic object-oriented mold. +(There are a lot of these in JavaScript!) +Here's a simple example that shows how to create a mixin: + +```ts +function extend(first: First, second: Second): First & Second { + const result: Partial = {}; + for (const prop in first) { + if (first.hasOwnProperty(prop)) { + (result as First)[prop] = first[prop]; + } + } + for (const prop in second) { + if (second.hasOwnProperty(prop)) { + (result as Second)[prop] = second[prop]; + } + } + return result as First & Second; +} + +class Person { + constructor(public name: string) { } +} + +interface Loggable { + log(name: string): void; +} + +class ConsoleLogger implements Loggable { + log(name) { + console.log(`Hello, I'm ${name}.`); + } +} + +const jim = extend(new Person('Jim'), ConsoleLogger.prototype); +jim.log(jim.name); +``` + # Union Types +Union types are closely related to intersection types, but they are used very differently. Occasionally, you'll run into a library that expects a parameter to be either a `number` or a `string`. For instance, take the following function: @@ -16,7 +104,7 @@ function padLeft(value: string, padding: any) { if (typeof padding === "string") { return padding + value; } - throw new Error(`Expected string or number, got '${value}'.`); + throw new Error(`Expected string or number, got '${padding}'.`); } padLeft("Hello world", 4); // returns " Hello world" @@ -32,7 +120,7 @@ let indentedString = padLeft("Hello world", true); // passes at compile time, fa In traditional object-oriented code, we might abstract over the two types by creating a hierarchy of types. While this is much more explicit, it's also a little bit overkill. One of the nice things about the original version of `padLeft` was that we were able to just pass in primitives. -That meant that usage was simple and not overly verbose. +That meant that usage was simple and concise. This new approach also wouldn't help if we were just trying to use a function that already exists elsewhere. Instead of `any`, we can use a *union type* for the `padding` parameter: @@ -105,11 +193,10 @@ To get the same code working, we'll need to use a type assertion: ```ts let pet = getSmallPet(); -if ((pet).swim) { - (pet).swim(); -} -else { - (pet).fly(); +if ((pet as Fish).swim) { + (pet as Fish).swim(); +} else if ((pet as Bird).fly) { + (pet as Bird).fly(); } ``` @@ -120,11 +207,14 @@ It would be much better if once we performed the check, we could know the type o It just so happens that TypeScript has something called a *type guard*. A type guard is some expression that performs a runtime check that guarantees the type in some scope. + +### Using type predicates + To define a type guard, we simply need to define a function whose return type is a *type predicate*: ```ts function isFish(pet: Fish | Bird): pet is Fish { - return (pet).swim !== undefined; + return (pet as Fish).swim !== undefined; } ``` @@ -147,9 +237,24 @@ else { Notice that TypeScript not only knows that `pet` is a `Fish` in the `if` branch; it also knows that in the `else` branch, you *don't* have a `Fish`, so you must have a `Bird`. +### Using the `in` operator + +The `in` operator now acts as a narrowing expression for types. + +For a `n in x` expression, where `n` is a string literal or string literal type and `x` is a union type, the "true" branch narrows to types which have an optional or required property `n`, and the "false" branch narrows to types which have an optional or missing property `n`. + +```ts +function move(pet: Fish | Bird) { + if ("swim" in pet) { + return pet.swim(); + } + return pet.fly(); +} +``` + ## `typeof` type guards -We didn't actually discuss the implementation of the version of `padLeft` which used union types. +Let's go back and write the code for the version of `padLeft` that uses union types. We could write it with type predicates as follows: ```ts @@ -168,7 +273,7 @@ function padLeft(value: string, padding: string | number) { if (isString(padding)) { return padding + value; } - throw new Error(`Expected string or number, got '${value}'.`); + throw new Error(`Expected string or number, got '${padding}'.`); } ``` @@ -184,12 +289,12 @@ function padLeft(value: string, padding: string | number) { if (typeof padding === "string") { return padding + value; } - throw new Error(`Expected string or number, got '${value}'.`); + throw new Error(`Expected string or number, got '${padding}'.`); } ``` These *`typeof` type guards* are recognized in two different forms: `typeof v === "typename"` and `typeof v !== "typename"`, where `"typename"` must be `"number"`, `"string"`, `"boolean"`, or `"symbol"`. -While TypeScript won't prohibit comparing to other strings, or switching the two sides of the comparison, the language won't recognize those forms as type guards. +While TypeScript won't stop you from comparing to other strings, the language won't recognize those expressions as type guards. ## `instanceof` type guards @@ -223,7 +328,7 @@ function getRandomPadder() { new StringPadder(" "); } -// Type is SpaceRepeatingPadder | StringPadder +// Type is 'SpaceRepeatingPadder | StringPadder' let padder: Padder = getRandomPadder(); if (padder instanceof SpaceRepeatingPadder) { @@ -241,44 +346,110 @@ The right side of the `instanceof` needs to be a constructor function, and TypeS in that order. -# Intersection Types +# Nullable types -Intersection types are closely related to union types, but they are used very differently. -An intersection type, `Person & Serializable & Loggable`, for example, is a `Person` *and* `Serializable` *and* `Loggable`. -That means an object of this type will have all members of all three types. -In practice you will mostly see intersection types used for mixins. -Here's a simple mixin example: +TypeScript has two special types, `null` and `undefined`, that have the values null and undefined respectively. +We mentioned these briefly in [the Basic Types section](./Basic%20Types.md). +By default, the type checker considers `null` and `undefined` assignable to anything. +Effectively, `null` and `undefined` are valid values of every type. +That means it's not possible to *stop* them from being assigned to any type, even when you would like to prevent it. +The inventor of `null`, Tony Hoare, calls this his ["billion dollar mistake"](https://en.wikipedia.org/wiki/Null_pointer#History). + +The `--strictNullChecks` flag fixes this: when you declare a variable, it doesn't automatically include `null` or `undefined`. +You can include them explicitly using a union type: + +```ts +let s = "foo"; +s = null; // error, 'null' is not assignable to 'string' +let sn: string | null = "bar"; +sn = null; // ok + +sn = undefined; // error, 'undefined' is not assignable to 'string | null' +``` + +Note that TypeScript treats `null` and `undefined` differently in order to match JavaScript semantics. +`string | null` is a different type than `string | undefined` and `string | undefined | null`. + +From TypeScript 3.7 and onwards, you can use [optional chaining](/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) to simplify working with nullable types. + +## Optional parameters and properties + +With `--strictNullChecks`, an optional parameter automatically adds `| undefined`: + +```ts +function f(x: number, y?: number) { + return x + (y || 0); +} +f(1, 2); +f(1); +f(1, undefined); +f(1, null); // error, 'null' is not assignable to 'number | undefined' +``` + +The same is true for optional properties: ```ts -function extend(first: T, second: U): T & U { - let result = {}; - for (let id in first) { - (result)[id] = (first)[id]; +class C { + a: number; + b?: number; +} +let c = new C(); +c.a = 12; +c.a = undefined; // error, 'undefined' is not assignable to 'number' +c.b = 13; +c.b = undefined; // ok +c.b = null; // error, 'null' is not assignable to 'number | undefined' +``` + +## Type guards and type assertions + +Since nullable types are implemented with a union, you need to use a type guard to get rid of the `null`. +Fortunately, this is the same code you'd write in JavaScript: + +```ts +function f(sn: string | null): string { + if (sn == null) { + return "default"; } - for (let id in second) { - if (!result.hasOwnProperty(id)) { - (result)[id] = (second)[id]; - } + else { + return sn; } - return result; } +``` -class Person { - constructor(public name: string) { } +The `null` elimination is pretty obvious here, but you can use terser operators too: + +```ts +function f(sn: string | null): string { + return sn || "default"; } -interface Loggable { - log(): void; +``` + +In cases where the compiler can't eliminate `null` or `undefined`, you can use the type assertion operator to manually remove them. +The syntax is postfix `!`: `identifier!` removes `null` and `undefined` from the type of `identifier`: + +```ts +function broken(name: string | null): string { + function postfix(epithet: string) { + return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null + } + name = name || "Bob"; + return postfix("great"); } -class ConsoleLogger implements Loggable { - log() { - // ... - } + +function fixed(name: string | null): string { + function postfix(epithet: string) { + return name!.charAt(0) + '. the ' + epithet; // ok + } + name = name || "Bob"; + return postfix("great"); } -var jim = extend(new Person("Jim"), new ConsoleLogger()); -var n = jim.name; -jim.log(); ``` +The example uses a nested function here because the compiler can't eliminate nulls inside a nested function (except immediately-invoked function expressions). +That's because it can't track all calls to the nested function, especially if you return it from the outer function. +Without knowing where the function is called, it can't know what the type of `name` will be at the time the body executes. + # Type Aliases Type aliases create a new name for a type. @@ -289,7 +460,7 @@ type Name = string; type NameResolver = () => string; type NameOrResolver = Name | NameResolver; function getName(n: NameOrResolver): Name { - if (typeof n === 'string') { + if (typeof n === "string") { return n; } else { @@ -343,7 +514,21 @@ type Yikes = Array; // error As we mentioned, type aliases can act sort of like interfaces; however, there are some subtle differences. -One important difference is that type aliases cannot be extended or implemented from (nor can they extend/implement other types). +One difference is that interfaces create a new name that is used everywhere. +Type aliases don't create a new name — for instance, error messages won't use the alias name. +In the code below, hovering over `interfaced` in an editor will show that it returns an `Interface`, but will show that `aliased` returns object literal type. + +```ts +type Alias = { num: number } +interface Interface { + num: number; +} +declare function aliased(arg: Alias): Alias; +declare function interfaced(arg: Interface): Interface; +``` + +In older versions of TypeScript, type aliases couldn't be extended or implemented from (nor could they extend/implement other types). As of version 2.7, type aliases can be extended by creating a new intersection type e.g. `type Cat = Animal & { purrs: true }`. + Because [an ideal property of software is being open to extension](https://en.wikipedia.org/wiki/Open/closed_principle), you should always use an interface over a type alias if possible. On the other hand, if you can't express some shape with an interface and you need to use a union or tuple type, type aliases are usually the way to go. @@ -393,6 +578,139 @@ function createElement(tagName: string): Element { } ``` +# Numeric Literal Types + +TypeScript also has numeric literal types. + +```ts +function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 { + // ... +} +``` + +These are seldom written explicitly, but they can be useful when narrowing issues and can catch bugs: + +```ts +function foo(x: number) { + if (x !== 1 || x !== 2) { + // ~~~~~~~ + // Operator '!==' cannot be applied to types '1' and '2'. + } +} +``` + +In other words, `x` must be `1` when it gets compared to `2`, meaning that the above check is making an invalid comparison. + +# Enum Member Types + +As mentioned in [our section on enums](./Enums.md#union-enums-and-enum-member-types), enum members have types when every member is literal-initialized. + +Much of the time when we talk about "singleton types", we're referring to both enum member types as well as numeric/string literal types, though many users will use "singleton types" and "literal types" interchangeably. + +# Discriminated Unions + +You can combine singleton types, union types, type guards, and type aliases to build an advanced pattern called *discriminated unions*, also known as *tagged unions* or *algebraic data types*. +Discriminated unions are useful in functional programming. +Some languages automatically discriminate unions for you; TypeScript instead builds on JavaScript patterns as they exist today. +There are three ingredients: + +1. Types that have a common, singleton type property — the *discriminant*. +2. A type alias that takes the union of those types — the *union*. +3. Type guards on the common property. + +```ts +interface Square { + kind: "square"; + size: number; +} +interface Rectangle { + kind: "rectangle"; + width: number; + height: number; +} +interface Circle { + kind: "circle"; + radius: number; +} +``` + +First we declare the interfaces we will union. +Each interface has a `kind` property with a different string literal type. +The `kind` property is called the *discriminant* or *tag*. +The other properties are specific to each interface. +Notice that the interfaces are currently unrelated. +Let's put them into a union: + +```ts +type Shape = Square | Rectangle | Circle; +``` + +Now let's use the discriminated union: + +```ts +function area(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.height * s.width; + case "circle": return Math.PI * s.radius ** 2; + } +} +``` + +## Exhaustiveness checking + +We would like the compiler to tell us when we don't cover all variants of the discriminated union. +For example, if we add `Triangle` to `Shape`, we need to update `area` as well: + +```ts +type Shape = Square | Rectangle | Circle | Triangle; +function area(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.height * s.width; + case "circle": return Math.PI * s.radius ** 2; + } + // should error here - we didn't handle case "triangle" +} +``` + +There are two ways to do this. +The first is to turn on `--strictNullChecks` and specify a return type: + +```ts +function area(s: Shape): number { // error: returns number | undefined + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.height * s.width; + case "circle": return Math.PI * s.radius ** 2; + } +} +``` + +Because the `switch` is no longer exhaustive, TypeScript is aware that the function could sometimes return `undefined`. +If you have an explicit return type `number`, then you will get an error that the return type is actually `number | undefined`. +However, this method is quite subtle and, besides, `--strictNullChecks` does not always work with old code. + +The second method uses the `never` type that the compiler uses to check for exhaustiveness: + +```ts +function assertNever(x: never): never { + throw new Error("Unexpected object: " + x); +} +function area(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.height * s.width; + case "circle": return Math.PI * s.radius ** 2; + default: return assertNever(s); // error here if there are missing cases + } +} +``` + +Here, `assertNever` checks that `s` is of type `never` — the type that's left after all other cases have been removed. +If you forget a case, then `s` will have a real type and you will get a type error. +This method requires you to define an extra function, but it's much more obvious when you forget it. + # Polymorphic `this` types A polymorphic `this` type represents a type that is the *subtype* of the containing class or interface. @@ -447,3 +765,547 @@ let v = new ScientificCalculator(2) Without `this` types, `ScientificCalculator` would not have been able to extend `BasicCalculator` and keep the fluent interface. `multiply` would have returned `BasicCalculator`, which doesn't have the `sin` method. However, with `this` types, `multiply` returns `this`, which is `ScientificCalculator` here. + +# Index types + +With index types, you can get the compiler to check code that uses dynamic property names. +For example, a common JavaScript pattern is to pick a subset of properties from an object: + +```js +function pluck(o, propertyNames) { + return propertyNames.map(n => o[n]); +} +``` + +Here's how you would write and use this function in TypeScript, using the **index type query** and **indexed access** operators: + +```ts +function pluck(o: T, propertyNames: K[]): T[K][] { + return propertyNames.map(n => o[n]); +} + +interface Car { + manufacturer: string; + model: string; + year: number; +} +let taxi: Car = { + manufacturer: 'Toyota', + model: 'Camry', + year: 2014 +}; + +// Manufacturer and model are both of type string, +// so we can pluck them both into a typed string array +let makeAndModel: string[] = pluck(taxi, ['manufacturer', 'model']); + +// If we try to pluck model and year, we get an +// array of a union type: (string | number)[] +let modelYear = pluck(taxi, ['model', 'year']) +``` + +The compiler checks that `manufacturer` and `model` are actually properties on `Car`. +The example introduces a couple of new type operators. +First is `keyof T`, the **index type query operator**. +For any type `T`, `keyof T` is the union of known, public property names of `T`. +For example: + +```ts +let carProps: keyof Car; // the union of ('manufacturer' | 'model' | 'year') +``` + +`keyof Car` is completely interchangeable with `'manufacturer' | 'model' | 'year'`. +The difference is that if you add another property to `Car`, say `ownersAddress: string`, then `keyof Car` will automatically update to be `'manufacturer' | 'model' | 'year' | 'ownersAddress'`. +And you can use `keyof` in generic contexts like `pluck`, where you can't possibly know the property names ahead of time. +That means the compiler will check that you pass the right set of property names to `pluck`: + +```ts +// error, 'unknown' is not in 'manufacturer' | 'model' | 'year' +pluck(taxi, ['year', 'unknown']); / +``` + +The second operator is `T[K]`, the **indexed access operator**. +Here, the type syntax reflects the expression syntax. +That means that `person['name']` has the type `Person['name']` — which in our example is just `string`. +However, just like index type queries, you can use `T[K]` in a generic context, which is where its real power comes to life. +You just have to make sure that the type variable `K extends keyof T`. +Here's another example with a function named `getProperty`. + +```ts +function getProperty(o: T, propertyName: K): T[K] { + return o[propertyName]; // o[propertyName] is of type T[K] +} +``` + +In `getProperty`, `o: T` and `propertyName: K`, so that means `o[propertyName]: T[K]`. +Once you return the `T[K]` result, the compiler will instantiate the actual type of the key, so the return type of `getProperty` will vary according to which property you request. + +```ts +let name: string = getProperty(taxi, 'manufacturer'); +let year: number = getProperty(taxi, 'year'); + +// error, 'unknown' is not in 'manufacturer' | 'model' | 'year' +let unknown = getProperty(taxi, 'unknown'); +``` + +## Index types and index signatures + +`keyof` and `T[K]` interact with index signatures. An index signature parameter type must be 'string' or 'number'. +If you have a type with a string index signature, `keyof T` will be `string | number` +(and not just `string`, since in JavaScript you can access an object property either +by using strings (`object['42'`]) or numbers (`object[42]`)). +And `T[string]` is just the type of the index signature: + +```ts +interface Dictionary { + [key: string]: T; +} +let keys: keyof Dictionary; // string | number +let value: Dictionary['foo']; // number +``` + +If you have a type with a number index signature, `keyof T` will just be `number`. + +```ts +interface Dictionary { + [key: number]: T; +} +let keys: keyof Dictionary; // number +let value: Dictionary['foo']; // Error, Property 'foo' does not exist on type 'Dictionary'. +let value: Dictionary[42]; // number +``` + +# Mapped types + +A common task is to take an existing type and make each of its properties optional: + +```ts +interface PersonPartial { + name?: string; + age?: number; +} +``` + +Or we might want a readonly version: + +```ts +interface PersonReadonly { + readonly name: string; + readonly age: number; +} +``` + +This happens often enough in JavaScript that TypeScript provides a way to create new types based on old types — **mapped types**. +In a mapped type, the new type transforms each property in the old type in the same way. +For example, you can make all properties of a type `readonly` or optional. +Here are a couple of examples: + +```ts +type Readonly = { + readonly [P in keyof T]: T[P]; +} +type Partial = { + [P in keyof T]?: T[P]; +} +``` + +And to use it: + +```ts +type PersonPartial = Partial; +type ReadonlyPerson = Readonly; +``` + +Note that this syntax describes a type rather than a member. +If you want to add members, you can use an intersection type: + +```ts +// Use this: +type PartialWithNewMember = { + [P in keyof T]?: T[P]; +} & { newMember: boolean } + +// **Do not** use the following! +// This is an error! +type PartialWithNewMember = { + [P in keyof T]?: T[P]; + newMember: boolean; +} +``` + +Let's take a look at the simplest mapped type and its parts: + +```ts +type Keys = 'option1' | 'option2'; +type Flags = { [K in Keys]: boolean }; +``` + +The syntax resembles the syntax for index signatures with a `for .. in` inside. +There are three parts: + +1. The type variable `K`, which gets bound to each property in turn. +2. The string literal union `Keys`, which contains the names of properties to iterate over. +3. The resulting type of the property. + +In this simple example, `Keys` is a hard-coded list of property names and the property type is always `boolean`, so this mapped type is equivalent to writing: + +```ts +type Flags = { + option1: boolean; + option2: boolean; +} +``` + +Real applications, however, look like `Readonly` or `Partial` above. +They're based on some existing type, and they transform the properties in some way. +That's where `keyof` and indexed access types come in: + +```ts +type NullablePerson = { [P in keyof Person]: Person[P] | null } +type PartialPerson = { [P in keyof Person]?: Person[P] } +``` + +But it's more useful to have a general version. + +```ts +type Nullable = { [P in keyof T]: T[P] | null } +type Partial = { [P in keyof T]?: T[P] } +``` + +In these examples, the properties list is `keyof T` and the resulting type is some variant of `T[P]`. +This is a good template for any general use of mapped types. +That's because this kind of transformation is [homomorphic](https://en.wikipedia.org/wiki/Homomorphism), which means that the mapping applies only to properties of `T` and no others. +The compiler knows that it can copy all the existing property modifiers before adding any new ones. +For example, if `Person.name` was readonly, `Partial.name` would be readonly and optional. + +Here's one more example, in which `T[P]` is wrapped in a `Proxy` class: + +```ts +type Proxy = { + get(): T; + set(value: T): void; +} +type Proxify = { + [P in keyof T]: Proxy; +} +function proxify(o: T): Proxify { + // ... wrap proxies ... +} +let proxyProps = proxify(props); +``` + +Note that `Readonly` and `Partial` are so useful, they are included in TypeScript's standard library along with `Pick` and `Record`: + +```ts +type Pick = { + [P in K]: T[P]; +} +type Record = { + [P in K]: T; +} +``` + +`Readonly`, `Partial` and `Pick` are homomorphic whereas `Record` is not. +One clue that `Record` is not homomorphic is that it doesn't take an input type to copy properties from: + +```ts +type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string> +``` + +Non-homomorphic types are essentially creating new properties, so they can't copy property modifiers from anywhere. + +## Inference from mapped types + +Now that you know how to wrap the properties of a type, the next thing you'll want to do is unwrap them. +Fortunately, that's pretty easy: + +```ts +function unproxify(t: Proxify): T { + let result = {} as T; + for (const k in t) { + result[k] = t[k].get(); + } + return result; +} + +let originalProps = unproxify(proxyProps); +``` + +Note that this unwrapping inference only works on homomorphic mapped types. +If the mapped type is not homomorphic you'll have to give an explicit type parameter to your unwrapping function. + +# Conditional Types + +TypeScript 2.8 introduces *conditional types* which add the ability to express non-uniform type mappings. +A conditional type selects one of two possible types based on a condition expressed as a type relationship test: + +```ts +T extends U ? X : Y +``` + +The type above means when `T` is assignable to `U` the type is `X`, otherwise the type is `Y`. + +A conditional type `T extends U ? X : Y` is either *resolved* to `X` or `Y`, or *deferred* because the condition depends on one or more type variables. +When `T` or `U` contains type variables, whether to resolve to `X` or `Y`, or to defer, is determined by whether or not the type system has enough information to conclude that `T` is always assignable to `U`. + +As an example of some types that are immediately resolved, we can take a look at the following example: + +```ts +declare function f(x: T): T extends true ? string : number; + +// Type is 'string | number' +let x = f(Math.random() < 0.5) + +``` + +Another example would be the `TypeName` type alias, which uses nested conditional types: + +```ts +type TypeName = + T extends string ? "string" : + T extends number ? "number" : + T extends boolean ? "boolean" : + T extends undefined ? "undefined" : + T extends Function ? "function" : + "object"; + +type T0 = TypeName; // "string" +type T1 = TypeName<"a">; // "string" +type T2 = TypeName; // "boolean" +type T3 = TypeName<() => void>; // "function" +type T4 = TypeName; // "object" +``` + +But as an example of a place where conditional types are deferred - where they stick around instead of picking a branch - would be in the following: + +```ts +interface Foo { + propA: boolean; + propB: boolean; +} + +declare function f(x: T): T extends Foo ? string : number; + +function foo(x: U) { + // Has type 'U extends Foo ? string : number' + let a = f(x); + + // This assignment is allowed though! + let b: string | number = a; +} +``` + +In the above, the variable `a` has a conditional type that hasn't yet chosen a branch. +When another piece of code ends up calling `foo`, it will substitute in `U` with some other type, and TypeScript will re-evaluate the conditional type, deciding whether it can actually pick a branch. + +In the meantime, we can assign a conditional type to any other target type as long as each branch of the conditional is assignable to that target. +So in our example above we were able to assign `U extends Foo ? string : number` to `string | number` since no matter what the conditional evaluates to, it's known to be either `string` or `number`. + +## Distributive conditional types + +Conditional types in which the checked type is a naked type parameter are called *distributive conditional types*. +Distributive conditional types are automatically distributed over union types during instantiation. +For example, an instantiation of `T extends U ? X : Y` with the type argument `A | B | C` for `T` is resolved as `(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)`. + +### Example + +```ts +type T10 = TypeName void)>; // "string" | "function" +type T12 = TypeName; // "string" | "object" | "undefined" +type T11 = TypeName; // "object" +``` + +In instantiations of a distributive conditional type `T extends U ? X : Y`, references to `T` within the conditional type are resolved to individual constituents of the union type (i.e. `T` refers to the individual constituents *after* the conditional type is distributed over the union type). +Furthermore, references to `T` within `X` have an additional type parameter constraint `U` (i.e. `T` is considered assignable to `U` within `X`). + +### Example + +```ts +type BoxedValue = { value: T }; +type BoxedArray = { array: T[] }; +type Boxed = T extends any[] ? BoxedArray : BoxedValue; + +type T20 = Boxed; // BoxedValue; +type T21 = Boxed; // BoxedArray; +type T22 = Boxed; // BoxedValue | BoxedArray; +``` + +Notice that `T` has the additional constraint `any[]` within the true branch of `Boxed` and it is therefore possible to refer to the element type of the array as `T[number]`. Also, notice how the conditional type is distributed over the union type in the last example. + +The distributive property of conditional types can conveniently be used to *filter* union types: + +```ts +type Diff = T extends U ? never : T; // Remove types from T that are assignable to U +type Filter = T extends U ? T : never; // Remove types from T that are not assignable to U + +type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" +type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" +type T32 = Diff void), Function>; // string | number +type T33 = Filter void), Function>; // () => void + +type NonNullable = Diff; // Remove null and undefined from T + +type T34 = NonNullable; // string | number +type T35 = NonNullable; // string | string[] + +function f1(x: T, y: NonNullable) { + x = y; // Ok + y = x; // Error +} + +function f2(x: T, y: NonNullable) { + x = y; // Ok + y = x; // Error + let s1: string = x; // Error + let s2: string = y; // Ok +} +``` + +Conditional types are particularly useful when combined with mapped types: + +```ts +type FunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]; +type FunctionProperties = Pick>; + +type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]; +type NonFunctionProperties = Pick>; + +interface Part { + id: number; + name: string; + subparts: Part[]; + updatePart(newName: string): void; +} + +type T40 = FunctionPropertyNames; // "updatePart" +type T41 = NonFunctionPropertyNames; // "id" | "name" | "subparts" +type T42 = FunctionProperties; // { updatePart(newName: string): void } +type T43 = NonFunctionProperties; // { id: number, name: string, subparts: Part[] } +``` + +Similar to union and intersection types, conditional types are not permitted to reference themselves recursively. +For example the following is an error. + +### Example + +```ts +type ElementType = T extends any[] ? ElementType : T; // Error +``` + +## Type inference in conditional types + +Within the `extends` clause of a conditional type, it is now possible to have `infer` declarations that introduce a type variable to be inferred. +Such inferred type variables may be referenced in the true branch of the conditional type. +It is possible to have multiple `infer` locations for the same type variable. + +For example, the following extracts the return type of a function type: + +```ts +type ReturnType = T extends (...args: any[]) => infer R ? R : any; +``` + +Conditional types can be nested to form a sequence of pattern matches that are evaluated in order: + +```ts +type Unpacked = + T extends (infer U)[] ? U : + T extends (...args: any[]) => infer U ? U : + T extends Promise ? U : + T; + +type T0 = Unpacked; // string +type T1 = Unpacked; // string +type T2 = Unpacked<() => string>; // string +type T3 = Unpacked>; // string +type T4 = Unpacked[]>; // Promise +type T5 = Unpacked[]>>; // string +``` + +The following example demonstrates how multiple candidates for the same type variable in co-variant positions causes a union type to be inferred: + +```ts +type Foo = T extends { a: infer U, b: infer U } ? U : never; +type T10 = Foo<{ a: string, b: string }>; // string +type T11 = Foo<{ a: string, b: number }>; // string | number +``` + +Likewise, multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred: + +```ts +type Bar = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never; +type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string +type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number +``` + +When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made from the *last* signature (which, presumably, is the most permissive catch-all case). +It is not possible to perform overload resolution based on a list of argument types. + +```ts +declare function foo(x: string): number; +declare function foo(x: number): string; +declare function foo(x: string | number): string | number; +type T30 = ReturnType; // string | number +``` + +It is not possible to use `infer` declarations in constraint clauses for regular type parameters: + +```ts +type ReturnType infer R> = R; // Error, not supported +``` + +However, much the same effect can be obtained by erasing the type variables in the constraint and instead specifying a conditional type: + +```ts +type AnyFunction = (...args: any[]) => any; +type ReturnType = T extends (...args: any[]) => infer R ? R : any; +``` + +## Predefined conditional types + +TypeScript 2.8 adds several predefined conditional types to `lib.d.ts`: + +* `Exclude` -- Exclude from `T` those types that are assignable to `U`. +* `Extract` -- Extract from `T` those types that are assignable to `U`. +* `NonNullable` -- Exclude `null` and `undefined` from `T`. +* `ReturnType` -- Obtain the return type of a function type. +* `InstanceType` -- Obtain the instance type of a constructor function type. + +### Example + +```ts +type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" +type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" + +type T02 = Exclude void), Function>; // string | number +type T03 = Extract void), Function>; // () => void + +type T04 = NonNullable; // string | number +type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[] + +function f1(s: string) { + return { a: 1, b: s }; +} + +class C { + x = 0; + y = 0; +} + +type T10 = ReturnType<() => string>; // string +type T11 = ReturnType<(s: string) => void>; // void +type T12 = ReturnType<(() => T)>; // {} +type T13 = ReturnType<(() => T)>; // number[] +type T14 = ReturnType; // { a: number, b: string } +type T15 = ReturnType; // any +type T16 = ReturnType; // never +type T17 = ReturnType; // Error +type T18 = ReturnType; // Error + +type T20 = InstanceType; // C +type T21 = InstanceType; // any +type T22 = InstanceType; // never +type T23 = InstanceType; // Error +type T24 = InstanceType; // Error +``` + +> Note: The `Exclude` type is a proper implementation of the `Diff` type suggested [here](https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458). We've used the name `Exclude` to avoid breaking existing code that defines a `Diff`, plus we feel that name better conveys the semantics of the type. diff --git a/pages/Basic Types.md b/pages/Basic Types.md index 45e2b2356..9a05ba253 100644 --- a/pages/Basic Types.md +++ b/pages/Basic Types.md @@ -1,3 +1,19 @@ +# Table of Contents +1. [Introduction](#introduction) +2. [Boolean](#boolean) +3. [Number](#number) +4. [String](#string) +5. [Array](#array) +6. [Tuple](#tuple) +7. [Enum](#enum) +8. [Any](#any) +9. [Void](#void) +10. [Null and Undefined](#null-and-undefined) +11. [Never](#never) +12. [Object](#object) +13. [Type assertions](#type-assertions) +14. [A note about 'let'](#a-note-about-let) + # Introduction For programs to be useful, we need to be able to work with some of the simplest units of data: numbers, strings, structures, boolean values, and the like. @@ -32,7 +48,7 @@ Just like JavaScript, TypeScript also uses double quotes (`"`) or single quotes ```ts let color: string = "blue"; -name = 'red'; +color = 'red'; ``` You can also use *template strings*, which can span multiple lines and have embedded expressions. @@ -43,14 +59,14 @@ let fullName: string = `Bob Bobbington`; let age: number = 37; let sentence: string = `Hello, my name is ${ fullName }. -I'll be ${ age + 1 } years old next month.` +I'll be ${ age + 1 } years old next month.`; ``` This is equivalent to declaring `sentence` like so: ```ts let sentence: string = "Hello, my name is " + fullName + ".\n\n" + - "I'll be " + (age + 1) + " years old next month." + "I'll be " + (age + 1) + " years old next month."; ``` # Array @@ -71,44 +87,39 @@ let list: Array = [1, 2, 3]; # Tuple -Tuple types allow you to express an array where the type of a fixed number of elements is known, but need not be the same. -For example, you may want to represent a value as a pair of a `string` and a `number`: +Tuple types allow you to express an array with a fixed number of elements whose types are known, but need not be the same. For example, you may want to represent a value as a pair of a `string` and a `number`: ```ts // Declare a tuple type let x: [string, number]; // Initialize it -x = ['hello', 10]; // OK +x = ["hello", 10]; // OK // Initialize it incorrectly -x = [10, 'hello']; // Error +x = [10, "hello"]; // Error ``` When accessing an element with a known index, the correct type is retrieved: ```ts -console.log(x[0].substr(1)); // OK -console.log(x[1].substr(1)); // Error, 'number' does not have 'substr' +console.log(x[0].substring(1)); // OK +console.log(x[1].substring(1)); // Error, 'number' does not have 'substring' ``` -When accessing an element outside the set of known indices, a union type is used instead: +Accessing an element outside the set of known indices fails with an error: ```ts -x[3] = 'world'; // OK, string can be assigned to (string | number) - -console.log(x[5].toString()); // OK, 'string' and 'number' both have toString +x[3] = "world"; // Error, Property '3' does not exist on type '[string, number]'. -x[6] = true; // Error, boolean isn't (string | number) +console.log(x[5].toString()); // Error, Property '5' does not exist on type '[string, number]'. ``` -Union types are an advanced topic that we'll cover in a later chapter. - # Enum A helpful addition to the standard set of datatypes from JavaScript is the `enum`. As in languages like C#, an enum is a way of giving more friendly names to sets of numeric values. ```ts -enum Color {Red, Green, Blue}; +enum Color {Red, Green, Blue} let c: Color = Color.Green; ``` @@ -117,14 +128,14 @@ You can change this by manually setting the value of one of its members. For example, we can start the previous example at `1` instead of `0`: ```ts -enum Color {Red = 1, Green, Blue}; +enum Color {Red = 1, Green, Blue} let c: Color = Color.Green; ``` Or, even manually set all the values in the enum: ```ts -enum Color {Red = 1, Green = 2, Blue = 4}; +enum Color {Red = 1, Green = 2, Blue = 4} let c: Color = Color.Green; ``` @@ -132,17 +143,17 @@ A handy feature of enums is that you can also go from a numeric value to the nam For example, if we had the value `2` but weren't sure what that mapped to in the `Color` enum above, we could look up the corresponding name: ```ts -enum Color {Red = 1, Green, Blue}; +enum Color {Red = 1, Green, Blue} let colorName: string = Color[2]; -alert(colorName); +console.log(colorName); // Displays 'Green' as its value is 2 above ``` # Any We may need to describe the type of variables that we do not know when we are writing an application. These values may come from dynamic content, e.g. from the user or a 3rd party library. -In these cases, we want to opt-out of type-checking and let the values pass through compile-time checks. +In these cases, we want to opt-out of type checking and let the values pass through compile-time checks. To do so, we label these with the `any` type: ```ts @@ -151,9 +162,9 @@ notSure = "maybe a string instead"; notSure = false; // okay, definitely a boolean ``` -The `any` type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type-checking during compilation. +The `any` type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type checking during compilation. You might expect `Object` to play a similar role, as it does in other languages. -But variables of type `Object` only allow you to assign any value to them -- you can't call arbitrary methods on them, even ones that actually exist: +However, variables of type `Object` only allow you to assign any value to them. You can't call arbitrary methods on them, even ones that actually exist: ```ts let notSure: any = 4; @@ -164,6 +175,8 @@ let prettySure: Object = 4; prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'. ``` +> Note: Avoid using `Object` in favor of the non-primitive `object` type as described in our [Do's and Don'ts](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#general-types) section. + The `any` type is also handy if you know some part of the type, but perhaps not all of it. For example, you may have an array but the array has a mix of different types: @@ -180,14 +193,84 @@ You may commonly see this as the return type of functions that do not return a v ```ts function warnUser(): void { - alert("This is my warning message"); + console.log("This is my warning message"); } ``` -Declaring variables of type `void` is not useful because you can only assign `undefined` or `null` to them: +Declaring variables of type `void` is not useful because you can only assign `null` (only if `--strictNullChecks` is not specified, see next section) or `undefined` to them: ```ts let unusable: void = undefined; +unusable = null; // OK if `--strictNullChecks` is not given +``` + +# Null and Undefined + +In TypeScript, both `undefined` and `null` actually have their own types named `undefined` and `null` respectively. +Much like `void`, they're not extremely useful on their own: + +```ts +// Not much else we can assign to these variables! +let u: undefined = undefined; +let n: null = null; +``` + +By default `null` and `undefined` are subtypes of all other types. +That means you can assign `null` and `undefined` to something like `number`. + +However, when using the `--strictNullChecks` flag, `null` and `undefined` are only assignable to `any` and their respective types (the one exception being that `undefined` is also assignable to `void`). +This helps avoid *many* common errors. +In cases where you want to pass in either a `string` or `null` or `undefined`, you can use the union type `string | null | undefined`. + +Union types are an advanced topic that we'll cover in a later chapter. + +> As a note: we encourage the use of `--strictNullChecks` when possible, but for the purposes of this handbook, we will assume it is turned off. + +# Never + +The `never` type represents the type of values that never occur. +For instance, `never` is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns; +Variables also acquire the type `never` when narrowed by any type guards that can never be true. + +The `never` type is a subtype of, and assignable to, every type; however, *no* type is a subtype of, or assignable to, `never` (except `never` itself). +Even `any` isn't assignable to `never`. + +Some examples of functions returning `never`: + +```ts +// Function returning never must have unreachable end point +function error(message: string): never { + throw new Error(message); +} + +// Inferred return type is never +function fail() { + return error("Something failed"); +} + +// Function returning never must have unreachable end point +function infiniteLoop(): never { + while (true) { + } +} +``` + +# Object + +`object` is a type that represents the non-primitive type, i.e. anything that is not `number`, `string`, `boolean`, `bigint`, `symbol`, `null`, or `undefined`. + +With `object` type, APIs like `Object.create` can be better represented. For example: + +```ts +declare function create(o: object | null): void; + +create({ prop: 0 }); // OK +create(null); // OK + +create(42); // Error +create("string"); // Error +create(false); // Error +create(undefined); // Error ``` # Type assertions @@ -223,5 +306,5 @@ Using one over the other is mostly a choice of preference; however, when using T # A note about `let` You may've noticed that so far, we've been using the `let` keyword instead of JavaScript's `var` keyword which you might be more familiar with. -The `let` keyword is actually a newer JavaScript construct that TypeScript makes available. +The `let` keyword was introduced to JavaScript in ES2015 and is now considered the standard because it's safer than `var`. We'll discuss the details later, but many common problems in JavaScript are alleviated by using `let`, so you should use it instead of `var` whenever possible. diff --git a/pages/Classes.md b/pages/Classes.md index 492608494..dd9934dfe 100644 --- a/pages/Classes.md +++ b/pages/Classes.md @@ -1,6 +1,6 @@ # Introduction -Traditional JavaScript focuses on functions and prototype-based inheritance as the basic means of building up reusable components, but this may feel a bit awkward to programmers more comfortable with an object-oriented approach, where classes inherit functionality and objects are built from these classes. +Traditional JavaScript uses functions and prototype-based inheritance to build up reusable components, but this may feel a bit awkward to programmers more comfortable with an object-oriented approach, where classes inherit functionality and objects are built from these classes. Starting with ECMAScript 2015, also known as ECMAScript 6, JavaScript programmers will be able to build their applications using this object-oriented class-based approach. In TypeScript, we allow developers to use these techniques now, and compile them down to JavaScript that works across all major browsers and platforms, without having to wait for the next version of JavaScript. @@ -34,10 +34,37 @@ This calls into the constructor we defined earlier, creating a new object with t # Inheritance In TypeScript, we can use common object-oriented patterns. -Of course, one of the most fundamental patterns in class-based programming is being able to extend existing classes to create new ones using inheritance. +One of the most fundamental patterns in class-based programming is being able to extend existing classes to create new ones using inheritance. Let's take a look at an example: +```ts +class Animal { + move(distanceInMeters: number = 0) { + console.log(`Animal moved ${distanceInMeters}m.`); + } +} + +class Dog extends Animal { + bark() { + console.log('Woof! Woof!'); + } +} + +const dog = new Dog(); +dog.bark(); +dog.move(10); +dog.bark(); +``` + +This example shows the most basic inheritance feature: classes inherit properties and methods from base classes. +Here, `Dog` is a *derived* class that derives from the `Animal` *base* class using the `extends` keyword. +Derived classes are often called *subclasses*, and base classes are often called *superclasses*. + +Because `Dog` extends the functionality from `Animal`, we were able to create an instance of `Dog` that could both `bark()` and `move()`. + +Let's now look at a more complex example. + ```ts class Animal { name: string; @@ -70,14 +97,16 @@ sam.move(); tom.move(34); ``` -This example covers quite a few of the inheritance features in TypeScript that are common to other languages. -Here we see the `extends` keywords used to create a subclass. You can see this where `Horse` and `Snake` subclass the base class `Animal` and gain access to its features. +This example covers a few other features we didn't previously mention. +Again, we see the `extends` keywords used to create two new subclasses of `Animal`: `Horse` and `Snake`. -Derived classes that contain constructor functions must call `super()` which will execute the constructor function on the base class. +One difference from the prior example is that each derived class that contains a constructor function *must* call `super()` which will execute the constructor of the base class. +What's more, before we *ever* access a property on `this` in a constructor body, we *have* to call `super()`. +This is an important rule that TypeScript will enforce. The example also shows how to override methods in the base class with methods that are specialized for the subclass. Here both `Snake` and `Horse` create a `move` method that overrides the `move` from `Animal`, giving it functionality specific to each class. -Note that even though `tom` is declared as an `Animal`, since its value is a `Horse`, when `tom.move(34)` calls the overriding method in `Horse`: +Note that even though `tom` is declared as an `Animal`, since its value is a `Horse`, calling `tom.move(34)` will call the overriding method in `Horse`: ```Text Slithering... @@ -107,9 +136,25 @@ class Animal { } ``` -## Understanding `private` +## ECMAScript Private Fields -When a member is marked `private`, it cannot be accessed from outside of its containing class. For example: +With TypeScript 3.8, TypeScript supports the new JavaScript syntax for private fields: + +```ts +class Animal { + #name: string; + constructor(theName: string) { this.#name = theName; } +} + +new Animal("Cat").#name; // Property '#name' is not accessible outside class 'Animal' because it has a private identifier. +``` + +This syntax is built into the JavaScript runtime and can have better guarantees about the isolation of each private field. +Right now, the best documentation for these private fields is in the TypeScript 3.8 [release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/#ecmascript-private-fields). + +## Understanding TypeScript's `private` + +TypeScript also has it's own way to declare a member as being marked `private`, it cannot be accessed from outside of its containing class. For example: ```ts class Animal { @@ -149,7 +194,7 @@ let rhino = new Rhino(); let employee = new Employee("Bob"); animal = rhino; -animal = employee; // Error: Animal and Employee are not compatible +animal = employee; // Error: 'Animal' and 'Employee' are not compatible ``` In this example, we have an `Animal` and a `Rhino`, with `Rhino` being a subclass of `Animal`. @@ -161,7 +206,7 @@ Even though `Employee` also has a `private` member called `name`, it's not the o ## Understanding `protected` -The `protected` modifier acts much like the `private` modifier with the exception that members declared `protected` can also be accessed by instances of deriving classes. For example, +The `protected` modifier acts much like the `private` modifier with the exception that members declared `protected` can also be accessed within deriving classes. For example, ```ts class Person { @@ -189,27 +234,69 @@ console.log(howard.name); // error Notice that while we can't use `name` from outside of `Person`, we can still use it from within an instance method of `Employee` because `Employee` derives from `Person`. +A constructor may also be marked `protected`. +This means that the class cannot be instantiated outside of its containing class, but can be extended. For example, + +```ts +class Person { + protected name: string; + protected constructor(theName: string) { this.name = theName; } +} + +// Employee can extend Person +class Employee extends Person { + private department: string; + + constructor(name: string, department: string) { + super(name); + this.department = department; + } + + public getElevatorPitch() { + return `Hello, my name is ${this.name} and I work in ${this.department}.`; + } +} + +let howard = new Employee("Howard", "Sales"); +let john = new Person("John"); // Error: The 'Person' constructor is protected +``` + +# Readonly modifier + +You can make properties readonly by using the `readonly` keyword. +Readonly properties must be initialized at their declaration or in the constructor. + +```ts +class Octopus { + readonly name: string; + readonly numberOfLegs: number = 8; + constructor (theName: string) { + this.name = theName; + } +} +let dad = new Octopus("Man with the 8 strong legs"); +dad.name = "Man with the 3-piece suit"; // error! name is readonly. +``` + ## Parameter properties -In our last example, we had to declare a private member `name` and a constructor parameter `theName`, and we then immediately set `name` to `theName`. -This turns out to be a very common practice. +In our last example, we had to declare a readonly member `name` and a constructor parameter `theName` in the `Octopus` class. This is needed in order to have the value of `theName` accessible after the `Octopus` constructor is executed. *Parameter properties* let you create and initialize a member in one place. -Here's a further revision of the previous `Animal` class using a parameter property: +Here's a further revision of the previous `Octopus` class using a parameter property: ```ts -class Animal { - constructor(private name: string) { } - move(distanceInMeters: number) { - console.log(`${this.name} moved ${distanceInMeters}m.`); +class Octopus { + readonly numberOfLegs: number = 8; + constructor(readonly name: string) { } } ``` -Notice how we dropped `theName` altogether and just use the shortened `private name: string` parameter on the constructor to create and initialize the `name` member. +Notice how we dropped `theName` altogether and just use the shortened `readonly name: string` parameter on the constructor to create and initialize the `name` member. We've consolidated the declarations and assignment into one location. -Parameter properties are declared by prefixing a constructor parameter with an accessibility modifier. -Using `private` for a parameter property declares and initializes a private member; likewise, the same is done for `public` and `protected`. +Parameter properties are declared by prefixing a constructor parameter with an accessibility modifier or `readonly`, or both. +Using `private` for a parameter property declares and initializes a private member; likewise, the same is done for `public`, `protected`, and `readonly`. # Accessors @@ -231,14 +318,14 @@ if (employee.fullName) { } ``` -While allowing people to randomly set `fullName` directly is pretty handy, this might get us in trouble if people can change names on a whim. +While allowing people to randomly set `fullName` directly is pretty handy, we may also want enforce some constraints when `fullName` is set. + +In this version, we add a setter that checks the length of the `newName` to make sure it's compatible with the max-length of our backing database field. If it isn't we throw an error notifying client code that something went wrong. -In this version, we check to make sure the user has a secret passcode available before we allow them to modify the employee. -We do this by replacing the direct access to `fullName` with a `set` that will check the passcode. -We add a corresponding `get` to allow the previous example to continue to work seamlessly. +To preserve existing functionality, we also add a simple getter that retrieves `fullName` unmodified. ```ts -let passcode = "secret passcode"; +const fullNameMaxLength = 10; class Employee { private _fullName: string; @@ -248,12 +335,11 @@ class Employee { } set fullName(newName: string) { - if (passcode && passcode == "secret passcode") { - this._fullName = newName; - } - else { - console.log("Error: Unauthorized update of employee!"); + if (newName && newName.length > fullNameMaxLength) { + throw new Error("fullName has a max length of " + fullNameMaxLength); } + + this._fullName = newName; } } @@ -264,13 +350,18 @@ if (employee.fullName) { } ``` -To prove to ourselves that our accessor is now checking the passcode, we can modify the passcode and see that when it doesn't match we instead get the message warning us we don't have access to update the employee. +To prove to ourselves that our accessor is now checking the length of values, we can attempt to assign a name longer than 10 characters and verify that we get an error. + +A couple of things to note about accessors: -Note: Accessors require you to set the compiler to output ECMAScript 5 or higher. +First, accessors require you to set the compiler to output ECMAScript 5 or higher. +Downleveling to ECMAScript 3 is not supported. +Second, accessors with a `get` and no `set` are automatically inferred to be `readonly`. +This is helpful when generating a `.d.ts` file from your code, because users of your property can see that they can't change it. # Static Properties -Up to this point, we've only talked about the *instance* members of the class, those that show up on the object when its instantiated. +Up to this point, we've only talked about the *instance* members of the class, those that show up on the object when it's instantiated. We can also create *static* members of a class, those that are visible on the class itself rather than on the instances. In this example, we use `static` on the origin, as it's a general value for all grids. Each instance accesses this value through prepending the name of the class. @@ -305,7 +396,7 @@ The `abstract` keyword is used to define abstract classes as well as abstract me abstract class Animal { abstract makeSound(): void; move(): void { - console.log('roaming the earth...'); + console.log("roaming the earth..."); } } ``` @@ -322,7 +413,7 @@ abstract class Department { } printName(): void { - console.log('Department name: ' + this.name); + console.log("Department name: " + this.name); } abstract printMeeting(): void; // must be implemented in derived classes @@ -331,15 +422,15 @@ abstract class Department { class AccountingDepartment extends Department { constructor() { - super('Accounting and Auditing'); // constructors in derived classes must call super() + super("Accounting and Auditing"); // constructors in derived classes must call super() } printMeeting(): void { - console.log('The Accounting Department meets each Monday at 10am.'); + console.log("The Accounting Department meets each Monday at 10am."); } generateReports(): void { - console.log('Generating accounting reports...'); + console.log("Generating accounting reports..."); } } @@ -371,7 +462,7 @@ class Greeter { let greeter: Greeter; greeter = new Greeter("world"); -console.log(greeter.greet()); +console.log(greeter.greet()); // "Hello, world" ``` Here, when we say `let greeter: Greeter`, we're using `Greeter` as the type of instances of the class `Greeter`. @@ -394,7 +485,7 @@ let Greeter = (function () { let greeter; greeter = new Greeter("world"); -console.log(greeter.greet()); +console.log(greeter.greet()); // "Hello, world" ``` Here, `let Greeter` is going to be assigned the constructor function. @@ -420,13 +511,13 @@ class Greeter { let greeter1: Greeter; greeter1 = new Greeter(); -console.log(greeter1.greet()); +console.log(greeter1.greet()); // "Hello, there" let greeterMaker: typeof Greeter = Greeter; greeterMaker.standardGreeting = "Hey there!"; let greeter2: Greeter = new greeterMaker(); -console.log(greeter2.greet()); +console.log(greeter2.greet()); // "Hey there!" ``` In this example, `greeter1` works similarly to before. @@ -437,7 +528,7 @@ Next, we then use the class directly. Here we create a new variable called `greeterMaker`. This variable will hold the class itself, or said another way its constructor function. Here we use `typeof Greeter`, that is "give me the type of the `Greeter` class itself" rather than the instance type. -Or, more precisely, "give me the type of the symbol called `Greeter`", which is the type of the constructor function. +Or, more precisely, "give me the type of the symbol called `Greeter`," which is the type of the constructor function. This type will contain all of the static members of Greeter along with the constructor that creates instances of the `Greeter` class. We show this by using `new` on `greeterMaker`, creating new instances of `Greeter` and invoking them as before. diff --git a/pages/Compiler Options in MSBuild.md b/pages/Compiler Options in MSBuild.md index 8ef72c22a..618a6fe99 100644 --- a/pages/Compiler Options in MSBuild.md +++ b/pages/Compiler Options in MSBuild.md @@ -22,52 +22,84 @@ Compiler options can be specified using MSBuild properties within an MSBuild pro Compiler Option | MSBuild Property Name | Allowed Values ---------------------------------------------|--------------------------------------------|----------------- -`--declaration` | TypeScriptGeneratesDeclarations | boolean -`--module` | TypeScriptModuleKind | `AMD`, `CommonJs`, `UMD`, or `System` -`--target` | TypeScriptTarget | `ES3`, `ES5`, or `ES6` +`--allowJs` | *Not supported in MSBuild* | +`--allowSyntheticDefaultImports` | TypeScriptAllowSyntheticDefaultImports | boolean +`--allowUnreachableCode` | TypeScriptAllowUnreachableCode | boolean +`--allowUnusedLabels` | TypeScriptAllowUnusedLabels | boolean +`--alwaysStrict` | TypeScriptAlwaysStrict | boolean +`--baseUrl` | TypeScriptBaseUrl | File path `--charset` | TypeScriptCharset | +`--declaration` | TypeScriptGeneratesDeclarations | boolean +`--declarationDir` | TypeScriptDeclarationDir | File path +`--diagnostics` | *Not supported in MSBuild* | +`--disableSizeLimit` | *Not supported in MSBuild* | `--emitBOM` | TypeScriptEmitBOM | boolean `--emitDecoratorMetadata` | TypeScriptEmitDecoratorMetadata | boolean +`--emitDeclarationOnly` | TypeScriptEmitDeclarationOnly | boolean +`--esModuleInterop` | TypeScriptESModuleInterop | boolean +`--experimentalAsyncFunctions` | TypeScriptExperimentalAsyncFunctions | boolean `--experimentalDecorators` | TypeScriptExperimentalDecorators | boolean +`--forceConsistentCasingInFileNames` | TypeScriptForceConsistentCasingInFileNames | boolean +`--help` | *Not supported in MSBuild* | +`--importHelpers` | TypeScriptImportHelpers | boolean `--inlineSourceMap` | TypeScriptInlineSourceMap | boolean `--inlineSources` | TypeScriptInlineSources | boolean +`--init` | *Not supported in MSBuild* | +`--isolatedModules` | TypeScriptIsolatedModules | boolean +`--jsx` | TypeScriptJSXEmit | `react`, `react-native`, `preserve` +`--jsxFactory` | TypeScriptJSXFactory | qualified name +`--lib` | TypeScriptLib | Comma-separated list of strings +`--listEmittedFiles` | *Not supported in MSBuild* | +`--listFiles` | *Not supported in MSBuild* | `--locale` | *automatic* | Automatically set to PreferredUILang value `--mapRoot` | TypeScriptMapRoot | File path +`--maxNodeModuleJsDepth` | *Not supported in MSBuild* | +`--module` | TypeScriptModuleKind | `AMD`, `CommonJs`, `UMD`, `System` or `ES6` +`--moduleResolution` | TypeScriptModuleResolution | `Classic` or `Node` `--newLine` | TypeScriptNewLine | `CRLF` or `LF` -`--noEmitOnError` | TypeScriptNoEmitOnError | boolean +`--noEmit` | *Not supported in MSBuild* | `--noEmitHelpers` | TypeScriptNoEmitHelpers | boolean +`--noEmitOnError` | TypeScriptNoEmitOnError | boolean +`--noFallthroughCasesInSwitch` | TypeScriptNoFallthroughCasesInSwitch | boolean `--noImplicitAny` | TypeScriptNoImplicitAny | boolean +`--noImplicitReturns` | TypeScriptNoImplicitReturns | boolean +`--noImplicitThis` | TypeScriptNoImplicitThis | boolean +`--noImplicitUseStrict` | TypeScriptNoImplicitUseStrict | boolean +`--noStrictGenericChecks` | TypeScriptNoStrictGenericChecks | boolean +`--noUnusedLocals` | TypeScriptNoUnusedLocals | boolean +`--noUnusedParameters` | TypeScriptNoUnusedParameters | boolean `--noLib` | TypeScriptNoLib | boolean `--noResolve` | TypeScriptNoResolve | boolean `--out` | TypeScriptOutFile | File path `--outDir` | TypeScriptOutDir | File path +`--outFile` | TypeScriptOutFile | File path +`--paths` | *Not supported in MSBuild* | `--preserveConstEnums` | TypeScriptPreserveConstEnums | boolean +`--preserveSymlinks` | TypeScriptPreserveSymlinks | boolean +`--listEmittedFiles` | *Not supported in MSBuild* | +`--pretty` | *Not supported in MSBuild* | +`--reactNamespace` | TypeScriptReactNamespace | string `--removeComments` | TypeScriptRemoveComments | boolean `--rootDir` | TypeScriptRootDir | File path -`--isolatedModules` | TypeScriptIsolatedModules | boolean +`--rootDirs` | *Not supported in MSBuild* | +`--skipLibCheck` | TypeScriptSkipLibCheck | boolean +`--skipDefaultLibCheck` | TypeScriptSkipDefaultLibCheck | boolean `--sourceMap` | TypeScriptSourceMap | File path `--sourceRoot` | TypeScriptSourceRoot | File path -`--suppressImplicitAnyIndexErrors` | TypeScriptSuppressImplicitAnyIndexErrors | boolean +`--strict` | TypeScriptStrict | boolean +`--strictFunctionTypes` | TypeScriptStrictFunctionTypes | boolean +`--strictNullChecks` | TypeScriptStrictNullChecks | boolean +`--strictPropertyInitialization` | TypeScriptStrictPropertyInitialization | boolean +`--stripInternal` | TypeScriptStripInternal | boolean `--suppressExcessPropertyErrors` | TypeScriptSuppressExcessPropertyErrors | boolean -`--moduleResolution` | TypeScriptModuleResolution | `Classic` or `Node` -`--experimentalAsyncFunctions` | TypeScriptExperimentalAsyncFunctions | boolean -`--jsx` | TypeScriptJSXEmit | `React` or `Preserve` -`--reactNamespace` | TypeScriptReactNamespace | string -`--skipDefaultLibCheck` | TypeScriptSkipDefaultLibCheck | boolean -`--allowUnusedLabels` | TypeScriptAllowUnusedLabels | boolean -`--noImplicitReturns` | TypeScriptNoImplicitReturns | boolean -`--noFallthroughCasesInSwitch` | TypeScriptNoFallthroughCasesInSwitch | boolean -`--allowUnreachableCode` | TypeScriptAllowUnreachableCode | boolean -`--forceConsistentCasingInFileNames` | TypeScriptForceConsistentCasingInFileNames | boolean -`--allowSyntheticDefaultImports` | TypeScriptAllowSyntheticDefaultImports | boolean -`--noImplicitUseStrict` | TypeScriptNoImplicitUseStrict | boolean -`--project` | *Not supported in VS* | -`--watch` | *Not supported in VS* | -`--diagnostics` | *Not supported in VS* | -`--listFiles` | *Not supported in VS* | -`--noEmit` | *Not supported in VS* | -`--allowJs` | *Not supported in VS* | -*VS only option* | TypeScriptAdditionalFlags | *Any compiler option* +`--suppressImplicitAnyIndexErrors` | TypeScriptSuppressImplicitAnyIndexErrors | boolean +`--target` | TypeScriptTarget | `ES3`, `ES5`, or `ES6` +`--traceResolution` | *Not supported in MSBuild* | +`--types` | *Not supported in MSBuild* | +`--typeRoots` | *Not supported in MSBuild* | +`--useDefineForClassFields` | TypeScriptUseDefineForClassFields | boolean +`--watch` | *Not supported in MSBuild* | +*MSBuild only option* | TypeScriptAdditionalFlags | *Any compiler option* ## What is supported in my version of Visual Studio? @@ -77,7 +109,7 @@ The authoritative mappings between MSBuild XML tags and `tsc` compiler options l ## ToolsVersion The value of `1.7` property in the project file identifies the compiler version to use to build (1.7 in this example). -This allows a project to build against the save versions of the compiler on different machines. +This allows a project to build against the same versions of the compiler on different machines. If `TypeScriptToolsVersion` is not specified, the latest compiler version installed on the machine will be used to build. @@ -86,4 +118,4 @@ Users using newer versions of TS, will see a prompt to upgrade their project on ## TypeScriptCompileBlocked If you are using a different build tool to build your project (e.g. gulp, grunt , etc.) and VS for the development and debugging experience, set `true` in your project. -This should give you all the editing support, but not the build when you hit F5. \ No newline at end of file +This should give you all the editing support, but not the build when you hit F5. diff --git a/pages/Compiler Options.md b/pages/Compiler Options.md index 8264490ce..404348a21 100644 --- a/pages/Compiler Options.md +++ b/pages/Compiler Options.md @@ -1,63 +1,129 @@ +## Using the CLI + +Running `tsc` locally will compile the closest project defined by a `tsconfig.json`, you can compile a set of TypeScript +files by passing in a glob of files you want. + +```sh +# Run a compile based on a backwards look through the fs for a tsconfig.json +tsc + +# Transpile just the index.ts with the compiler defaults +tsc index.ts + +# Transpile any .ts files in the folder src, with the default settings +tsc src/*.ts + +# Transpile any .ts files in the folder src, with the compiler settings from tsconfig.json +tsc --project tsconfig.json src/*.ts +``` + ## Compiler Options +If you're looking for more information about the compiler options in a tsconfig, check out the TSConfig Reference beta +available in [the v2 site](https://www.staging-typescript.org/tsconfig). + Option | Type | Default | Description -----------------------------------------------|-----------|--------------------------------|---------------------------------------------------------------------- -`--allowJs` | `boolean` | `true` | Allow JavaScript files to be compiled. -`--allowSyntheticDefaultImports` | `boolean` | `(module === "system")` | Allow default imports from modules with no default export. This does not affect code emit, just typechecking. -`--allowUnreachableCode` | `boolean` | `false` | Do not report errors on unreachable code. +`--allowJs` | `boolean` | `false` | Allow JavaScript files to be compiled. +`--allowSyntheticDefaultImports` | `boolean` | `module === "system"` or `--esModuleInterop` | Allow default imports from modules with no default export. This does not affect code emit, just typechecking. +`--allowUmdGlobalAccess` | `boolean` | `false` | Allow accessing UMD globals from modules. +`--allowUnreachableCode` | `boolean` | `false` | Do not report errors on unreachable code. `--allowUnusedLabels` | `boolean` | `false` | Do not report errors on unused labels. +`--alwaysStrict` | `boolean` | `false` | Parse in strict mode and emit `"use strict"` for each source file +`--assumeChangesOnlyAffectDirectDependencies` | `boolean` | `false` | Have recompiles in '--incremental' and '--watch' assume that changes within a file will only affect files directly depending on it +`--baseUrl` | `string` | | Base directory to resolve non-relative module names. See [Module Resolution documentation](./Module%20Resolution.md#base-url) for more details. +`--build`
`-b` | `boolean` | `false` | Builds this project and all of its dependencies specified by [Project References](./Project%20References.md). Note that this flag is not compatible with others on this page. See more [here](./Project%20References.md) `--charset` | `string` | `"utf8"` | The character set of the input files. -`--declaration`
`-d` | `boolean` | `false` | Generates corresponding '.d.ts' file. +`--checkJs` | `boolean` | `false` | Report errors in `.js` files. Use in conjunction with `--allowJs`. +`--composite` | `boolean` | `true` | Ensure TypeScript can determine where to find the outputs of the referenced project to compile project. +`--declaration`
`-d` | `boolean` | `false` | Generates corresponding `.d.ts` file. +`--declarationDir` | `string` | | Output directory for generated declaration files. +`--declarationMap` | `boolean` | `false` | Generates a sourcemap for each corresponding '.d.ts' file. `--diagnostics` | `boolean` | `false` | Show diagnostic information. +`--disableSizeLimit` | `boolean` | `false` | Disable size limitation on JavaScript project. +`--downlevelIteration` | `boolean` | `false` | Provide full support for iterables in `for..of`, spread and destructuring when targeting ES5 or ES3. `--emitBOM` | `boolean` | `false` | Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. +`--emitDeclarationOnly` | `boolean` | `false` | Only emit '.d.ts' declaration files. `--emitDecoratorMetadata`[1] | `boolean` | `false` | Emit design-type metadata for decorated declarations in source. See [issue #2577](https://github.com/Microsoft/TypeScript/issues/2577) for details. -`--experimentalDecorators`[1] | `boolean` | `false` | Enables experimental support for ES7 decorators. +`--esModuleInterop` | `boolean` | `false` | Emit `__importStar` and `__importDefault` helpers for runtime babel ecosystem compatibility and enable `--allowSyntheticDefaultImports` for typesystem compatibility. +`--experimentalDecorators`[1] | `boolean` | `false` | Enables experimental support for ES decorators. +`--extendedDiagnostics` | `boolean` | `false` | Show verbose diagnostic information `--forceConsistentCasingInFileNames` | `boolean` | `false` | Disallow inconsistently-cased references to the same file. +`--generateCpuProfile` | `string` | `profile.cpuprofile` | Generates a cpu profile at the given path. Passing an existing directory name instead of a file path will cause a timestamp-named profile to be generated in that directory instead. `--help`
`-h` | | | Print help message. +`--importHelpers` | `boolean` | `false` | Import emit helpers (e.g. `__extends`, `__rest`, etc..) from [`tslib`](https://www.npmjs.com/package/tslib) +`--importsNotUsedAsValues` | `string` | `remove` | Specify emit/checking behavior for imports that are only used for types. `"remove"` and `"preserve"` specify whether to emit unused imports for side effects, and `"error"` enforces that imports used only for types are written with `import type`. +`--incremental` | `boolean` | `true` if `composite` is on, `false` otherwise | Enable incremental compilation by reading/writing information from prior compilations to a file on disk. This file is controlled by the `--tsBuildInfoFile` flag. `--inlineSourceMap` | `boolean` | `false` | Emit a single file with source maps instead of having a separate file. `--inlineSources` | `boolean` | `false` | Emit the source alongside the sourcemaps within a single file; requires `--inlineSourceMap` or `--sourceMap` to be set. `--init` | | | Initializes a TypeScript project and creates a `tsconfig.json` file. -`--isolatedModules` | `boolean` | `false` | Unconditionally emit imports for unresolved files. -`--jsx` | `string` | `"Preserve"` | Support JSX in '.tsx' files: `'React'` or `'Preserve'`. See [JSX](./JSX.md). +`--isolatedModules` | `boolean` | `false` | Perform additional checks to ensure that separate compilation (such as with [`transpileModule`](https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-simple-transform-function) or [@babel/plugin-transform-typescript](https://babeljs.io/docs/en/babel-plugin-transform-typescript)) would be safe. +`--jsx` | `string` | `"preserve"` | Support JSX in `.tsx` files: `"react"`, `"preserve"`, `"react-native"`. See [JSX](./JSX.md). +`--jsxFactory` | `string` | `"React.createElement"` | Specify the JSX factory function to use when targeting react JSX emit, e.g. `React.createElement` or `h`. +`--jsxFragmentFactory` | `string` | `"React.Fragment"` | Specify the JSX fragment reference to use when targeting react JSX emit, e.g. `React.Fragment` or `Fragment`. +`--keyofStringsOnly` | `boolean` | `false` | Resolve `keyof` to string valued property names only (no numbers or symbols). +`--useDefineForClassFields` | `boolean` | `false` | Emit class fields with ECMAScript-standard semantics. +`--lib` | `string[]`| | List of library files to be included in the compilation.
Possible values are:
► `ES5`
► `ES6`
► `ES2015`
► `ES7`
► `ES2016`
► `ES2017`
► `ES2018`
► `ESNext`
► `DOM`
► `DOM.Iterable`
► `WebWorker`
► `ScriptHost`
► `ES2015.Core`
► `ES2015.Collection`
► `ES2015.Generator`
► `ES2015.Iterable`
► `ES2015.Promise`
► `ES2015.Proxy`
► `ES2015.Reflect`
► `ES2015.Symbol`
► `ES2015.Symbol.WellKnown`
► `ES2016.Array.Include`
► `ES2017.object`
► `ES2017.Intl`
► `ES2017.SharedMemory`
► `ES2017.String`
► `ES2017.TypedArrays`
► `ES2018.Intl`
► `ES2018.Promise`
► `ES2018.RegExp`
► `ESNext.AsyncIterable`
► `ESNext.Array`
► `ESNext.Intl`
► `ESNext.Symbol`

Note: If `--lib` is not specified a default list of libraries are injected. The default libraries injected are:
► For `--target ES5`: `DOM,ES5,ScriptHost`
► For `--target ES6`: `DOM,ES6,DOM.Iterable,ScriptHost` +`--listEmittedFiles` | `boolean` | `false` | Print names of generated files part of the compilation. `--listFiles` | `boolean` | `false` | Print names of files part of the compilation. -`--locale` | `string` | *(platform specific)* | The locale to use to show error messages, e.g. en-us. -`--mapRoot` | `string` | `null` | Specifies the location where debugger should locate map files instead of generated locations. Use this flag if the .map files will be located at run-time in a different location than than the .js files. The location specified will be embedded in the sourceMap to direct the debugger where the map files where be located. -`--module`
`-m` | `string` | `(target === "ES6" ? "ES6" : "CommonJS")` | Specify module code generation: `'none'`, `'commonjs'`, `'amd'`, `'system'`, `'umd'`, `'es6'`, or `'es2015'`.
► Only `'amd'` and `'system'` can be used in conjunction with `--outFile`.
► `'es6'` and `'es2015'` values may not be used when targeting ES5 or lower. -`--moduleResolution` | `string` | `"Classic"` | Determine how modules get resolved. Either `'node'` for Node.js/io.js style resolution, or `'classic'` (default). See [Module Resolution documentation](Module Resolution.md) for more details. -`--newLine` | `string` | *(platform specific)* | Use the specified end of line sequence to be used when emitting files: `'crlf'` (windows) or `'lf'` (unix)." +`--locale` | `string` | *(platform specific)* | The locale to use to show error messages, e.g. en-us.
Possible values are:
► English (US): `en`
► Czech: `cs`
► German: `de`
► Spanish: `es`
► French: `fr`
► Italian: `it`
► Japanese: `ja`
► Korean: `ko`
► Polish: `pl`
► Portuguese(Brazil): `pt-BR`
► Russian: `ru`
► Turkish: `tr`
► Simplified Chinese: `zh-CN`
► Traditional Chinese: `zh-TW` +`--mapRoot` | `string` | | Specifies the location where debugger should locate map files instead of generated locations. Use this flag if the .map files will be located at run-time in a different location than the .js files. The location specified will be embedded in the sourceMap to direct the debugger where the map files will be located. This flag will not create the specified path and generate the map files in that location. Instead, create a post build step that moves the files to the specified path. +`--maxNodeModuleJsDepth` | `number` | `0` | The maximum dependency depth to search under node_modules and load JavaScript files. Only applicable with `--allowJs`. +`--module`
`-m` | `string` | `target === "ES3" or "ES5" ? "CommonJS" : "ES6"` | Specify module code generation: `"None"`, `"CommonJS"`, `"AMD"`, `"System"`, `"UMD"`, `"ES6"`, `"ES2015"` or `"ESNext"`.
► Only `"AMD"` and `"System"` can be used in conjunction with `--outFile`.
► `"ES6"` and `"ES2015"` values may be used when targeting `"ES5"` or lower. +`--moduleResolution` | `string` | `module === "AMD" or "System" or "ES6" ? "Classic" : "Node"` | Determine how modules get resolved. Either `"Node"` for Node.js/io.js style resolution, or `"Classic"`. See [Module Resolution documentation](./Module%20Resolution.md) for more details. +`--newLine` | `string` | *(platform specific)* | Use the specified end of line sequence to be used when emitting files: `"crlf"` (windows) or `"lf"` (unix)." `--noEmit` | `boolean` | `false` | Do not emit outputs. `--noEmitHelpers` | `boolean` | `false` | Do not generate custom helper functions like `__extends` in compiled output. `--noEmitOnError` | `boolean` | `false` | Do not emit outputs if any errors were reported. +`--noErrorTruncation` | `boolean` | `false` | Do not truncate error messages. `--noFallthroughCasesInSwitch` | `boolean` | `false` | Report errors for fallthrough cases in switch statement. -`--noImplicitAny` | `boolean` | `false` | Raise error on expressions and declarations with an implied 'any' type. -`--noImplicitReturns` | `boolean` | `false` | Report error when not all code paths in function return a value. +`--noImplicitAny` | `boolean` | `false` | Raise error on expressions and declarations with an implied `any` type. +`--noImplicitReturns` | `boolean` | `false` | Report an error when not all code paths in function return a value. +`--noImplicitThis` | `boolean` | `false` | Raise error on `this` expressions with an implied `any` type. `--noImplicitUseStrict` | `boolean` | `false` | Do not emit `"use strict"` directives in module output. -`--noLib` | `boolean` | `false` | Do not include the default library file (lib.d.ts). +`--noLib` | `boolean` | `false` | Do not include the default library file (`lib.d.ts`). `--noResolve` | `boolean` | `false` | Do not add triple-slash references or module import targets to the list of compiled files. -~~`--out`~~ | `string` | `null` | DEPRECATED. Use `--outFile` instead. -`--outDir` | `string` | `null` | Redirect output structure to the directory. -`--outFile` | `string` | `null` | Concatenate and emit output to single file. The order of concatenation is determined by the list of files passed to the compiler on the command line along with triple-slash references and imports. See output file order documentation for more details. +`--noStrictGenericChecks` | `boolean` | `false` | Disable strict checking of generic signatures in function types. +`--noUnusedLocals` | `boolean` | `false` | Report errors on unused locals. +`--noUnusedParameters` | `boolean` | `false` | Report errors on unused parameters. +~~`--out`~~ | `string` | | DEPRECATED. Use `--outFile` instead. +`--outDir` | `string` | | Redirect output structure to the directory. +`--outFile` | `string` | | Concatenate and emit output to single file. The order of concatenation is determined by the list of files passed to the compiler on the command line along with triple-slash references and imports. See [output file order documentation](https://github.com/Microsoft/TypeScript/wiki/FAQ#how-do-i-control-file-ordering-in-combined-output---out-) for more details. +`paths`[2] | `Object` | | List of path mapping entries for module names to locations relative to the `baseUrl`. See [Module Resolution documentation](./Module%20Resolution.md#path-mapping) for more details. `--preserveConstEnums` | `boolean` | `false` | Do not erase const enum declarations in generated code. See [const enums documentation](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#94-constant-enum-declarations) for more details. -`--pretty`[1] | `boolean` | `false` | Stylize errors and messages using color and context. -`--project`
`-p` | `string` | `null` | Compile the project in the given directory. The directory needs to contain a `tsconfig.json` file to direct compilation. See [tsconfig.json](./tsconfig.json.md) documentation for more details. -`--reactNamespace` | `string` | `"React"` | Specifies the object invoked for `createElement` and `__spread` when targeting 'react' JSX emit. +`--preserveSymlinks` | `boolean` | `false` | Do not resolve symlinks to their real path; treat a symlinked file like a real one. +`--preserveWatchOutput` | `boolean` | `false` | Keep outdated console output in watch mode instead of clearing the screen +`--pretty` | `boolean` | `true` unless piping to another program or redirecting output to a file | Stylize errors and messages using color and context. +`--project`
`-p` | `string` | | Compile a project given a valid configuration file.
The argument can be a file path to a valid JSON configuration file, or a directory path to a directory containing a `tsconfig.json` file.
See [tsconfig.json](./tsconfig.json.md) documentation for more details. +`--reactNamespace` | `string` | `"React"` | DEPRECATED. Use `--jsxFactory` instead.
Specifies the object invoked for `createElement` and `__spread` when targeting `"react"` JSX emit. `--removeComments` | `boolean` | `false` | Remove all comments except copy-right header comments beginning with `/*!` +`--resolveJsonModule` | `boolean` | `false` | Include modules imported with `.json` extension. `--rootDir` | `string` | *(common root directory is computed from the list of input files)* | Specifies the root directory of input files. Only use to control the output directory structure with `--outDir`. -`--skipDefaultLibCheck` | `boolean` | `false` | Don't check a user-defined default lib file's valitidy. -`--sourceMap` | `boolean` | `false` | Generates corresponding '.map' file. -`--sourceRoot` | `string` | `null` | Specifies the location where debugger should locate TypeScript files instead of source locations. Use this flag if the sources will be located at run-time in a different location than that at design-time. The location specified will be embedded in the sourceMap to direct the debugger where the source files where be located. +`rootDirs`[2] | `string[]`| | List of root folders whose combined content represent the structure of the project at runtime. See [Module Resolution documentation](./Module%20Resolution.md#virtual-directories-with-rootdirs) for more details. +`--showConfig` | `boolean` | `false` | Rather than actually execute a build with the other input options and config files, show the final implied config file in the output. +`--skipDefaultLibCheck` | `boolean` | `false` | DEPRECATED. Use `--skipLibCheck` instead.
Skip type checking of [default library declaration files](./Triple-Slash%20Directives.md#-reference-no-default-libtrue). +`--skipLibCheck` | `boolean` | `false` | Skip type checking of all declaration files (`*.d.ts`). +`--sourceMap` | `boolean` | `false` | Generates corresponding `.map` file. +`--sourceRoot` | `string` | | Specifies the location where debugger should locate TypeScript files instead of source locations. Use this flag if the sources will be located at run-time in a different location than that at design-time. The location specified will be embedded in the sourceMap to direct the debugger where the source files will be located. +`--strict` | `boolean` | `false` | Enable all strict type checking options.
Enabling `--strict` enables `--noImplicitAny`, `--noImplicitThis`, `--alwaysStrict`, `--strictBindCallApply`, `--strictNullChecks`, `--strictFunctionTypes` and `--strictPropertyInitialization`. +`--strictBindCallApply` | `boolean` | `false` | Enable stricter checking of the `bind`, `call`, and `apply` methods on functions. +`--strictFunctionTypes` | `boolean` | `false` | Disable bivariant parameter checking for function types. +`--strictPropertyInitialization` | `boolean` | `false` | Ensure non-undefined class properties are initialized in the constructor. This option requires `--strictNullChecks` be enabled in order to take effect. `--strictNullChecks` | `boolean` | `false` | In strict null checking mode, the `null` and `undefined` values are not in the domain of every type and are only assignable to themselves and `any` (the one exception being that `undefined` is also assignable to `void`). -`--stripInternal`[1] | `boolean` | `false` | Do not emit declarations for code that has an `/** @internal */` JSDoc annotation. `--suppressExcessPropertyErrors` | `boolean` | `false` | Suppress excess property checks for object literals. `--suppressImplicitAnyIndexErrors` | `boolean` | `false` | Suppress `--noImplicitAny` errors for indexing objects lacking index signatures. See [issue #1232](https://github.com/Microsoft/TypeScript/issues/1232#issuecomment-64510362) for more details. -`--target`
`-t` | `string` | `"ES5"` | Specify ECMAScript target version: `'es3'` (default), `'es5'`, or `'es6'`. +`--target`
`-t` | `string` | `"ES3"` | Specify ECMAScript target version:
► `"ES3"` (default)
► `"ES5"`
► `"ES6"`/`"ES2015"`
► `"ES2016"`
► `"ES2017"`
► `"ES2018"`
► `"ES2019"`
► `"ES2020"`
► `"ESNext"`

Note: `"ESNext"` targets latest supported [ES proposed features](https://github.com/tc39/proposals). `--traceResolution` | `boolean` | `false` | Report module resolution log messages. +`--tsBuildInfoFile` | `string` | `.tsbuildinfo` | Specify what file to store incremental build information in. +`--types` | `string[]`| | List of names of type definitions to include. See [@types, --typeRoots and --types](./tsconfig.json.md#types-typeroots-and-types) for more details. +`--typeRoots` | `string[]`| | List of folders to include type definitions from. See [@types, --typeRoots and --types](./tsconfig.json.md#types-typeroots-and-types) for more details. `--version`
`-v` | | | Print the compiler's version. -`--watch`
`-w` | | | Run the compiler in watch mode. Watch input files and trigger recompilation on changes. +`--watch`
`-w` | | | Run the compiler in watch mode. Watch input files and trigger recompilation on changes. The implementation of watching files and directories can be configured using environment variable. See [configuring watch](./Configuring%20Watch.md) for more details. -[1] These options are experimental. +* [1] These options are experimental. +* [2] These options are only allowed in `tsconfig.json`, and not through command-line switches. ## Related * Setting compiler options in [`tsconfig.json`](./tsconfig.json.md) files. -* Setting compiler options in [MSBuild projects](./Compiler Options in MSBuild.md). +* Setting compiler options in [MSBuild projects](./Compiler%20Options%20in%20MSBuild.md). diff --git a/pages/Configuring Watch.md b/pages/Configuring Watch.md new file mode 100644 index 000000000..10fdeb99c --- /dev/null +++ b/pages/Configuring Watch.md @@ -0,0 +1,30 @@ +Compiler supports configuring how to watch files and directories using the environment variables. + +## Configuring file watching using environment variable `TSC_WATCHFILE` + +Option | Description +-----------------------------------------------|---------------------------------------------------------------------- +`PriorityPollingInterval` | Use `fs.watchFile` but use different polling intervals for source files, config files and missing files +`DynamicPriorityPolling` | Use a dynamic queue where in the frequently modified files will be polled at shorter interval and the files unchanged will be polled less frequently +`UseFsEvents` | Use `fs.watch` which uses file system events (but might not be accurate on different OS) to get the notifications for the file changes/creation/deletion. Note that few OS eg. linux has limit on number of watches and failing to create watcher using `fs.watch` will result it in creating using `fs.watchFile` +`UseFsEventsWithFallbackDynamicPolling` | This option is similar to `UseFsEvents` except on failing to create watch using `fs.watch`, the fallback watching happens through dynamic polling queues (as explained in `DynamicPriorityPolling`) +`UseFsEventsOnParentDirectory` | This option watches parent directory of the file with `fs.watch` (using file system events) thus being low on CPU but can compromise accuracy. +default (no value specified) | If environment variable `TSC_NONPOLLING_WATCHER` is set to true, watches parent directory of files (just like `UseFsEventsOnParentDirectory`). Otherwise watch files using `fs.watchFile` with `250ms` as the timeout for any file + +## Configuring directory watching using environment variable `TSC_WATCHDIRECTORY` + +The watching of directory on platforms that don't support recursive directory watching natively in node, is supported through recursively creating directory watcher for the child directories using different options selected by `TSC_WATCHDIRECTORY`. Note that on platforms that support native recursive directory watching (e.g windows) the value of this environment variable is ignored. + +Option | Description +-----------------------------------------------|---------------------------------------------------------------------- +`RecursiveDirectoryUsingFsWatchFile` | Use `fs.watchFile` to watch the directories and child directories which is a polling watch (consuming CPU cycles) +`RecursiveDirectoryUsingDynamicPriorityPolling`| Use dynamic polling queue to poll changes to the directory and child directories. +default (no value specified) | Use `fs.watch` to watch directories and child directories + +## Background + +`--watch` implementation of the compiler relies on `fs.watch` and `fs.watchFile` provided by node, both of these methods have pros and cons. + +`fs.watch` uses file system events to notify the changes in the file/directory. But this is OS dependent and the notification is not completely reliable and does not work as expected on many OS. Also there could be limit on number of watches that can be created, eg. linux and we could exhaust it pretty quickly with programs that include large number of files. But because this uses file system events, there is not much CPU cycle involved. Compiler typically uses `fs.watch` to watch directories (eg. source directories included by config file, directories in which module resolution failed etc) These can handle the missing precision in notifying about the changes. But recursive watching is supported on only Windows and OSX. That means we need something to replace the recursive nature on other OS. + +`fs.watchFile` uses polling and thus involves CPU cycles. But this is the most reliable mechanism to get the update on the status of file/directory. Compiler typically uses `fs.watchFile` to watch source files, config files and missing files (missing file references) that means the CPU usage depends on number of files in the program. diff --git a/pages/Declaration Merging.md b/pages/Declaration Merging.md index 83f6e51b8..68226278c 100644 --- a/pages/Declaration Merging.md +++ b/pages/Declaration Merging.md @@ -46,8 +46,9 @@ interface Box { let box: Box = {height: 5, width: 6, scale: 10}; ``` -Non-function members of the interfaces must be unique. -The compiler will issue an error if the interfaces both declare a non-function member of the same name. +Non-function members of the interfaces should be unique. +If they are not unique, they must be of the same type. +The compiler will issue an error if the interfaces both declare a non-function member of the same name, but of different types. For function members, each function member of the same name is treated as describing an overload of the same function. Of note, too, is that in the case of interface `A` merging with later interface `A`, the second interface will have a higher precedence than the first. @@ -162,7 +163,7 @@ namespace Animal { namespace Animal { export function doAnimalsHaveMuscles() { - return haveMuscles; // <-- error, haveMuscles is not visible here + return haveMuscles; // Error, because haveMuscles is not accessible here } } ``` @@ -174,7 +175,7 @@ The `doAnimalsHaveMuscles` function, even though it's part of the merged `Animal Namespaces are flexible enough to also merge with other types of declarations. To do so, the namespace declaration must follow the declaration it will merge with. The resulting declaration has properties of both declaration types. -TypeScript uses this capability to model some of patterns in JavaScript as well as other programming languages. +TypeScript uses this capability to model some of the patterns in JavaScript as well as other programming languages. ## Merging Namespaces with Classes @@ -193,7 +194,7 @@ The visibility rules for merged members is the same as described in the 'Merging The end result is a class managed inside of another class. You can also use namespaces to add more static members to an existing class. -In addition to the pattern of inner classes, you may also be familiar with JavaScript practice of creating a function and then extending the function further by adding properties onto the function. +In addition to the pattern of inner classes, you may also be familiar with the JavaScript practice of creating a function and then extending the function further by adding properties onto the function. TypeScript uses declaration merging to build up definitions like this in a type-safe way. ```ts @@ -206,7 +207,7 @@ namespace buildLabel { export let prefix = "Hello, "; } -alert(buildLabel("Sam Smith")); +console.log(buildLabel("Sam Smith")); ``` Similarly, namespaces can be used to extend enums with static members: @@ -247,13 +248,14 @@ For information on mimicking class merging, see the [Mixins in TypeScript](./Mix Although JavaScript modules do not support merging, you can patch existing objects by importing and then updating them. Let's look at a toy Observable example: -```js -// observable.js + +```ts +// observable.ts export class Observable { // ... implementation left as an exercise for the reader ... } -// map.js +// map.ts import { Observable } from "./observable"; Observable.prototype.map = function (f) { // ... another exercise for the reader @@ -264,7 +266,11 @@ This works fine in TypeScript too, but the compiler doesn't know about `Observab You can use module augmentation to tell the compiler about it: ```ts -// observable.ts stays the same +// observable.ts +export class Observable { + // ... implementation left as an exercise for the reader ... +} + // map.ts import { Observable } from "./observable"; declare module "./observable" { @@ -287,7 +293,11 @@ o.map(x => x.toFixed()); The module name is resolved the same way as module specifiers in `import`/`export`. See [Modules](./Modules.md) for more information. Then the declarations in an augmentation are merged as if they were declared in the same file as the original. -However, you can't declare new top-level declarations in the augmentation -- just patches to existing declarations. + +However, there are two limitations to keep in mind: + +1. You can't declare new top-level declarations in the augmentation -- just patches to existing declarations. +2. Default exports also cannot be augmented, only named exports (since you need to augment an export by its exported name, and `default` is a reserved word - see [#14080](https://github.com/Microsoft/TypeScript/issues/14080) for details) ## Global augmentation diff --git a/pages/Decorators.md b/pages/Decorators.md index b58f92f04..e4994eb5b 100644 --- a/pages/Decorators.md +++ b/pages/Decorators.md @@ -2,7 +2,7 @@ With the introduction of Classes in TypeScript and ES6, there now exist certain scenarios that require additional features to support annotating or modifying classes and class members. Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members. -Decorators are a [stage 1 proposal](https://github.com/wycats/javascript-decorators/blob/master/README.md) for JavaScript and are available as an experimental feature of TypeScript. +Decorators are a [stage 2 proposal](https://github.com/tc39/proposal-decorators) for JavaScript and are available as an experimental feature of TypeScript. > NOTE  Decorators are an experimental feature that may change in future releases. @@ -34,7 +34,7 @@ For example, given the decorator `@sealed` we might write the `sealed` function ```ts function sealed(target) { - // do something with "target" ... + // do something with 'target' ... } ``` @@ -50,7 +50,7 @@ We can write a decorator factory in the following fashion: ```ts function color(value: string) { // this is the decorator factory return function (target) { // this is the decorator - // do something with "target" and "value"... + // do something with 'target' and 'value'... } } ``` @@ -134,7 +134,7 @@ The expression for the class decorator will be called as a function at runtime, If the class decorator returns a value, it will replace the class declaration with the provided constructor function. -> NOTE  Should you chose to return a new constructor function, you must take care to maintain the original prototype. +> NOTE  Should you choose to return a new constructor function, you must take care to maintain the original prototype. The logic that applies decorators at runtime will **not** do this for you. The following is an example of a class decorator (`@sealed`) applied to the `Greeter` class: @@ -163,6 +163,28 @@ function sealed(constructor: Function) { When `@sealed` is executed, it will seal both the constructor and its prototype. +Next we have an example of how to override the constructor. + +```ts +function classDecorator(constructor:T) { + return class extends constructor { + newProperty = "new property"; + hello = "override"; + } +} + +@classDecorator +class Greeter { + property = "property"; + hello: string; + constructor(m: string) { + this.hello = m; + } +} + +console.log(new Greeter("world")); +``` + ## Method Decorators A *Method Decorator* is declared just before a method declaration. @@ -272,13 +294,9 @@ The expression for the property decorator will be called as a function at runtim 2. The name of the member. > NOTE  A *Property Descriptor* is not provided as an argument to a property decorator due to how property decorators are initialized in TypeScript. -This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. +This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. The return value is ignored too. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class. -If the property decorator returns a value, it will be used as the *Property Descriptor* for the member. - -> NOTE  The return value is ignored if your script target is less than `ES5`. - We can use this information to record metadata about the property, as in the following example: ```ts @@ -453,6 +471,7 @@ function validate(target: any, propertyKey: string, descriptor: TypedProperty if (!(value instanceof type)) { throw new TypeError("Invalid type."); } + set.call(target, value); } } ``` diff --git a/pages/Enums.md b/pages/Enums.md index d2bf6008e..12b8d8c34 100644 --- a/pages/Enums.md +++ b/pages/Enums.md @@ -1,6 +1,12 @@ # Enums -Enums allow us to define a set of named numeric constants. +Enums allow us to define a set of named constants. +Using enums can make it easier to document intent, or create a set of distinct cases. +TypeScript provides both numeric and string-based enums. + +## Numeric enums + +We'll first start off with numeric enums, which are probably more familiar if you're coming from other languages. An enum can be defined using the `enum` keyword. ```ts @@ -8,28 +14,120 @@ enum Direction { Up = 1, Down, Left, - Right + Right, +} +``` + +Above, we have a numeric enum where `Up` is initialized with `1`. +All of the following members are auto-incremented from that point on. +In other words, `Direction.Up` has the value `1`, `Down` has `2`, `Left` has `3`, and `Right` has `4`. + +If we wanted, we could leave off the initializers entirely: + +```ts +enum Direction { + Up, + Down, + Left, + Right, +} +``` + +Here, `Up` would have the value `0`, `Down` would have `1`, etc. +This auto-incrementing behavior is useful for cases where we might not care about the member values themselves, but do care that each value is distinct from other values in the same enum. + +Using an enum is simple: just access any member as a property off of the enum itself, and declare types using the name of the enum: + +```ts +enum Response { + No = 0, + Yes = 1, +} + +function respond(recipient: string, message: Response): void { + // ... +} + +respond("Princess Caroline", Response.Yes) +``` + +Numeric enums can be mixed in [computed and constant members (see below)](#computed-and-constant-members). +The short story is, enums without initializers either need to be first, or have to come after numeric enums initialized with numeric constants or other constant enum members. +In other words, the following isn't allowed: + +```ts +enum E { + A = getSomeValue(), + B, // Error! Enum member must have initializer. } ``` -The body of an enum consists of zero or more enum members. -Enum members have numeric value associated with them and can be either *constant* or *computed*. +## String enums + +String enums are a similar concept, but have some subtle [runtime differences](#enums-at-runtime) as documented below. +In a string enum, each member has to be constant-initialized with a string literal, or with another string enum member. + +```ts +enum Direction { + Up = "UP", + Down = "DOWN", + Left = "LEFT", + Right = "RIGHT", +} +``` + +While string enums don't have auto-incrementing behavior, string enums have the benefit that they "serialize" well. +In other words, if you were debugging and had to read the runtime value of a numeric enum, the value is often opaque - it doesn't convey any useful meaning on its own (though [reverse mapping](#enums-at-runtime) can often help), string enums allow you to give a meaningful and readable value when your code runs, independent of the name of the enum member itself. + +## Heterogeneous enums + +Technically enums can be mixed with string and numeric members, but it's not clear why you would ever want to do so: + +```ts +enum BooleanLikeHeterogeneousEnum { + No = 0, + Yes = "YES", +} +``` + +Unless you're really trying to take advantage of JavaScript's runtime behavior in a clever way, it's advised that you don't do this. + +## Computed and constant members + +Each enum member has a value associated with it which can be either *constant* or *computed*. An enum member is considered constant if: -* It does not have an initializer and the preceding enum member was constant. - In this case the value of the current enum member will be the value of the preceding enum member plus one. - One exception to this rule is the first element on an enum. - If it does not have initializer it is assigned the value `0`. +* It is the first member in the enum and it has no initializer, in which case it's assigned the value `0`: + + ```ts + // E.X is constant: + enum E { X } + ``` + +* It does not have an initializer and the preceding enum member was a *numeric* constant. + In this case the value of the current enum member will be the value of the preceding enum member plus one. + + ```ts + // All enum members in 'E1' and 'E2' are constant. + + enum E1 { X, Y, Z } + + enum E2 { + A = 1, B, C + } + ``` + * The enum member is initialized with a constant enum expression. - A constant enum expression is a subset of TypeScript expressions that can be fully evaluated at compile time. - An expression is a constant enum expression if it is either: - * numeric literal - * reference to previously defined constant enum member (it can be defined in different enum). - If member is defined in the same enum it can be referenced using unqualified name. - * parenthesized constant enum expression - * `+`, `-`, `~` unary operators applied to constant enum expression - * `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `>>>`, `&`, `|`, `^` binary operators with constant enum expressions as operands - It is a compile time error for constant enum expressions to be evaluated to `NaN` or `Infinity`. + A constant enum expression is a subset of TypeScript expressions that can be fully evaluated at compile time. + An expression is a constant enum expression if it is: + + 1. a literal enum expression (basically a string literal or a numeric literal) + 2. a reference to previously defined constant enum member (which can originate from a different enum) + 3. a parenthesized constant enum expression + 4. one of the `+`, `-`, `~` unary operators applied to constant enum expression + 5. `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `>>>`, `&`, `|`, `^` binary operators with constant enum expressions as operands + + It is a compile time error for constant enum expressions to be evaluated to `NaN` or `Infinity`. In all other cases enum member is considered computed. @@ -45,18 +143,127 @@ enum FileAccess { } ``` +## Union enums and enum member types + +There is a special subset of constant enum members that aren't calculated: literal enum members. +A literal enum member is a constant enum member with no initialized value, or with values that are initialized to + +* any string literal (e.g. `"foo"`, `"bar`, `"baz"`) +* any numeric literal (e.g. `1`, `100`) +* a unary minus applied to any numeric literal (e.g. `-1`, `-100`) + +When all members in an enum have literal enum values, some special semantics come to play. + +The first is that enum members also become types as well! +For example, we can say that certain members can *only* have the value of an enum member: + +```ts +enum ShapeKind { + Circle, + Square, +} + +interface Circle { + kind: ShapeKind.Circle; + radius: number; +} + +interface Square { + kind: ShapeKind.Square; + sideLength: number; +} + +let c: Circle = { + kind: ShapeKind.Square, // Error! Type 'ShapeKind.Square' is not assignable to type 'ShapeKind.Circle'. + radius: 100, +} +``` + +The other change is that enum types themselves effectively become a *union* of each enum member. +While we haven't discussed [union types](./Advanced%20Types.md#union-types) yet, all that you need to know is that with union enums, the type system is able to leverage the fact that it knows the exact set of values that exist in the enum itself. +Because of that, TypeScript can catch silly bugs where we might be comparing values incorrectly. +For example: + +```ts +enum E { + Foo, + Bar, +} + +function f(x: E) { + if (x !== E.Foo || x !== E.Bar) { + // ~~~~~~~~~~~ + // Error! This condition will always return 'true' since the types 'E.Foo' and 'E.Bar' have no overlap. + } +} +``` + +In that example, we first checked whether `x` was *not* `E.Foo`. +If that check succeeds, then our `||` will short-circuit, and the body of the 'if' will run. +However, if the check didn't succeed, then `x` can *only* be `E.Foo`, so it doesn't make sense to see whether it's equal to `E.Bar`. + +## Enums at runtime + Enums are real objects that exist at runtime. -One reason is the ability to maintain a reverse mapping from enum values to enum names. +For example, the following enum + +```ts +enum E { + X, Y, Z +} +``` + +can actually be passed around to functions + +```ts +function f(obj: { X: number }) { + return obj.X; +} + +// Works, since 'E' has a property named 'X' which is a number. +f(E); +``` + +## Enums at compile time + +Even though Enums are real objects that exist at runtime, the `keyof` keyword works differently than you might expect for typical objects. Instead, use `keyof typeof` to get a Type that represents all Enum keys as strings. + +```ts +enum LogLevel { + ERROR, WARN, INFO, DEBUG +} + +/** + * This is equivalent to: + * type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'; + */ +type LogLevelStrings = keyof typeof LogLevel; + +function printImportant(key: LogLevelStrings, message: string) { + const num = LogLevel[key]; + if (num <= LogLevel.WARN) { + console.log('Log level key is: ', key); + console.log('Log level value is: ', num); + console.log('Log level message is: ', message); + } +} +printImportant('ERROR', 'This is a message'); +``` + +### Reverse mappings + +In addition to creating an object with property names for members, numeric enums members also get a *reverse mapping* from enum values to enum names. +For example, in this example: ```ts enum Enum { A } let a = Enum.A; -let nameOfA = Enum[Enum.A]; // "A" +let nameOfA = Enum[a]; // "A" ``` -is compiled to: +TypeScript might compile this down to something like the following JavaScript: ```js var Enum; @@ -64,15 +271,20 @@ var Enum; Enum[Enum["A"] = 0] = "A"; })(Enum || (Enum = {})); var a = Enum.A; -var nameOfA = Enum[Enum.A]; // "A" +var nameOfA = Enum[a]; // "A" ``` -In generated code an enum is compiled into an object that stores both forward (`name` -> `value`) and reverse (`value` -> `name`) mappings. -References to enum members are always emitted as property accesses and never inlined. -In lots of cases this is a perfectly valid solution. +In this generated code, an enum is compiled into an object that stores both forward (`name` -> `value`) and reverse (`value` -> `name`) mappings. +References to other enum members are always emitted as property accesses and never inlined. + +Keep in mind that string enum members *do not* get a reverse mapping generated at all. + +### `const` enums + +In most cases, enums are a perfectly valid solution. However sometimes requirements are tighter. -To avoid paying the cost of extra generated code and additional indirection when accessing enum values it is possible to use const enums. -Const enums are defined using the `const` modifier that precedes the `enum` keyword. +To avoid paying the cost of extra generated code and additional indirection when accessing enum values, it's possible to use `const` enums. +Const enums are defined using the `const` modifier on our enums: ```ts const enum Enum { @@ -102,7 +314,7 @@ in generated code will become var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]; ``` -# Ambient enums +## Ambient enums Ambient enums are used to describe the shape of already existing enum types. @@ -114,5 +326,5 @@ declare enum Enum { } ``` -One important difference between ambient and non-ambient enums is that, in regular enums, members that don't have an initializer are considered constant members. -For non-const ambient enums member that does not have initializer is considered computed. +One important difference between ambient and non-ambient enums is that, in regular enums, members that don't have an initializer will be considered constant if its preceding enum member is considered constant. +In contrast, an ambient (and non-const) enum member that does not have initializer is *always* considered computed. diff --git a/pages/Functions.md b/pages/Functions.md index 8ee74b85c..f09469bd3 100644 --- a/pages/Functions.md +++ b/pages/Functions.md @@ -1,6 +1,6 @@ # Introduction -Functions are the fundamental building block of any applications in JavaScript. +Functions are the fundamental building block of any application in JavaScript. They're how you build up layers of abstraction, mimicking classes, information hiding, and modules. In TypeScript, while there are classes, namespaces, and modules, functions still play the key role in describing how to *do* things. TypeScript also adds some new capabilities to the standard JavaScript functions to make them easier to work with. @@ -19,12 +19,12 @@ function add(x, y) { } // Anonymous function -let myAdd = function(x, y) { return x+y; }; +let myAdd = function(x, y) { return x + y; }; ``` Just as in JavaScript, functions can refer to variables outside of the function body. -When they do so, they're said to `capture` these variables. -While understanding how this works, and the trade-offs when using this technique, are outside of the scope of this article, having a firm understanding how this mechanic is an important piece of working with JavaScript and TypeScript. +When they do so, they're said to *capture* these variables. +While understanding how this works (and the trade-offs when using this technique) is outside of the scope of this article, having a firm understanding how this mechanic works is an important piece of working with JavaScript and TypeScript. ```ts let z = 100; @@ -45,7 +45,7 @@ function add(x: number, y: number): number { return x + y; } -let myAdd = function(x: number, y: number): number { return x+y; }; +let myAdd = function(x: number, y: number): number { return x + y; }; ``` We can add types to each of the parameters and then to the function itself to add a return type. @@ -53,11 +53,11 @@ TypeScript can figure the return type out by looking at the return statements, s ## Writing the function type -Now that we've typed the function, let's write the full type of the function out by looking at the each piece of the function type. +Now that we've typed the function, let's write the full type of the function out by looking at each piece of the function type. ```ts -let myAdd: (x: number, y: number)=>number = - function(x: number, y: number): number { return x+y; }; +let myAdd: (x: number, y: number) => number = + function(x: number, y: number): number { return x + y; }; ``` A function's type has the same two parts: the type of the arguments and the return type. @@ -67,7 +67,7 @@ This name is just to help with readability. We could have instead written: ```ts -let myAdd: (baseValue:number, increment:number) => number = +let myAdd: (baseValue: number, increment: number) => number = function(x: number, y: number): number { return x + y; }; ``` @@ -79,18 +79,19 @@ As mentioned before, this is a required part of the function type, so if the fun Of note, only the parameters and the return type make up the function type. Captured variables are not reflected in the type. -In effect, captured variables are part of the 'hidden state' of any function and do not make up its API. +In effect, captured variables are part of the "hidden state" of any function and do not make up its API. ## Inferring the types -In playing with the example, you may notice that the TypeScript compiler can figure out the type if you have types on one side of the equation but not the other: +In playing with the example, you may notice that the TypeScript compiler can figure out the type even if you only have types on one side of the equation: + ```ts // myAdd has the full function type let myAdd = function(x: number, y: number): number { return x + y; }; -// The parameters `x` and `y` have the type number -let myAdd: (baseValue:number, increment:number) => number = +// The parameters 'x' and 'y' have the type number +let myAdd: (baseValue: number, increment: number) => number = function(x, y) { return x + y; }; ``` @@ -100,7 +101,7 @@ This helps cut down on the amount of effort to keep your program typed. # Optional and Default Parameters In TypeScript, every parameter is assumed to be required by the function. -This doesn't mean that it can't be given `null` or `undefined`, but rather, when the function is called the compiler will check that the user has provided a value for each parameter. +This doesn't mean that it can't be given `null` or `undefined`, but rather, when the function is called, the compiler will check that the user has provided a value for each parameter. The compiler also assumes that these parameters are the only parameters that will be passed to the function. In short, the number of arguments given to a function has to match the number of parameters the function expects. @@ -133,7 +134,7 @@ let result3 = buildName("Bob", "Adams"); // ah, just right ``` Any optional parameters must follow required parameters. -Had we wanted to make the first name optional rather than the last name, we would need to change the order of parameters in the function, putting the first name last in the list. +Had we wanted to make the first name optional, rather than the last name, we would need to change the order of parameters in the function, putting the first name last in the list. In TypeScript, we can also set a value that a parameter will be assigned if the user does not provide one, or if the user passes `undefined` in its place. These are called default-initialized parameters. @@ -198,6 +199,7 @@ function buildName(firstName: string, ...restOfName: string[]) { return firstName + " " + restOfName.join(" "); } +// employeeName will be "Joseph Samuel Lucas MacKinzie" let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie"); ``` @@ -215,16 +217,19 @@ function buildName(firstName: string, ...restOfName: string[]) { let buildNameFun: (fname: string, ...rest: string[]) => string = buildName; ``` -# Lambdas and using `this` +# `this` -How `this` works in JavaScript functions is a common theme in programmers coming to JavaScript. -Indeed, learning how to use it is something of a rite of passage as developers become more accustomed to working in JavaScript. +Learning how to use `this` in JavaScript is something of a rite of passage. Since TypeScript is a superset of JavaScript, TypeScript developers also need to learn how to use `this` and how to spot when it's not being used correctly. -A whole article could be written on how to use `this` in JavaScript, and many have. Here, we'll focus on some of the basics. +Fortunately, TypeScript lets you catch incorrect uses of `this` with a couple of techniques. +If you need to learn how `this` works in JavaScript, though, first read Yehuda Katz's [Understanding JavaScript Function Invocation and "this"](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/). +Yehuda's article explains the inner workings of `this` very well, so we'll just cover the basics here. + +## `this` and arrow functions In JavaScript, `this` is a variable that's set when a function is called. This makes it a very powerful and flexible feature, but it comes at the cost of always having to know about the context that a function is executing in. -This can be notoriously confusing when, for instance, a function is used as a callback. +This is notoriously confusing, especially when returning a function or passing a function as an argument. Let's look at an example: @@ -248,22 +253,72 @@ let pickedCard = cardPicker(); alert("card: " + pickedCard.card + " of " + pickedCard.suit); ``` +Notice that `createCardPicker` is a function that itself returns a function. If we tried to run the example, we would get an error instead of the expected alert box. This is because the `this` being used in the function created by `createCardPicker` will be set to `window` instead of our `deck` object. -This happens as a result of calling `cardPicker()`. Here, there is no dynamic binding for `this` other than Window. (note: under strict mode, this will be undefined rather than window). +That's because we call `cardPicker()` on its own. +A top-level non-method syntax call like this will use `window` for `this`. +(Note: under strict mode, `this` will be `undefined` rather than `window`). We can fix this by making sure the function is bound to the correct `this` before we return the function to be used later. -This way, regardless of how its later used, it will still be able to see the original `deck` object. - -To fix this, we switch the function expression to use the arrow syntax (`() => {}`) rather than the JavaScript function expression. -This will automatically capture the `this` available when the function is created rather than when it is invoked: +This way, regardless of how it's later used, it will still be able to see the original `deck` object. +To do this, we change the function expression to use the ECMAScript 6 arrow syntax. +Arrow functions capture the `this` where the function is created rather than where it is invoked: ```ts let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function() { - // Notice: the line below is now a lambda, allowing us to capture `this` earlier + // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here + return () => { + let pickedCard = Math.floor(Math.random() * 52); + let pickedSuit = Math.floor(pickedCard / 13); + + return {suit: this.suits[pickedSuit], card: pickedCard % 13}; + } + } +} + +let cardPicker = deck.createCardPicker(); +let pickedCard = cardPicker(); + +alert("card: " + pickedCard.card + " of " + pickedCard.suit); +``` + +Even better, TypeScript will warn you when you make this mistake if you pass the `--noImplicitThis` flag to the compiler. +It will point out that `this` in `this.suits[pickedSuit]` is of type `any`. + +## `this` parameters + +Unfortunately, the type of `this.suits[pickedSuit]` is still `any`. +That's because `this` comes from the function expression inside the object literal. +To fix this, you can provide an explicit `this` parameter. +`this` parameters are fake parameters that come first in the parameter list of a function: + +```ts +function f(this: void) { + // make sure `this` is unusable in this standalone function +} +``` + +Let's add a couple of interfaces to our example above, `Card` and `Deck`, to make the types clearer and easier to reuse: + +```ts +interface Card { + suit: string; + card: number; +} +interface Deck { + suits: string[]; + cards: number[]; + createCardPicker(this: Deck): () => Card; +} +let deck: Deck = { + suits: ["hearts", "spades", "clubs", "diamonds"], + cards: Array(52), + // NOTE: The function now explicitly specifies that its callee must be of type Deck + createCardPicker: function(this: Deck) { return () => { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); @@ -279,7 +334,68 @@ let pickedCard = cardPicker(); alert("card: " + pickedCard.card + " of " + pickedCard.suit); ``` -For more information on ways to think about `this`, you can read Yehuda Katz's [Understanding JavaScript Function Invocation and "this"](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/). +Now TypeScript knows that `createCardPicker` expects to be called on a `Deck` object. +That means that `this` is of type `Deck` now, not `any`, so `--noImplicitThis` will not cause any errors. + +### `this` parameters in callbacks + +You can also run into errors with `this` in callbacks, when you pass functions to a library that will later call them. +Because the library that calls your callback will call it like a normal function, `this` will be `undefined`. +With some work you can use `this` parameters to prevent errors with callbacks too. +First, the library author needs to annotate the callback type with `this`: + +```ts +interface UIElement { + addClickListener(onclick: (this: void, e: Event) => void): void; +} +``` + +`this: void` means that `addClickListener` expects `onclick` to be a function that does not require a `this` type. +Second, annotate your calling code with `this`: + +```ts +class Handler { + info: string; + onClickBad(this: Handler, e: Event) { + // oops, used `this` here. using this callback would crash at runtime + this.info = e.message; + } +} +let h = new Handler(); +uiElement.addClickListener(h.onClickBad); // error! +``` + +With `this` annotated, you make it explicit that `onClickBad` must be called on an instance of `Handler`. +Then TypeScript will detect that `addClickListener` requires a function that has `this: void`. +To fix the error, change the type of `this`: + +```ts +class Handler { + info: string; + onClickGood(this: void, e: Event) { + // can't use `this` here because it's of type void! + console.log('clicked!'); + } +} +let h = new Handler(); +uiElement.addClickListener(h.onClickGood); +``` + +Because `onClickGood` specifies its `this` type as `void`, it is legal to pass to `addClickListener`. +Of course, this also means that it can't use `this.info`. +If you want both then you'll have to use an arrow function: + +```ts +class Handler { + info: string; + onClickGood = (e: Event) => { this.info = e.message } +} +``` + +This works because arrow functions use the outer `this`, so you can always pass them to something that expects `this: void`. +The downside is that one arrow function is created per object of type Handler. +Methods, on the other hand, are only created once and attached to Handler's prototype. +They are shared between all objects of type Handler. # Overloads @@ -311,7 +427,7 @@ let pickedCard2 = pickCard(15); alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); ``` -Here the `pickCard` function will return two different things based on what the user has passed in. +Here, the `pickCard` function will return two different things based on what the user has passed in. If the users passes in an object that represents the deck, the function will pick the card. If the user picks the card, we tell them which card they've picked. But how do we describe this to the type system? @@ -347,12 +463,12 @@ let pickedCard2 = pickCard(15); alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); ``` -With this change, the overloads now give us type-checked calls to the `pickCard` function. +With this change, the overloads now give us type checked calls to the `pickCard` function. -In order for the compiler to pick the correct typecheck, it follows a similar process to the underlying JavaScript. -It looks at the overload list, and proceeding with the first overload attempts to call the function with the provided parameters. +In order for the compiler to pick the correct type check, it follows a similar process to the underlying JavaScript. +It looks at the overload list and, proceeding with the first overload, attempts to call the function with the provided parameters. If it finds a match, it picks this overload as the correct overload. -For this reason, its customary to order overloads from most specific to least specific. +For this reason, it's customary to order overloads from most specific to least specific. Note that the `function pickCard(x): any` piece is not part of the overload list, so it only has two overloads: one that takes an object and one that takes a number. Calling `pickCard` with any other parameter types would cause an error. diff --git a/pages/Generics.md b/pages/Generics.md index 3400417dd..121f3547f 100644 --- a/pages/Generics.md +++ b/pages/Generics.md @@ -3,14 +3,14 @@ A major part of software engineering is building components that not only have well-defined and consistent APIs, but are also reusable. Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems. -In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is 'generics', that is, being able to create a component that can work over a variety of types rather than a single one. +In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is *generics*, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types. # Hello World of Generics To start off, let's do the "hello world" of generics: the identity function. The identity function is a function that will return back whatever is passed in. -You can think of this in a similar way to the 'echo' command. +You can think of this in a similar way to the `echo` command. Without generics, we would either have to give the identity function a specific type: @@ -28,7 +28,7 @@ function identity(arg: any): any { } ``` -While using `any` is certainly generic in that will accept any and all types for the type of `arg`, we actually are losing the information about what that type was when the function returns. +While using `any` is certainly generic in that it will cause the function to accept any and all types for the type of `arg`, we actually are losing the information about what that type was when the function returns. If we passed in a number, the only information we have is that any type could be returned. Instead, we need a way of capturing the type of the argument in such a way that we can also use it to denote what is being returned. @@ -55,15 +55,15 @@ The first way is to pass all of the arguments, including the type argument, to t let output = identity("myString"); // type of output will be 'string' ``` -Here we explicitly set `T` to be string as one of the arguments to the function call, denoted using the `<>` around the arguments rather than `()`. +Here we explicitly set `T` to be `string` as one of the arguments to the function call, denoted using the `<>` around the arguments rather than `()`. -The second way is also perhaps the most common. Here we use *type argument inference*, that is, we want the compiler to set the value of `T` for us automatically based on the type of the argument we pass in: +The second way is also perhaps the most common. Here we use *type argument inference* -- that is, we want the compiler to set the value of `T` for us automatically based on the type of the argument we pass in: ```ts let output = identity("myString"); // type of output will be 'string' ``` -Notice that we didn't have to explicitly pass the type in the angle brackets (`<>`), the compiler just looked at the value `"myString"`, and set `T` to its type. +Notice that we didn't have to explicitly pass the type in the angle brackets (`<>`); the compiler just looked at the value `"myString"`, and set `T` to its type. While type argument inference can be a helpful tool to keep code shorter and more readable, you may need to explicitly pass in the type arguments as we did in the previous example when the compiler fails to infer the type, as may happen in more complex examples. # Working with Generic Type Variables @@ -216,7 +216,7 @@ let stringNumeric = new GenericNumber(); stringNumeric.zeroValue = ""; stringNumeric.add = function(x, y) { return x + y; }; -alert(stringNumeric.add(stringNumeric.zeroValue, "test")); +console.log(stringNumeric.add(stringNumeric.zeroValue, "test")); ``` Just as with interface, putting the type parameter on the class itself lets us make sure all of the properties of the class are working with the same type. @@ -227,7 +227,7 @@ Generic classes are only generic over their instance side rather than their stat # Generic Constraints If you remember from an earlier example, you may sometimes want to write a generic function that works on a set of types where you have some knowledge about what capabilities that set of types will have. -In our `loggingIdentity` example, we wanted to be able access the `.length` property of `arg`, but the compiler could not prove that every type had a `.length` property, so it warns us that we can't make this assumption. +In our `loggingIdentity` example, we wanted to be able to access the `.length` property of `arg`, but the compiler could not prove that every type had a `.length` property, so it warns us that we can't make this assumption. ```ts function loggingIdentity(arg: T): T { @@ -269,21 +269,18 @@ loggingIdentity({length: 10, value: 3}); ## Using Type Parameters in Generic Constraints You can declare a type parameter that is constrained by another type parameter. -For example, here we'd like to take two objects and copy properties from one to the other. -We'd like to ensure that we're not accidentally writing any extra properties from our `source`, so we'll place a constraint between the two types: +For example, here we'd like to get a property from an object given its name. +We'd like to ensure that we're not accidentally grabbing a property that does not exist on the `obj`, so we'll place a constraint between the two types: ```ts -function copyFields(target: T, source: U): T { - for (let id in source) { - target[id] = source[id]; - } - return target; +function getProperty(obj: T, key: K) { + return obj[key]; } let x = { a: 1, b: 2, c: 3, d: 4 }; -copyFields(x, { b: 10, d: 20 }); // okay -copyFields(x, { Q: 90 }); // error: property 'Q' isn't declared in 'x'. +getProperty(x, "a"); // okay +getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'. ``` ## Using Class Types in Generics @@ -319,11 +316,10 @@ class Lion extends Animal { keeper: ZooKeeper; } -function findKeeper (a: {new(): A; - prototype: {keeper: K}}): K { - - return a.prototype.keeper; +function createInstance(c: new () => A): A { + return new c(); } -findKeeper(Lion).nametag; // typechecks! +createInstance(Lion).keeper.nametag; // typechecks! +createInstance(Bee).keeper.hasMask; // typechecks! ``` diff --git a/pages/Integrating with Build Tools.md b/pages/Integrating with Build Tools.md index c6d669cee..135282048 100644 --- a/pages/Integrating with Build Tools.md +++ b/pages/Integrating with Build Tools.md @@ -1,3 +1,52 @@ +Build tools + +* [Babel](#babel) +* [Browserify](#browserify) +* [Duo](#duo) +* [Grunt](#grunt) +* [Gulp](#gulp) +* [Jspm](#jspm) +* [Webpack](#webpack) +* [MSBuild](#msbuild) +* [NuGet](#nuget) + +# Babel + +### Install + +```sh +npm install @babel/cli @babel/core @babel/preset-typescript --save-dev +``` + +### .babelrc + +```js +{ + "presets": ["@babel/preset-typescript"] +} +``` +### Using Command Line Interface +```sh +./node_modules/.bin/babel --out-file bundle.js src/index.ts +``` + + +### package.json + +```js +{ + "scripts": { + "build": "babel --out-file bundle.js main.ts" + }, +} +``` + +### Execute Babel from the command line +```sh +npm run build +``` + + # Browserify ### Install @@ -19,8 +68,8 @@ var browserify = require("browserify"); var tsify = require("tsify"); browserify() - .add('main.ts') - .plugin('tsify', { noImplicitAny: true }) + .add("main.ts") + .plugin("tsify", { noImplicitAny: true }) .bundle() .pipe(process.stdout); ``` @@ -44,15 +93,15 @@ duo --use duo-typescript entry.ts ### Using API ```js -var Duo = require('duo'); -var fs = require('fs') -var path = require('path') -var typescript = require('duo-typescript'); +var Duo = require("duo"); +var fs = require("fs") +var path = require("path") +var typescript = require("duo-typescript"); var out = path.join(__dirname, "output.js") Duo(__dirname) - .entry('entry.ts') + .entry("entry.ts") .use(typescript()) .run(function (err, results) { if (err) throw err; @@ -89,7 +138,7 @@ module.exports = function(grunt) { More details: [TypeStrong/grunt-ts](https://github.com/TypeStrong/grunt-ts) -# gulp +# Gulp ### Install @@ -109,13 +158,13 @@ gulp.task("default", function () { noImplicitAny: true, out: "output.js" })); - return tsResult.js.pipe(gulp.dest('built/local')); + return tsResult.js.pipe(gulp.dest("built/local")); }); ``` More details: [ivogabe/gulp-typescript](https://github.com/ivogabe/gulp-typescript) -# jspm +# Jspm ### Install @@ -127,7 +176,7 @@ _Note: Currently TypeScript support in jspm is in 0.16beta_ More details: [TypeScriptSamples/jspm](https://github.com/Microsoft/TypeScriptSamples/tree/master/jspm) -# webpack +# Webpack ### Install @@ -135,7 +184,28 @@ More details: [TypeScriptSamples/jspm](https://github.com/Microsoft/TypeScriptSa npm install ts-loader --save-dev ``` -### Basic webpack.config.js +### Basic webpack.config.js when using Webpack 2 + +```js +module.exports = { + entry: "./src/index.tsx", + output: { + path: '/', + filename: "bundle.js" + }, + resolve: { + extensions: [".tsx", ".ts", ".js", ".json"] + }, + module: { + rules: [ + // all files with a '.ts' or '.tsx' extension will be handled by 'ts-loader' + { test: /\.tsx?$/, use: ["ts-loader"], exclude: /node_modules/ } + ] + } +} +``` + +### Basic webpack.config.js when using Webpack 1 ```js module.exports = { @@ -148,7 +218,7 @@ module.exports = { extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"] }, module: { - loaders: [ + rules: [ // all files with a '.ts' or '.tsx' extension will be handled by 'ts-loader' { test: /\.tsx?$/, loader: "ts-loader" } ] @@ -169,7 +239,7 @@ Update project file to include locally installed `Microsoft.TypeScript.Default.p ```xml - + @@ -191,7 +261,7 @@ Update project file to include locally installed `Microsoft.TypeScript.Default.p ``` -More details about defining MSBuild compiler options: [Setting Compiler Options in MSBuild projects](./Compiler Options in MSBuild.md) +More details about defining MSBuild compiler options: [Setting Compiler Options in MSBuild projects](./Compiler%20Options%20in%20MSBuild.md) # NuGet diff --git a/pages/Interfaces.md b/pages/Interfaces.md index 12610e672..59fd8784f 100644 --- a/pages/Interfaces.md +++ b/pages/Interfaces.md @@ -1,6 +1,6 @@ # Introduction -One of TypeScript's core principles is that type-checking focuses on the *shape* that values have. +One of TypeScript's core principles is that type checking focuses on the *shape* that values have. This is sometimes called "duck typing" or "structural subtyping". In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project. @@ -9,40 +9,40 @@ In TypeScript, interfaces fill the role of naming these types, and are a powerfu The easiest way to see how interfaces work is to start with a simple example: ```ts -function printLabel(labelledObj: { label: string }) { - console.log(labelledObj.label); +function printLabel(labeledObj: { label: string }) { + console.log(labeledObj.label); } let myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj); ``` -The type-checker checks the call to `printLabel`. -The `printLabel` function has a single parameter that requires that the object passed in has a property called `label` of type string. +The type checker checks the call to `printLabel`. +The `printLabel` function has a single parameter that requires that the object passed in has a property called `label` of type `string`. Notice that our object actually has more properties than this, but the compiler only checks that *at least* the ones required are present and match the types required. There are some cases where TypeScript isn't as lenient, which we'll cover in a bit. We can write the same example again, this time using an interface to describe the requirement of having the `label` property that is a string: ```ts -interface LabelledValue { +interface LabeledValue { label: string; } -function printLabel(labelledObj: LabelledValue) { - console.log(labelledObj.label); +function printLabel(labeledObj: LabeledValue) { + console.log(labeledObj.label); } let myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj); ``` -The interface `LabelledValue` is a name we can now use to describe the requirement in the previous example. -It still represents having a single property called `label` that is of type string. +The interface `LabeledValue` is a name we can now use to describe the requirement in the previous example. +It still represents having a single property called `label` that is of type `string`. Notice we didn't have to explicitly say that the object we pass to `printLabel` implements this interface like we might have to in other languages. Here, it's only the shape that matters. If the object we pass to the function meets the requirements listed, then it's allowed. -It's worth pointing out that the type-checker does not require that these properties come in any sort of order, only that the properties the interface requires are present and have the required type. +It's worth pointing out that the type checker does not require that these properties come in any sort of order, only that the properties the interface requires are present and have the required type. # Optional Properties @@ -85,9 +85,9 @@ interface SquareConfig { function createSquare(config: SquareConfig): { color: string; area: number } { let newSquare = {color: "white", area: 100}; - if (config.color) { - // Error: Property 'collor' does not exist on type 'SquareConfig' - newSquare.color = config.collor; + if (config.clor) { + // Error: Property 'clor' does not exist on type 'SquareConfig' + newSquare.color = config.clor; } if (config.width) { newSquare.area = config.width * config.width; @@ -98,13 +98,55 @@ function createSquare(config: SquareConfig): { color: string; area: number } { let mySquare = createSquare({color: "black"}); ``` +# Readonly properties + +Some properties should only be modifiable when an object is first created. +You can specify this by putting `readonly` before the name of the property: + +```ts +interface Point { + readonly x: number; + readonly y: number; +} +``` + +You can construct a `Point` by assigning an object literal. +After the assignment, `x` and `y` can't be changed. + +```ts +let p1: Point = { x: 10, y: 20 }; +p1.x = 5; // error! +``` + +TypeScript comes with a `ReadonlyArray` type that is the same as `Array` with all mutating methods removed, so you can make sure you don't change your arrays after creation: + +```ts +let a: number[] = [1, 2, 3, 4]; +let ro: ReadonlyArray = a; +ro[0] = 12; // error! +ro.push(5); // error! +ro.length = 100; // error! +a = ro; // error! +``` + +On the last line of the snippet you can see that even assigning the entire `ReadonlyArray` back to a normal array is illegal. +You can still override it with a type assertion, though: + +```ts +a = ro as number[]; +``` + +## `readonly` vs `const` + +The easiest way to remember whether to use `readonly` or `const` is to ask whether you're using it on a variable or a property. +Variables use `const` whereas properties use `readonly`. + # Excess Property Checks -In our first example using interfaces, TypeScript let us pass `{ size: number; label: string; }` to something that only expected a `{ label: string; }`. +In our first example using interfaces, TypeScript lets us pass `{ size: number; label: string; }` to something that only expected a `{ label: string; }`. We also just learned about optional properties, and how they're useful when describing so-called "option bags". -However, combining the two naively would let you to shoot yourself in the foot the same way you might in JavaScript. -For example, taking our last example using `createSquare`: +However, combining the two naively would allow an error to sneak in. For example, taking our last example using `createSquare`: ```ts interface SquareConfig { @@ -126,10 +168,10 @@ You could argue that this program is correctly typed, since the `width` properti However, TypeScript takes the stance that there's probably a bug in this code. Object literals get special treatment and undergo *excess property checking* when assigning them to other variables, or passing them as arguments. -If an object literal has any properties that the "target type" doesn't have, you'll get an error. +If an object literal has any properties that the "target type" doesn't have, you'll get an error: ```ts -// error: 'colour' not expected in type 'SquareConfig' +// error: Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write 'color'? let mySquare = createSquare({ colour: "red", width: 100 }); ``` @@ -141,7 +183,7 @@ let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig); ``` However, a better approach might be to add a string index signature if you're sure that the object can have some extra properties that are used in some special way. -If `SquareConfig`s can have `color` and `width` properties with the above types, but could *also* have any number of other properties, then we could define it like so: +If `SquareConfig` can have `color` and `width` properties with the above types, but could *also* have any number of other properties, then we could define it like so: ```ts interface SquareConfig { @@ -161,6 +203,14 @@ let squareOptions = { colour: "red", width: 100 }; let mySquare = createSquare(squareOptions); ``` +The above workaround will work as long as you have a common property between `squareOptions` and `SquareConfig`. +In this example, it was the property `width`. It will however, fail if the variable does not have any common object property. For example: + +```ts +let squareOptions = { colour: "red" }; +let mySquare = createSquare(squareOptions); +``` + Keep in mind that for simple code like above, you probably shouldn't be trying to "get around" these checks. For more complex object literals that have methods and hold state, you might need to keep these techniques in mind, but a majority of excess property errors are actually bugs. That means if you're running into excess property checking problems for something like option bags, you might need to revise some of your type declarations. @@ -187,49 +237,46 @@ Here, we show how you can create a variable of a function type and assign it a f let mySearch: SearchFunc; mySearch = function(source: string, subString: string) { let result = source.search(subString); - if (result == -1) { - return false; - } - else { - return true; - } + return result > -1; } ``` -For function types to correctly type-check, the names of the parameters do not need to match. +For function types to correctly type check, the names of the parameters do not need to match. We could have, for example, written the above example like this: ```ts let mySearch: SearchFunc; mySearch = function(src: string, sub: string): boolean { let result = src.search(sub); - if (result == -1) { - return false; - } - else { - return true; - } + return result > -1; } ``` Function parameters are checked one at a time, with the type in each corresponding parameter position checked against each other. -If you do not want to specify types at all, Typescript's contextual typing can infer the argument types since the function value is assigned directly to a variable of type `SearchFunc`. +If you do not want to specify types at all, TypeScript's contextual typing can infer the argument types since the function value is assigned directly to a variable of type `SearchFunc`. Here, also, the return type of our function expression is implied by the values it returns (here `false` and `true`). -Had the function expression returned numbers or strings, the type-checker would have warned us that return type doesn't match the return type described in the `SearchFunc` interface. ```ts let mySearch: SearchFunc; mySearch = function(src, sub) { let result = src.search(sub); - if (result == -1) { - return false; - } - else { - return true; - } + return result > -1; } ``` +Had the function expression returned numbers or strings, the type checker would have made an error that indicates return type doesn't match the return type described in the `SearchFunc` interface. + +```ts +let mySearch: SearchFunc; + +// error: Type '(src: string, sub: string) => string' is not assignable to type 'SearchFunc'. +// Type 'string' is not assignable to type 'boolean'. +mySearch = function(src, sub) { + let result = src.search(sub); + return "string"; +}; +``` + # Indexable Types Similarly to how we can use interfaces to describe function types, we can also describe types that we can "index into" like `a[10]`, or `ageMap["daniel"]`. @@ -263,7 +310,7 @@ class Dog extends Animal { breed: string; } -// Error: indexing with a 'string' will sometimes get you a Dog! +// Error: indexing with a numeric string might get you a completely separate type of Animal! interface NotOkay { [x: number]: Animal; [x: string]: Dog; @@ -272,7 +319,7 @@ interface NotOkay { While string index signatures are a powerful way to describe the "dictionary" pattern, they also enforce that all properties match their return type. This is because a string index declares that `obj.property` is also available as `obj["property"]`. -In the following example, `name`'s type does not match the string index's type, and the type-checker gives an error: +In the following example, `name`'s type does not match the string index's type, and the type checker gives an error: ```ts interface NumberDictionary { @@ -282,6 +329,28 @@ interface NumberDictionary { } ``` +However, properties of different types are acceptable if the index signature is a union of the property types: + +```ts +interface NumberOrStringDictionary { + [index: string]: number | string; + length: number; // ok, length is a number + name: string; // ok, name is a string +} +``` + +Finally, you can make index signatures `readonly` in order to prevent assignment to their indices: + +```ts +interface ReadonlyStringArray { + readonly [index: number]: string; +} +let myArray: ReadonlyStringArray = ["Alice", "Bob"]; +myArray[2] = "Mallory"; // error! +``` + +You can't set `myArray[2]` because the index signature is readonly. + # Class Types ## Implementing an interface @@ -294,7 +363,7 @@ interface ClockInterface { } class Clock implements ClockInterface { - currentTime: Date; + currentTime: Date = new Date(); constructor(h: number, m: number) { } } ``` @@ -304,11 +373,11 @@ You can also describe methods in an interface that are implemented in the class, ```ts interface ClockInterface { currentTime: Date; - setTime(d: Date); + setTime(d: Date): void; } class Clock implements ClockInterface { - currentTime: Date; + currentTime: Date = new Date(); setTime(d: Date) { this.currentTime = d; } @@ -340,14 +409,14 @@ Since the constructor sits in the static side, it is not included in this check. Instead, you would need to work with the static side of the class directly. In this example, we define two interfaces, `ClockConstructor` for the constructor and `ClockInterface` for the instance methods. -Then for convenience we define a constructor function `createClock` that creates instances of the type that is passed to it. +Then, for convenience, we define a constructor function `createClock` that creates instances of the type that is passed to it: ```ts interface ClockConstructor { new (hour: number, minute: number): ClockInterface; } interface ClockInterface { - tick(); + tick(): void; } function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface { @@ -373,6 +442,26 @@ let analog = createClock(AnalogClock, 7, 32); Because `createClock`'s first parameter is of type `ClockConstructor`, in `createClock(AnalogClock, 7, 32)`, it checks that `AnalogClock` has the correct constructor signature. +Another simple way is to use class expressions: + +```ts +interface ClockConstructor { + new (hour: number, minute: number); +} + +interface ClockInterface { + tick(); +} + +const Clock: ClockConstructor = class Clock implements ClockInterface { + constructor(h: number, m: number) {} + tick() { + console.log("beep beep"); + } +} +``` + + # Extending Interfaces Like classes, interfaces can extend each other. @@ -387,7 +476,7 @@ interface Square extends Shape { sideLength: number; } -let square = {}; +let square = {} as Square; square.color = "blue"; square.sideLength = 10; ``` @@ -407,7 +496,7 @@ interface Square extends Shape, PenStroke { sideLength: number; } -let square = {}; +let square = {} as Square; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0; @@ -428,7 +517,7 @@ interface Counter { } function getCounter(): Counter { - let counter = function (start: number) { }; + let counter = (function (start: number) { }) as Counter; counter.interval = 123; counter.reset = function () { }; return counter; @@ -462,7 +551,7 @@ interface SelectableControl extends Control { select(): void; } -class Button extends Control { +class Button extends Control implements SelectableControl { select() { } } @@ -470,11 +559,14 @@ class TextBox extends Control { select() { } } -class Image extends Control { +// Error: Property 'state' is missing in type 'Image'. +class Image implements SelectableControl { + private state: any; + select() { } } class Location { - select() { } + } ``` diff --git a/pages/Iterators and Generators.md b/pages/Iterators and Generators.md index d7c12ba80..ded388b4d 100644 --- a/pages/Iterators and Generators.md +++ b/pages/Iterators and Generators.md @@ -27,11 +27,11 @@ Here is an example that demonstrates this distinction: let list = [4, 5, 6]; for (let i in list) { - console.log(i); // "0", "1", "2", + console.log(i); // "0", "1", "2", } for (let i of list) { - console.log(i); // "4", "5", "6" + console.log(i); // "4", "5", "6" } ``` @@ -43,7 +43,7 @@ let pets = new Set(["Cat", "Dog", "Hamster"]); pets["species"] = "mammals"; for (let pet in pets) { - console.log(pet); // "species" + console.log(pet); // "species" } for (let pet of pets) { @@ -55,7 +55,7 @@ for (let pet of pets) { #### Targeting ES5 and ES3 -When targeting an ES5 or ES3, iterators are only allowed on values of `Array` type. +When targeting an ES5 or ES3-compliant engine, iterators are only allowed on values of `Array` type. It is an error to use `for..of` loops on non-Array values, even if these non-Array values implement the `Symbol.iterator` property. The compiler will generate a simple `for` loop for a `for..of` loop, for instance: diff --git a/pages/JSDoc Supported Types.md b/pages/JSDoc Supported Types.md new file mode 100644 index 000000000..468698928 --- /dev/null +++ b/pages/JSDoc Supported Types.md @@ -0,0 +1,585 @@ +The list below outlines which constructs are currently supported +when using JSDoc annotations to provide type information in JavaScript files. + +Note any tags which are not explicitly listed below (such as `@async`) are not yet supported. + +* `@type` +* `@param` (or `@arg` or `@argument`) +* `@returns` (or `@return`) +* `@typedef` +* `@callback` +* `@template` +* `@class` (or `@constructor`) +* `@this` +* `@extends` (or `@augments`) +* `@enum` + +The meaning is usually the same, or a superset, of the meaning of the tag given at [jsdoc.app](https://jsdoc.app). +The code below describes the differences and gives some example usage of each tag. + +**Note:** You can use [the playground to explore JSDoc support](https://www.typescriptlang.org/play/index.html?useJavaScript=truee=4#example/jsdoc-support). + +## `@type` + +You can use the "@type" tag and reference a type name (either primitive, defined in a TypeScript declaration, or in a JSDoc "@typedef" tag). +You can use any Typescript type, and most JSDoc types. + +```js +/** + * @type {string} + */ +var s; + +/** @type {Window} */ +var win; + +/** @type {PromiseLike} */ +var promisedString; + +// You can specify an HTML Element with DOM properties +/** @type {HTMLElement} */ +var myElement = document.querySelector(selector); +element.dataset.myData = ''; + +``` + +`@type` can specify a union type — for example, something can be either a string or a boolean. + +```js +/** + * @type {(string | boolean)} + */ +var sb; +``` + +Note that parentheses are optional for union types. + +```js +/** + * @type {string | boolean} + */ +var sb; +``` + +You can specify array types using a variety of syntaxes: + +```js +/** @type {number[]} */ +var ns; +/** @type {Array.} */ +var nds; +/** @type {Array} */ +var nas; +``` + +You can also specify object literal types. +For example, an object with properties 'a' (string) and 'b' (number) uses the following syntax: + +```js +/** @type {{ a: string, b: number }} */ +var var9; +``` + +You can specify map-like and array-like objects using string and number index signatures, using either standard JSDoc syntax or Typescript syntax. + +```js +/** + * A map-like object that maps arbitrary `string` properties to `number`s. + * + * @type {Object.} + */ +var stringToNumber; + +/** @type {Object.} */ +var arrayLike; +``` + +The preceding two types are equivalent to the Typescript types `{ [x: string]: number }` and `{ [x: number]: any }`. The compiler understands both syntaxes. + +You can specify function types using either Typescript or Closure syntax: + +```js +/** @type {function(string, boolean): number} Closure syntax */ +var sbn; +/** @type {(s: string, b: boolean) => number} Typescript syntax */ +var sbn2; +``` + +Or you can just use the unspecified `Function` type: + +```js +/** @type {Function} */ +var fn7; +/** @type {function} */ +var fn6; +``` + +Other types from Closure also work: + +```js +/** + * @type {*} - can be 'any' type + */ +var star; +/** + * @type {?} - unknown type (same as 'any') + */ +var question; +``` + +### Casts + +Typescript borrows cast syntax from Closure. +This lets you cast types to other types by adding a `@type` tag before any parenthesized expression. + +```js +/** + * @type {number | string} + */ +var numberOrString = Math.random() < 0.5 ? "hello" : 100; +var typeAssertedNumber = /** @type {number} */ (numberOrString) +``` + +### Import types + +You can also import declarations from other files using import types. +This syntax is Typescript-specific and differs from the JSDoc standard: + +```js +/** + * @param p { import("./a").Pet } + */ +function walk(p) { + console.log(`Walking ${p.name}...`); +} +``` + +import types can also be used in type alias declarations: + +```js +/** + * @typedef { import("./a").Pet } Pet + */ + +/** + * @type {Pet} + */ +var myPet; +myPet.name; +``` + +import types can be used to get the type of a value from a module if you don't know the type, or if it has a large type that is annoying to type: + +```js +/** + * @type {typeof import("./a").x } + */ +var x = require("./a").x; +``` + +## `@param` and `@returns` + +`@param` uses the same type syntax as `@type`, but adds a parameter name. +The parameter may also be declared optional by surrounding the name with square brackets: + +```js +// Parameters may be declared in a variety of syntactic forms +/** + * @param {string} p1 - A string param. + * @param {string=} p2 - An optional param (Closure syntax) + * @param {string} [p3] - Another optional param (JSDoc syntax). + * @param {string} [p4="test"] - An optional param with a default value + * @return {string} This is the result + */ +function stringsStringStrings(p1, p2, p3, p4){ + // TODO +} +``` + +Likewise, for the return type of a function: + +```js +/** + * @return {PromiseLike} + */ +function ps(){} + +/** + * @returns {{ a: string, b: number }} - May use '@returns' as well as '@return' + */ +function ab(){} +``` + +## `@typedef`, `@callback`, and `@param` + +`@typedef` may be used to define complex types. +Similar syntax works with `@param`. + + +```js +/** + * @typedef {Object} SpecialType - creates a new type named 'SpecialType' + * @property {string} prop1 - a string property of SpecialType + * @property {number} prop2 - a number property of SpecialType + * @property {number=} prop3 - an optional number property of SpecialType + * @prop {number} [prop4] - an optional number property of SpecialType + * @prop {number} [prop5=42] - an optional number property of SpecialType with default + */ +/** @type {SpecialType} */ +var specialTypeObject; +``` + +You can use either `object` or `Object` on the first line. + +```js +/** + * @typedef {object} SpecialType1 - creates a new type named 'SpecialType' + * @property {string} prop1 - a string property of SpecialType + * @property {number} prop2 - a number property of SpecialType + * @property {number=} prop3 - an optional number property of SpecialType + */ +/** @type {SpecialType1} */ +var specialTypeObject1; +``` + +`@param` allows a similar syntax for one-off type specifications. +Note that the nested property names must be prefixed with the name of the parameter: + + +```js +/** + * @param {Object} options - The shape is the same as SpecialType above + * @param {string} options.prop1 + * @param {number} options.prop2 + * @param {number=} options.prop3 + * @param {number} [options.prop4] + * @param {number} [options.prop5=42] + */ +function special(options) { + return (options.prop4 || 1001) + options.prop5; +} +``` + +`@callback` is similar to `@typedef`, but it specifies a function type instead of an object type: + +```js +/** + * @callback Predicate + * @param {string} data + * @param {number} [index] + * @returns {boolean} + */ +/** @type {Predicate} */ +const ok = s => !(s.length % 2); +``` + +Of course, any of these types can be declared using Typescript syntax in a single-line `@typedef`: + +```js +/** @typedef {{ prop1: string, prop2: string, prop3?: number }} SpecialType */ +/** @typedef {(data: string, index?: number) => boolean} Predicate */ +``` + +## `@template` + +You can declare generic functions with the `@template` tag: + +```js +/** + * @template T + * @param {T} p1 - A generic parameter that flows through to the return type + * @return {T} + */ +function id(x){ return x } +``` + +Use comma or multiple tags to declare multiple type parameters: + +```js +/** + * @template T,U,V + * @template W,X + */ +``` + +You can also specify a type constraint before the type parameter name. +Only the first type parameter in a list is constrained: + +```js +/** + * @template {string} K - K must be a string or string literal + * @template {{ serious(): string }} Seriousalizable - must have a serious method + * @param {K} key + * @param {Seriousalizable} object + */ +function seriousalize(key, object) { + // ???? +} +``` + +Declaring generic classes or types is unsupported. + +## Classes + +Classes can be declared as ES6 classes. + +```js +class C { + /** + * @param {number} data + */ + constructor(data) { + // property types can be inferred + this.name = "foo"; + + // or set explicitly + /** @type {string | null} */ + this.title = null; + + // or simply annotated, if they're set elsewhere + /** @type {number} */ + this.size; + + this.initialize(data); // Should error, initializer expects a string + } + /** + * @param {string} s + */ + initialize = function (s) { + this.size = s.length + } +} + +var c = new C(0); +var result = C(1); // C should only be called with new +``` + +They can also be declared as constructor functions, as described in the next section: + +## `@constructor` + +The compiler infers constructor functions based on this-property assignments, but you can make checking stricter and suggestions better if you add a `@constructor` tag: + +```js +/** + * @constructor + * @param {number} data + */ +function C(data) { + // property types can be inferred + this.name = "foo"; + + // or set explicitly + /** @type {string | null} */ + this.title = null; + + // or simply annotated, if they're set elsewhere + /** @type {number} */ + this.size; + + this.initialize(data); // Should error, initializer expects a string +} +/** + * @param {string} s + */ +C.prototype.initialize = function (s) { + this.size = s.length +} + +var c = new C(0); +var result = C(1); // C should only be called with new +``` + +With `@constructor`, `this` is checked inside the constructor function `C`, so you will get suggestions for the `initialize` method and an error if you pass it a number. You will also get an error if you call `C` instead of constructing it. + +Unfortunately, this means that constructor functions that are also callable cannot use `@constructor`. + +## `@this` + +The compiler can usually figure out the type of `this` when it has some context to work with. When it doesn't, you can explicitly specify the type of `this` with `@this`: + +```js +/** + * @this {HTMLElement} + * @param {*} e + */ +function callbackForLater(e) { + this.clientHeight = parseInt(e) // should be fine! +} +``` + +## `@extends` + +When Javascript classes extend a generic base class, there is nowhere to specify what the type parameter should be. The `@extends` tag provides a place for that type parameter: + +```js +/** + * @template T + * @extends {Set} + */ +class SortableSet extends Set { + // ... +} +``` + +Note that `@extends` only works with classes. Currently, there is no way for a constructor function extend a class. + + +## `@enum` + +The `@enum` tag allows you to create an object literal whose members are all of a specified type. Unlike most object literals in Javascript, it does not allow other members. + +```js +/** @enum {number} */ +const JSDocState = { + BeginningOfLine: 0, + SawAsterisk: 1, + SavingComments: 2, +} +``` + +Note that `@enum` is quite different from, and much simpler than, Typescript's `enum`. However, unlike Typescript's enums, `@enum` can have any type: + +```js +/** @enum {function(number): number} */ +const Math = { + add1: n => n + 1, + id: n => -n, + sub1: n => n - 1, +} +``` + +## More examples + +```js +var someObj = { + /** + * @param {string} param1 - Docs on property assignments work + */ + x: function(param1){} +}; + +/** + * As do docs on variable assignments + * @return {Window} + */ +let someFunc = function(){}; + +/** + * And class methods + * @param {string} greeting The greeting to use + */ +Foo.prototype.sayHi = (greeting) => console.log("Hi!"); + +/** + * And arrow functions expressions + * @param {number} x - A multiplier + */ +let myArrow = x => x * x; + +/** + * Which means it works for stateless function components in JSX too + * @param {{a: string, b: number}} test - Some param + */ +var sfc = (test) =>
{test.a.charAt(0)}
; + +/** + * A parameter can be a class constructor, using Closure syntax. + * + * @param {{new(...args: any[]): object}} C - The class to register + */ +function registerClass(C) {} + +/** + * @param {...string} p1 - A 'rest' arg (array) of strings. (treated as 'any') + */ +function fn10(p1){} + +/** + * @param {...string} p1 - A 'rest' arg (array) of strings. (treated as 'any') + */ +function fn9(p1) { + return p1.join(); +} +``` + +## Patterns that are known NOT to be supported + +Referring to objects in the value space as types doesn't work unless the object also creates a type, like a constructor function. + +```js +function aNormalFunction() { + +} +/** + * @type {aNormalFunction} + */ +var wrong; +/** + * Use 'typeof' instead: + * @type {typeof aNormalFunction} + */ +var right; +``` + +Postfix equals on a property type in an object literal type doesn't specify an optional property: + +```js +/** + * @type {{ a: string, b: number= }} + */ +var wrong; +/** + * Use postfix question on the property name instead: + * @type {{ a: string, b?: number }} + */ +var right; +``` + +Nullable types only have meaning if `strictNullChecks` is on: + +```js +/** + * @type {?number} + * With strictNullChecks: true -- number | null + * With strictNullChecks: false -- number + */ +var nullable; +``` + +You can also use a union type: +```js +/** + * @type {number | null} + * With strictNullChecks: true -- number | null + * With strictNullChecks: false -- number + */ +var unionNullable; +``` + +Non-nullable types have no meaning and are treated just as their original type: + +```js +/** + * @type {!number} + * Just has type number + */ +var normal; +``` + +Unlike JSDoc's type system, Typescript only allows you to mark types as containing null or not. +There is no explicit non-nullability -- if strictNullChecks is on, then `number` is not nullable. +If it is off, then `number` is nullable. + +### Unsupported tags + +TypeScript ignores any unsupported JSDoc tags. + +The following tags have open issues to support them: + +- `@const` ([issue #19672](https://github.com/Microsoft/TypeScript/issues/19672)) +- `@inheritdoc` ([issue #23215](https://github.com/Microsoft/TypeScript/issues/23215)) +- `@memberof` ([issue #7237](https://github.com/Microsoft/TypeScript/issues/7237)) +- `@readonly` ([issue #17233](https://github.com/Microsoft/TypeScript/issues/17233)) +- `@yields` ([issue #23857](https://github.com/Microsoft/TypeScript/issues/23857)) +- `{@link …}` ([issue #16498](https://github.com/Microsoft/TypeScript/issues/16498)) diff --git a/pages/JSX.md b/pages/JSX.md index 07166546e..1d12e0ead 100644 --- a/pages/JSX.md +++ b/pages/JSX.md @@ -1,33 +1,62 @@ +# Table of contents + +[Introduction](#introduction) + +[Basic Usage](#basic-usage) + +[The as operator](#the-as-operator) + +[Type Checking](#type-checking) +* [Intrinsic elements](#intrinsic-elements) +* [Value-based elements](#value-based-elements) +* [Function Component](#function-component) +* [Class Component](#class-component) +* [Attribute type checking](#attribute-type-checking) +* [Children Type Checking](#children-type-checking) + +[The JSX result type](#the-jsx-result-type) + +[Embedding Expressions](#embedding-expressions) + +[React integration](#react-integration) + +[Factory Functions](#factory-functions) + # Introduction +
↥ back to top [JSX](https://facebook.github.io/jsx/) is an embeddable XML-like syntax. It is meant to be transformed into valid JavaScript, though the semantics of that transformation are implementation-specific. -JSX came to popularity with the [React](http://facebook.github.io/react/) framework, but has since seen other applications as well. -TypeScript supports embedding, type checking, and compiling JSX directly into JavaScript. +JSX rose to popularity with the [React](https://reactjs.org/) framework, but has since seen other implementations as well. +TypeScript supports embedding, type checking, and compiling JSX directly to JavaScript. # Basic usage +↥ back to top In order to use JSX you must do two things. 1. Name your files with a `.tsx` extension 2. Enable the `jsx` option -TypeScript ships with two JSX modes: `preserve` and `react`. +TypeScript ships with three JSX modes: `preserve`, `react`, and `react-native`. These modes only affect the emit stage - type checking is unaffected. The `preserve` mode will keep the JSX as part of the output to be further consumed by another transform step (e.g. [Babel](https://babeljs.io/)). Additionally the output will have a `.jsx` file extension. The `react` mode will emit `React.createElement`, does not need to go through a JSX transformation before use, and the output will have a `.js` file extension. +The `react-native` mode is the equivalent of `preserve` in that it keeps all JSX, but the output will instead have a `.js` file extension. -Mode | Input | Output | Output File Extension ------------|-----------|------------------------------|---------------------- -`preserve` | `
` | `
` | `.jsx` -`react` | `
` | `React.createElement("div")` | `.js` +Mode | Input | Output | Output File Extension +---------------|-----------|------------------------------|---------------------- +`preserve` | `
` | `
` | `.jsx` +`react` | `
` | `React.createElement("div")` | `.js` +`react-native` | `
` | `
` | `.js` You can specify this mode using either the `--jsx` command line flag or the corresponding option in your [tsconfig.json](./tsconfig.json.md) file. -> *Note: The identifier `React` is hard-coded, so you must make React available with an uppercase R.* +> *Note: You can specify the JSX factory function to use when targeting react JSX emit with `--jsxFactory` option (defaults to `React.createElement`) # The `as` operator +↥ back to top Recall how to write a type assertion: @@ -35,19 +64,20 @@ Recall how to write a type assertion: var foo = bar; ``` -Here we are asserting the variable `bar` to have the type `foo`. -Since TypeScript also uses angle brackets for type assertions, JSX's syntax introduces certain parsing difficulties. As a result, TypeScript disallows angle bracket type assertions in `.tsx` files. +This asserts the variable `bar` to have the type `foo`. +Since TypeScript also uses angle brackets for type assertions, combining it with JSX's syntax would introduce certain parsing difficulties. As a result, TypeScript disallows angle bracket type assertions in `.tsx` files. -To make up for this loss of functionality in `.tsx` files, a new type assertion operator has been added: `as`. -The above example can easily be rewritten with the `as` operator. +Since the above syntax cannot be used in `.tsx` files, an alternate type assertion operator should be used: `as`. +The example can easily be rewritten with the `as` operator. ```ts var foo = bar as foo; ``` -The `as` operator is available in both `.ts` and `.tsx` files, and is identical in behavior to the other type assertion style. +The `as` operator is available in both `.ts` and `.tsx` files, and is identical in behavior to the angle-bracket type assertion style. # Type Checking +↥ back to top In order to understand type checking with JSX, you must first understand the difference between intrinsic elements and value-based elements. Given a JSX expression ``, `expr` may either refer to something intrinsic to the environment (e.g. a `div` or `span` in a DOM environment) or to a custom component that you've created. @@ -61,10 +91,11 @@ TypeScript uses the [same convention that React does](http://facebook.github.io/ An intrinsic element always begins with a lowercase letter, and a value-based element always begins with an uppercase letter. ## Intrinsic elements +↥ back to top Intrinsic elements are looked up on the special interface `JSX.IntrinsicElements`. By default, if this interface is not specified, then anything goes and intrinsic elements will not be type checked. -However, if interface *is* present, then the name of the intrinsic element is looked up as a property on the `JSX.IntrinsicElements` interface. +However, if this interface *is* present, then the name of the intrinsic element is looked up as a property on the `JSX.IntrinsicElements` interface. For example: ```ts @@ -81,17 +112,19 @@ declare namespace JSX { In the above example, `` will work fine but `` will result in an error since it has not been specified on `JSX.IntrinsicElements`. > Note: You can also specify a catch-all string indexer on `JSX.IntrinsicElements` as follows: ->```ts ->declare namespace JSX { -> interface IntrinsicElements { -> [elemName: string]: any; -> } ->} ->``` + +```ts +declare namespace JSX { + interface IntrinsicElements { + [elemName: string]: any; + } +} +``` ## Value-based elements +↥ back to top -Value based elements are simply looked up by identifiers that are in scope. +Value-based elements are simply looked up by identifiers that are in scope. ```ts import MyComponent from "./myComponent"; @@ -100,14 +133,68 @@ import MyComponent from "./myComponent"; ; // error ``` -It is possible to limit the type of a value-based element. -However, for this we must introduce two new terms: the *element class type* and the *element instance type*. +There are two ways to define a value-based element: + +1. Function Component (FC) +2. Class Component + +Because these two types of value-based elements are indistinguishable from each other in a JSX expression, first TS tries to resolve the expression as a Function Component using overload resolution. If the process succeeds, then TS finishes resolving the expression to its declaration. If the value fails to resolve as a Function Component, TS will then try to resolve it as a class component. If that fails, TS will report an error. + +### Function Component +↥ back to top + +As the name suggests, the component is defined as a JavaScript function where its first argument is a `props` object. +TS enforces that its return type must be assignable to `JSX.Element`. + +```ts +interface FooProp { + name: string; + X: number; + Y: number; +} + +declare function AnotherComponent(prop: {name: string}); +function ComponentFoo(prop: FooProp) { + return ; +} + +const Button = (prop: {value: string}, context: { color: string }) => + +
+
+ ); +} +``` + +In general, it'd be a good idea to write a few tests for `onIncrement` and `onDecrement` being triggered when their respective buttons are clicked. +Give it a shot to get the hang of writing tests for your components. + +Now that our component is updated, we're ready to wrap it into a container. +Let's create a file named `src/containers/Hello.tsx` and start off with the following imports. + +```ts +import Hello from '../components/Hello'; +import * as actions from '../actions/'; +import { StoreState } from '../types/index'; +import { connect, Dispatch } from 'react-redux'; +``` + +The real two key pieces here are the original `Hello` component as well as the `connect` function from react-redux. +`connect` will be able to actually take our original `Hello` component and turn it into a container using two functions: + +* `mapStateToProps` which massages the data from the current store to part of the shape that our component needs. +* `mapDispatchToProps` which uses creates callback props to pump actions to our store using a given `dispatch` function. + +If we recall, our application state consists of two properties: `languageName` and `enthusiasmLevel`. +Our `Hello` component, on the other hand, expected a `name` and an `enthusiasmLevel`. +`mapStateToProps` will get the relevant data from the store, and adjust it if necessary, for our component's props. +Let's go ahead and write that. + +```ts +export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) { + return { + enthusiasmLevel, + name: languageName, + } +} +``` + +Note that `mapStateToProps` only creates 2 out of 4 of the properties a `Hello` component expects. +Namely, we still want to pass in the `onIncrement` and `onDecrement` callbacks. +`mapDispatchToProps` is a function that takes a dispatcher function. +This dispatcher function can pass actions into our store to make updates, so we can create a pair of callbacks that will call the dispatcher as necessary. + +```ts +export function mapDispatchToProps(dispatch: Dispatch) { + return { + onIncrement: () => dispatch(actions.incrementEnthusiasm()), + onDecrement: () => dispatch(actions.decrementEnthusiasm()), + } +} +``` + +Finally, we're ready to call `connect`. +`connect` will first take `mapStateToProps` and `mapDispatchToProps`, and then return another function that we can use to wrap our component. +Our resulting container is defined with the following line of code: + +```ts +export default connect(mapStateToProps, mapDispatchToProps)(Hello); +``` + +When we're finished, our file should look like this: + +```ts +// src/containers/Hello.tsx + +import Hello from '../components/Hello'; +import * as actions from '../actions/'; +import { StoreState } from '../types/index'; +import { connect, Dispatch } from 'react-redux'; + +export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) { + return { + enthusiasmLevel, + name: languageName, + } +} + +export function mapDispatchToProps(dispatch: Dispatch) { + return { + onIncrement: () => dispatch(actions.incrementEnthusiasm()), + onDecrement: () => dispatch(actions.decrementEnthusiasm()), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(Hello); +``` + +## Creating a store + +Let's go back to `src/index.tsx`. +To put this all together, we need to create a store with an initial state, and set it up with all of our reducers. + +```ts +import { createStore } from 'redux'; +import { enthusiasm } from './reducers/index'; +import { StoreState } from './types/index'; + +const store = createStore(enthusiasm, { + enthusiasmLevel: 1, + languageName: 'TypeScript', +}); +``` + +`store` is, as you might've guessed, our central store for our application's global state. + +Next, we're going to swap our use of `./src/components/Hello` with `./src/containers/Hello` and use react-redux's `Provider` to wire up our props with our container. +We'll import each: + +```ts +import Hello from './containers/Hello'; +import { Provider } from 'react-redux'; +``` + +and pass our `store` through to the `Provider`'s attributes: + +```ts +ReactDOM.render( + + + , + document.getElementById('root') as HTMLElement +); +``` + +Notice that `Hello` no longer needs props, since we used our `connect` function to adapt our application's state for our wrapped `Hello` component's props. + +# Ejecting + +If at any point, you feel like there are certain customizations that the create-react-app setup has made difficult, you can always opt-out and get the various configuration options you need. +For example, if you'd like to add a Webpack plugin, it might be necessary to take advantage of the "eject" functionality that create-react-app provides. + +Simply run + +```sh +npm run eject +``` + +and you should be good to go! + +As a heads up, you may want to commit all your work before running an eject. +You cannot undo an eject command, so opting out is permanent unless you can recover from a commit prior to running an eject. + +# Next steps + +create-react-app comes with a lot of great stuff. +Much of it is documented in the default `README.md` that was generated for our project, so give that a quick read. + +If you still want to learn more about Redux, you can [check out the official website](http://redux.js.org/) for documentation. +The same goes [for MobX](https://mobx.js.org/). + +If you want to eject at some point, you may need to know a little bit more about Webpack. +You can check out our [React & Webpack walkthrough here](./React%20&%20Webpack.md). + +At some point you might need routing. +There are several solutions, but [react-router](https://github.com/ReactTraining/react-router) is probably the most popular for Redux projects, and is often used in conjunction with [react-router-redux](https://github.com/reactjs/react-router-redux). diff --git a/pages/tutorials/TypeScript in 5 minutes.md b/pages/tutorials/TypeScript in 5 minutes.md new file mode 100644 index 000000000..7e4d5df25 --- /dev/null +++ b/pages/tutorials/TypeScript in 5 minutes.md @@ -0,0 +1,171 @@ +Let's get started by building a simple web application with TypeScript. + +## Installing TypeScript + +There are two main ways to get the TypeScript tools: + +* Via npm (the Node.js package manager) +* By installing TypeScript's Visual Studio plugins + +Visual Studio 2017 and Visual Studio 2015 Update 3 include TypeScript by default. +If you didn't install TypeScript with Visual Studio, you can still [download it](/#download-links). + +For NPM users: + +```shell +> npm install -g typescript +``` + +## Building your first TypeScript file + +In your editor, type the following JavaScript code in `greeter.ts`: + +```ts +function greeter(person) { + return "Hello, " + person; +} + +let user = "Jane User"; + +document.body.textContent = greeter(user); +``` + +## Compiling your code + +We used a `.ts` extension, but this code is just JavaScript. +You could have copy/pasted this straight out of an existing JavaScript app. + +At the command line, run the TypeScript compiler: + +```shell +tsc greeter.ts +``` + +The result will be a file `greeter.js` which contains the same JavaScript that you fed in. +We're up and running using TypeScript in our JavaScript app! + +Now we can start taking advantage of some of the new tools TypeScript offers. +Add a `: string` type annotation to the 'person' function argument as shown here: + +```ts +function greeter(person: string) { + return "Hello, " + person; +} + +let user = "Jane User"; + +document.body.textContent = greeter(user); +``` + +## Type annotations + +Type annotations in TypeScript are lightweight ways to record the intended contract of the function or variable. +In this case, we intend the greeter function to be called with a single string parameter. +We can try changing the call greeter to pass an array instead: + +```ts +function greeter(person: string) { + return "Hello, " + person; +} + +let user = [0, 1, 2]; + +document.body.textContent = greeter(user); +``` + +Re-compiling, you'll now see an error: + +```shell +error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'. +``` + +Similarly, try removing all the arguments to the greeter call. +TypeScript will let you know that you have called this function with an unexpected number of parameters. +In both cases, TypeScript can offer static analysis based on both the structure of your code, and the type annotations you provide. + +Notice that although there were errors, the `greeter.js` file is still created. +You can use TypeScript even if there are errors in your code. But in this case, TypeScript is warning that your code will likely not run as expected. + +## Interfaces + +Let's develop our sample further. Here we use an interface that describes objects that have a firstName and lastName field. +In TypeScript, two types are compatible if their internal structure is compatible. +This allows us to implement an interface just by having the shape the interface requires, without an explicit `implements` clause. + +```ts +interface Person { + firstName: string; + lastName: string; +} + +function greeter(person: Person) { + return "Hello, " + person.firstName + " " + person.lastName; +} + +let user = { firstName: "Jane", lastName: "User" }; + +document.body.textContent = greeter(user); +``` + +## Classes + +Finally, let's extend the example one last time with classes. +TypeScript supports new features in JavaScript, like support for class-based object-oriented programming. + +Here we're going to create a `Student` class with a constructor and a few public fields. +Notice that classes and interfaces play well together, letting the programmer decide on the right level of abstraction. + +Also of note, the use of `public` on arguments to the constructor is a shorthand that allows us to automatically create properties with that name. + +```ts +class Student { + fullName: string; + constructor(public firstName: string, public middleInitial: string, public lastName: string) { + this.fullName = firstName + " " + middleInitial + " " + lastName; + } +} + +interface Person { + firstName: string; + lastName: string; +} + +function greeter(person: Person) { + return "Hello, " + person.firstName + " " + person.lastName; +} + +let user = new Student("Jane", "M.", "User"); + +document.body.textContent = greeter(user); +``` + +Re-run `tsc greeter.ts` and you'll see the generated JavaScript is the same as the earlier code. +Classes in TypeScript are just a shorthand for the same prototype-based OO that is frequently used in JavaScript. + +## Running your TypeScript web app + +Now type the following in `greeter.html`: + +```html + + + TypeScript Greeter + + + + +``` + +Open `greeter.html` in the browser to run your first simple TypeScript web application! + +Optional: Open `greeter.ts` in Visual Studio, or copy the code into the TypeScript playground. +You can hover over identifiers to see their types. +Notice that in some cases these types are inferred automatically for you. +Re-type the last line, and see completion lists and parameter help based on the types of the DOM elements. +Put your cursor on the reference to the greeter function, and hit F12 to go to its definition. +Notice, too, that you can right-click on a symbol and use refactoring to rename it. + +The type information provided works together with the tools to work with JavaScript at application scale. +For more examples of what's possible in TypeScript, see the Samples section of the website. + +![Visual Studio picture](/assets/images/docs/greet_person.png) diff --git a/pages/tutorials/tsconfig.json.md b/pages/tutorials/tsconfig.json.md new file mode 100644 index 000000000..803659ff5 --- /dev/null +++ b/pages/tutorials/tsconfig.json.md @@ -0,0 +1,212 @@ +## Overview + +The presence of a `tsconfig.json` file in a directory indicates that the directory is the root of a TypeScript project. +The `tsconfig.json` file specifies the root files and the compiler options required to compile the project. +A project is compiled in one of the following ways: + +## Using tsconfig.json + +* By invoking `tsc` with no input files, in which case the compiler searches for the `tsconfig.json` file starting in the current directory and continuing up the parent directory chain. +* By invoking `tsc` with no input files and a `--project` (or just `-p`) command line option that specifies the path of a directory containing a `tsconfig.json` file, or a path to a valid `.json` file containing the configurations. + +When input files are specified on the command line, `tsconfig.json` files are ignored. + +If you're looking for more information about the compiler options in a tsconfig, check out the TSConfig Reference beta +available in [the v2 site](https://www.staging-typescript.org/tsconfig). + +## Examples + +Example `tsconfig.json` files: + +* Using the `"files"` property + + ```json + { + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": true + }, + "files": [ + "core.ts", + "sys.ts", + "types.ts", + "scanner.ts", + "parser.ts", + "utilities.ts", + "binder.ts", + "checker.ts", + "emitter.ts", + "program.ts", + "commandLineParser.ts", + "tsc.ts", + "diagnosticInformationMap.generated.ts" + ] + } + ``` + +* Using the `"include"` and `"exclude"` properties + + ```json + { + "compilerOptions": { + "module": "system", + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "outFile": "../../built/local/tsc.js", + "sourceMap": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "**/*.spec.ts" + ] + } + ``` + +## Details + +The `"compilerOptions"` property can be omitted, in which case the compiler's defaults are used. See our full list of supported [Compiler Options](./Compiler%20Options.md). + +The `"files"` property takes a list of relative or absolute file paths. +The `"include"` and `"exclude"` properties take a list of glob-like file patterns. +The supported glob wildcards are: + +* `*` matches zero or more characters (excluding directory separators) +* `?` matches any one character (excluding directory separators) +* `**/` recursively matches any subdirectory + +If a segment of a glob pattern includes only `*` or `.*`, then only files with supported extensions are included (e.g. `.ts`, `.tsx`, and `.d.ts` by default with `.js` and `.jsx` if `allowJs` is set to true). + +If the `"files"` and `"include"` are both left unspecified, the compiler defaults to including all TypeScript (`.ts`, `.d.ts` and `.tsx`) files in the containing directory and subdirectories except those excluded using the `"exclude"` property. JS files (`.js` and `.jsx`) are also included if `allowJs` is set to true. +If the `"files"` or `"include"` properties are specified, the compiler will instead include the union of the files included by those two properties. +Files in the directory specified using the `"outDir"` compiler option are excluded as long as `"exclude"` property is not specified. + +Files included using `"include"` can be filtered using the `"exclude"` property. +However, files included explicitly using the `"files"` property are always included regardless of `"exclude"`. +The `"exclude"` property defaults to excluding the `node_modules`, `bower_components`, `jspm_packages` and `` directories when not specified. + +Any files that are referenced by files included via the `"files"` or `"include"` properties are also included. +Similarly, if a file `B.ts` is referenced by another file `A.ts`, then `B.ts` cannot be excluded unless the referencing file `A.ts` is also specified in the `"exclude"` list. + +Please note that the compiler does not include files that can be possible outputs; e.g. if the input includes `index.ts`, then `index.d.ts` and `index.js` are excluded. +In general, having files that differ only in extension next to each other is not recommended. + +A `tsconfig.json` file is permitted to be completely empty, which compiles all files included by default (as described above) with the default compiler options. + +Compiler options specified on the command line override those specified in the `tsconfig.json` file. + +## `@types`, `typeRoots` and `types` + +By default all *visible* "`@types`" packages are included in your compilation. +Packages in `node_modules/@types` of any enclosing folder are considered *visible*; +specifically, that means packages within `./node_modules/@types/`, `../node_modules/@types/`, `../../node_modules/@types/`, and so on. + +If `typeRoots` is specified, *only* packages under `typeRoots` will be included. +For example: + +```json +{ + "compilerOptions": { + "typeRoots" : ["./typings"] + } +} +``` + +This config file will include *all* packages under `./typings`, and no packages from `./node_modules/@types`. + +If `types` is specified, only packages listed will be included. +For instance: + +```json +{ + "compilerOptions": { + "types" : ["node", "lodash", "express"] + } +} +``` + +This `tsconfig.json` file will *only* include `./node_modules/@types/node`, `./node_modules/@types/lodash` and `./node_modules/@types/express`. +Other packages under `node_modules/@types/*` will not be included. + +A types package is a folder with a file called `index.d.ts` or a folder with a `package.json` that has a `types` field. + +Specify `"types": []` to disable automatic inclusion of `@types` packages. + +Keep in mind that automatic inclusion is only important if you're using files with global declarations (as opposed to files declared as modules). +If you use an `import "foo"` statement, for instance, TypeScript may still look through `node_modules` & `node_modules/@types` folders to find the `foo` package. + +## Configuration inheritance with `extends` + +A `tsconfig.json` file can inherit configurations from another file using the `extends` property. + +The `extends` is a top-level property in `tsconfig.json` (alongside `compilerOptions`, `files`, `include`, and `exclude`). +`extends`' value is a string containing a path to another configuration file to inherit from. +The path may use Node.js style resolution. + +The configuration from the base file are loaded first, then overridden by those in the inheriting config file. +If a circularity is encountered, we report an error. + +`files`, `include` and `exclude` from the inheriting config file *overwrite* those from the base config file. + +All relative paths found in the configuration file will be resolved relative to the configuration file they originated in. + +For example: + +`configs/base.json`: + +```json +{ + "compilerOptions": { + "noImplicitAny": true, + "strictNullChecks": true + } +} +``` + +`tsconfig.json`: + +```json +{ + "extends": "./configs/base", + "files": [ + "main.ts", + "supplemental.ts" + ] +} +``` + +`tsconfig.nostrictnull.json`: + +```json +{ + "extends": "./tsconfig", + "compilerOptions": { + "strictNullChecks": false + } +} +``` + +## `compileOnSave` + +Setting a top-level property `compileOnSave` signals to the IDE to generate all files for a given tsconfig.json upon saving. + +```json +{ + "compileOnSave": true, + "compilerOptions": { + "noImplicitAny" : true + } +} +``` + +This feature is currently supported in Visual Studio 2015 with TypeScript 1.8.4 and above, and [atom-typescript](https://github.com/TypeStrong/atom-typescript#compile-on-save) plugin. + +## Schema + +Schema can be found at: [http://json.schemastore.org/tsconfig](http://json.schemastore.org/tsconfig)