diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d669fac..e16c1600 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: RUST_BACKTRACE: full steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: dsherret/rust-toolchain-file@v1 - name: Install wasm32 target if: matrix.config.kind == 'test_release' diff --git a/Cargo.lock b/Cargo.lock index d86eaed2..8d761dc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,26 +12,11 @@ dependencies = [ "regex", ] -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -41,24 +26,24 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "ast_node" @@ -69,29 +54,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "better_scoped_tls" @@ -104,27 +74,30 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.4.2" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" dependencies = [ "allocator-api2", ] [[package]] name = "cc" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" [[package]] name = "cfg-if" @@ -146,27 +119,31 @@ dependencies = [ ] [[package]] -name = "data-url" -version = "0.3.1" +name = "crossbeam-channel" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "debug-here" -version = "0.2.2" +name = "crossbeam-utils" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7791f83f55f330aa6406d71cc29d6d785ea1e2cf326001d7b2b45a5065efb1" -dependencies = [ - "lazy_static", - "which", - "winapi", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deno_ast" -version = "0.36.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c06f7b0771250e5531bb9f86975b3ec15130d9fa77222674c5be290317221f" +checksum = "da1e05031ec63082cd9eaf103bee347120bf58cf8de5c880ab4c732b661c335f" dependencies = [ "deno_media_type", "deno_terminal", @@ -180,15 +157,16 @@ dependencies = [ "swc_ecma_parser", "swc_eq_ignore_macros", "text_lines", + "thiserror", "unicode-width", "url", ] [[package]] name = "deno_media_type" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a798670c20308e5770cc0775de821424ff9e85665b602928509c8c70430b3ee0" +checksum = "edf9879493856d1622be70f396b0b0d3e519538dd6501b7c609ecbaa7e2194d2" dependencies = [ "data-url", "serde", @@ -238,12 +216,13 @@ dependencies = [ [[package]] name = "dprint-development" -version = "0.9.5" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2502cba4299d334fc11a8a3357eb459f378dc285d7fbbb365c595a0ce48c73" +checksum = "176d63dadef52a9f22befba316c8a441980c7fa74a3b51ae2880749db099c66a" dependencies = [ "anyhow", "console", + "file_test_runner", "serde_json", "similar", ] @@ -253,7 +232,6 @@ name = "dprint-plugin-typescript" version = "0.90.1" dependencies = [ "anyhow", - "debug-here", "deno_ast", "dprint-core", "dprint-core-macros", @@ -284,9 +262,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -301,12 +279,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "failure" -version = "0.1.8" +name = "file_test_runner" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +checksum = "d1113d2a60090d3e004084aa4e2fd3344d66070afa3473164c869c55f7e2b5a4" dependencies = [ - "backtrace", + "crossbeam-channel", + "deno_terminal", + "parking_lot", + "regex", + "thiserror", ] [[package]] @@ -326,15 +308,9 @@ checksum = "3a0b11eeb173ce52f84ebd943d42e58813a2ebb78a6a3ff0a243b71c5199cd7b" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.48", + "syn 2.0.58", ] -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - [[package]] name = "hashbrown" version = "0.14.3" @@ -347,15 +323,16 @@ dependencies = [ [[package]] name = "hstr" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17fafeca18cf0927e23ea44d7a5189c10536279dfe9094e0dfa953053fbb5377" +checksum = "96274be293b8877e61974a607105d09c84caebe9620b47774aa8a6b942042dd4" dependencies = [ + "hashbrown", "new_debug_unreachable", "once_cell", "phf", "rustc-hash", - "smallvec", + "triomphe", ] [[package]] @@ -370,9 +347,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -388,14 +365,14 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "lazy_static" @@ -410,25 +387,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] -name = "memchr" -version = "2.7.1" +name = "lock_api" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] -name = "miniz_oxide" -version = "0.7.2" +name = "memchr" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", -] +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "num-bigint" @@ -444,37 +422,50 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] -name = "object" -version = "0.32.2" +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "memchr", + "lock_api", + "parking_lot_core", ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "parking_lot_core" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] [[package]] name = "percent-encoding" @@ -512,7 +503,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] @@ -526,9 +517,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pretty_assertions" @@ -542,9 +533,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -560,9 +551,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -582,11 +573,20 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -607,15 +607,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-hash" @@ -625,9 +619,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "scoped-tls" @@ -635,31 +629,37 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "indexmap", "itoa", @@ -669,9 +669,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "siphasher" @@ -681,9 +681,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smartstring" @@ -696,6 +696,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "stacker" version = "0.1.15" @@ -724,7 +730,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] @@ -770,7 +776,7 @@ version = "0.112.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36226eb87bfd2f5620bde04f149a4b869ab34e78496d60cb0d8eb9da765d0732" dependencies = [ - "bitflags", + "bitflags 2.5.0", "is-macro", "num-bigint", "phf", @@ -812,7 +818,7 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] @@ -823,14 +829,14 @@ checksum = "50176cfc1cbc8bb22f41c6fe9d1ec53fbe057001219b5954961b8ad0f336fce9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] name = "swc_visit" -version = "0.5.10" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5b3e8d1269a7cb95358fed3412645d9c15aa0eb1f4ca003a25a38ef2f30f1b" +checksum = "0263be55289abfe9c877ffef83d877b5bdfac036ffe2de793f48f5e47e41dbae" dependencies = [ "either", "swc_visit_macros", @@ -846,7 +852,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] @@ -862,9 +868,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -889,6 +895,26 @@ dependencies = [ "serde", ] +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -923,7 +949,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] @@ -935,6 +961,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +dependencies = [ + "serde", + "stable_deref_trait", +] + [[package]] name = "typed-arena" version = "2.0.2" @@ -961,9 +997,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -992,16 +1028,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "which" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" -dependencies = [ - "failure", - "libc", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1039,65 +1065,129 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", ] [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "yansi" @@ -1122,5 +1212,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] diff --git a/Cargo.toml b/Cargo.toml index c9a75aea..1b7fe0bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,11 @@ panic = "abort" wasm = ["serde_json", "dprint-core/wasm"] tracing = ["dprint-core/tracing"] +[[test]] +name = "specs" +path = "tests/spec_test.rs" +harness = false + [dependencies] anyhow = "1.0.64" deno_ast = { version = "0.36.0", features = ["view"] } @@ -35,7 +40,6 @@ serde = { version = "1.0.144", features = ["derive"] } serde_json = { version = "1.0", optional = true } [dev-dependencies] -debug-here = "0.2" -dprint-development = "0.9.5" +dprint-development = "0.10.0" pretty_assertions = "1.3.0" serde_json = { version = "1.0" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4739bf10..86c03712 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.75.0" +channel = "1.77.2" components = ["clippy"] diff --git a/src/generation/generate.rs b/src/generation/generate.rs index e00c0e2e..db875666 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -4126,7 +4126,7 @@ mod string_literal { } fn format_with_double(string_value: &str) -> PrintItems { - const DOUBLE_QUOTE_SC: &'static StringContainer = sc!("\""); + const DOUBLE_QUOTE_SC: &StringContainer = sc!("\""); let mut items = PrintItems::new(); items.push_sc(DOUBLE_QUOTE_SC); items.extend(gen_from_raw_string(&string_value.replace('"', "\\\""))); @@ -4135,7 +4135,7 @@ mod string_literal { } fn format_with_single(string_value: &str) -> PrintItems { - const SINGLE_QUOTE_SC: &'static StringContainer = sc!("'"); + const SINGLE_QUOTE_SC: &StringContainer = sc!("'"); let mut items = PrintItems::new(); items.push_sc(SINGLE_QUOTE_SC); items.extend(gen_from_raw_string(&string_value.replace('\'', "\\'"))); diff --git a/tests/performance/checker.txt b/tests/performance/checker.txt deleted file mode 100644 index 0ddca7e7..00000000 --- a/tests/performance/checker.txt +++ /dev/null @@ -1,36903 +0,0 @@ -/* @internal */ -namespace ts { - const ambientModuleSymbolRegex = /^".+"$/; - const anon = "(anonymous)" as __String & string; - - let nextSymbolId = 1; - let nextNodeId = 1; - let nextMergeId = 1; - let nextFlowId = 1; - - const enum IterationUse { - AllowsSyncIterablesFlag = 1 << 0, - AllowsAsyncIterablesFlag = 1 << 1, - AllowsStringInputFlag = 1 << 2, - ForOfFlag = 1 << 3, - YieldStarFlag = 1 << 4, - SpreadFlag = 1 << 5, - DestructuringFlag = 1 << 6, - - // Spread, Destructuring, Array element assignment - Element = AllowsSyncIterablesFlag, - Spread = AllowsSyncIterablesFlag | SpreadFlag, - Destructuring = AllowsSyncIterablesFlag | DestructuringFlag, - - ForOf = AllowsSyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, - ForAwaitOf = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, - - YieldStar = AllowsSyncIterablesFlag | YieldStarFlag, - AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | YieldStarFlag, - - GeneratorReturnType = AllowsSyncIterablesFlag, - AsyncGeneratorReturnType = AllowsAsyncIterablesFlag, - - } - - const enum IterationTypeKind { - Yield, - Return, - Next, - } - - interface IterationTypesResolver { - iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable"; - iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator"; - iteratorSymbolName: "asyncIterator" | "iterator"; - getGlobalIteratorType: (reportErrors: boolean) => GenericType; - getGlobalIterableType: (reportErrors: boolean) => GenericType; - getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType; - getGlobalGeneratorType: (reportErrors: boolean) => GenericType; - resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined; - mustHaveANextMethodDiagnostic: DiagnosticMessage; - mustBeAMethodDiagnostic: DiagnosticMessage; - mustHaveAValueDiagnostic: DiagnosticMessage; - } - - const enum WideningKind { - Normal, - GeneratorYield - } - - const enum TypeFacts { - None = 0, - TypeofEQString = 1 << 0, // typeof x === "string" - TypeofEQNumber = 1 << 1, // typeof x === "number" - TypeofEQBigInt = 1 << 2, // typeof x === "bigint" - TypeofEQBoolean = 1 << 3, // typeof x === "boolean" - TypeofEQSymbol = 1 << 4, // typeof x === "symbol" - TypeofEQObject = 1 << 5, // typeof x === "object" - TypeofEQFunction = 1 << 6, // typeof x === "function" - TypeofEQHostObject = 1 << 7, // typeof x === "xxx" - TypeofNEString = 1 << 8, // typeof x !== "string" - TypeofNENumber = 1 << 9, // typeof x !== "number" - TypeofNEBigInt = 1 << 10, // typeof x !== "bigint" - TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" - TypeofNESymbol = 1 << 12, // typeof x !== "symbol" - TypeofNEObject = 1 << 13, // typeof x !== "object" - TypeofNEFunction = 1 << 14, // typeof x !== "function" - TypeofNEHostObject = 1 << 15, // typeof x !== "xxx" - EQUndefined = 1 << 16, // x === undefined - EQNull = 1 << 17, // x === null - EQUndefinedOrNull = 1 << 18, // x === undefined / x === null - NEUndefined = 1 << 19, // x !== undefined - NENull = 1 << 20, // x !== null - NEUndefinedOrNull = 1 << 21, // x != undefined / x != null - Truthy = 1 << 22, // x - Falsy = 1 << 23, // !x - All = (1 << 24) - 1, - // The following members encode facts about particular kinds of types for use in the getTypeFacts function. - // The presence of a particular fact means that the given test is true for some (and possibly all) values - // of that kind of type. - BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy, - StringFacts = BaseStringFacts | Truthy, - EmptyStringStrictFacts = BaseStringStrictFacts | Falsy, - EmptyStringFacts = BaseStringFacts, - NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy, - NonEmptyStringFacts = BaseStringFacts | Truthy, - BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy, - NumberFacts = BaseNumberFacts | Truthy, - ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy, - ZeroNumberFacts = BaseNumberFacts, - NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy, - NonZeroNumberFacts = BaseNumberFacts | Truthy, - BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy, - BigIntFacts = BaseBigIntFacts | Truthy, - ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy, - ZeroBigIntFacts = BaseBigIntFacts, - NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy, - NonZeroBigIntFacts = BaseBigIntFacts | Truthy, - BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy, - BooleanFacts = BaseBooleanFacts | Truthy, - FalseStrictFacts = BaseBooleanStrictFacts | Falsy, - FalseFacts = BaseBooleanFacts, - TrueStrictFacts = BaseBooleanStrictFacts | Truthy, - TrueFacts = BaseBooleanFacts | Truthy, - SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, - SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, - ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, - FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, - NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, - EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull), - EmptyObjectFacts = All, - } - - const typeofEQFacts: ReadonlyMap = createMapFromTemplate({ - string: TypeFacts.TypeofEQString, - number: TypeFacts.TypeofEQNumber, - bigint: TypeFacts.TypeofEQBigInt, - boolean: TypeFacts.TypeofEQBoolean, - symbol: TypeFacts.TypeofEQSymbol, - undefined: TypeFacts.EQUndefined, - object: TypeFacts.TypeofEQObject, - function: TypeFacts.TypeofEQFunction - }); - - const typeofNEFacts: ReadonlyMap = createMapFromTemplate({ - string: TypeFacts.TypeofNEString, - number: TypeFacts.TypeofNENumber, - bigint: TypeFacts.TypeofNEBigInt, - boolean: TypeFacts.TypeofNEBoolean, - symbol: TypeFacts.TypeofNESymbol, - undefined: TypeFacts.NEUndefined, - object: TypeFacts.TypeofNEObject, - function: TypeFacts.TypeofNEFunction - }); - - type TypeSystemEntity = Node | Symbol | Type | Signature; - - const enum TypeSystemPropertyName { - Type, - ResolvedBaseConstructorType, - DeclaredType, - ResolvedReturnType, - ImmediateBaseConstraint, - EnumTagType, - JSDocTypeReference, - ResolvedTypeArguments, - } - - const enum CheckMode { - Normal = 0, // Normal type checking - Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable - Inferential = 1 << 1, // Inferential typing - SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions - SkipGenericFunctions = 1 << 3, // Skip single signature generic functions - IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help - } - - const enum AccessFlags { - None = 0, - NoIndexSignatures = 1 << 0, - Writing = 1 << 1, - CacheSymbol = 1 << 2, - NoTupleBoundsCheck = 1 << 3, - } - - const enum SignatureCheckMode { - BivariantCallback = 1 << 0, - StrictCallback = 1 << 1, - IgnoreReturnTypes = 1 << 2, - StrictArity = 1 << 3, - Callback = BivariantCallback | StrictCallback, - } - - const enum MappedTypeModifiers { - IncludeReadonly = 1 << 0, - ExcludeReadonly = 1 << 1, - IncludeOptional = 1 << 2, - ExcludeOptional = 1 << 3, - } - - const enum ExpandingFlags { - None = 0, - Source = 1, - Target = 1 << 1, - Both = Source | Target, - } - - const enum MembersOrExportsResolutionKind { - resolvedExports = "resolvedExports", - resolvedMembers = "resolvedMembers" - } - - const enum UnusedKind { - Local, - Parameter, - } - - /** @param containingNode Node to check for parse error */ - type AddUnusedDiagnostic = (containingNode: Node, type: UnusedKind, diagnostic: DiagnosticWithLocation) => void; - - const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor); - - const enum DeclarationMeaning { - GetAccessor = 1, - SetAccessor = 2, - PropertyAssignment = 4, - Method = 8, - GetOrSetAccessor = GetAccessor | SetAccessor, - PropertyAssignmentOrMethod = PropertyAssignment | Method, - } - - const enum DeclarationSpaces { - None = 0, - ExportValue = 1 << 0, - ExportType = 1 << 1, - ExportNamespace = 1 << 2, - } - - export function getNodeId(node: Node): number { - if (!node.id) { - node.id = nextNodeId; - nextNodeId++; - } - return node.id; - } - - export function getSymbolId(symbol: Symbol): number { - if (!symbol.id) { - symbol.id = nextSymbolId; - nextSymbolId++; - } - - return symbol.id; - } - - export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { - const moduleState = getModuleInstanceState(node); - return moduleState === ModuleInstanceState.Instantiated || - (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly); - } - - export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { - const getPackagesSet: () => Map = memoize(() => { - const set = createMap(); - host.getSourceFiles().forEach(sf => { - if (!sf.resolvedModules) return; - - forEachEntry(sf.resolvedModules, r => { - if (r && r.packageId) set.set(r.packageId.name, true); - }); - }); - return set; - }); - - // Cancellation that controls whether or not we can cancel in the middle of type checking. - // In general cancelling is *not* safe for the type checker. We might be in the middle of - // computing something, and we will leave our internals in an inconsistent state. Callers - // who set the cancellation token should catch if a cancellation exception occurs, and - // should throw away and create a new TypeChecker. - // - // Currently we only support setting the cancellation token when getting diagnostics. This - // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if - // they no longer need the information (for example, if the user started editing again). - let cancellationToken: CancellationToken | undefined; - let requestedExternalEmitHelpers: ExternalEmitHelpers; - let externalHelpersModule: Symbol; - - const Symbol = objectAllocator.getSymbolConstructor(); - const Type = objectAllocator.getTypeConstructor(); - const Signature = objectAllocator.getSignatureConstructor(); - - let typeCount = 0; - let symbolCount = 0; - let enumCount = 0; - let instantiationCount = 0; - let instantiationDepth = 0; - let constraintDepth = 0; - let currentNode: Node | undefined; - - const emptySymbols = createSymbolTable(); - const identityMapper: (type: Type) => Type = identity; - - const compilerOptions = host.getCompilerOptions(); - const languageVersion = getEmitScriptTarget(compilerOptions); - const moduleKind = getEmitModuleKind(compilerOptions); - const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions); - const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks"); - const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes"); - const strictBindCallApply = getStrictOptionValue(compilerOptions, "strictBindCallApply"); - const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization"); - const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny"); - const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis"); - const keyofStringsOnly = !!compilerOptions.keyofStringsOnly; - const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral; - - const emitResolver = createResolver(); - const nodeBuilder = createNodeBuilder(); - - const globals = createSymbolTable(); - const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String); - undefinedSymbol.declarations = []; - - const globalThisSymbol = createSymbol(SymbolFlags.Module, "globalThis" as __String, CheckFlags.Readonly); - globalThisSymbol.exports = globals; - globalThisSymbol.declarations = []; - globals.set(globalThisSymbol.escapedName, globalThisSymbol); - - const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String); - const requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String); - - /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ - let apparentArgumentCount: number | undefined; - - // for public members that accept a Node or one of its subtypes, we must guard against - // synthetic nodes created during transformations by calling `getParseTreeNode`. - // for most of these, we perform the guard only on `checker` to avoid any possible - // extra cost of calling `getParseTreeNode` when calling these functions from inside the - // checker. - const checker: TypeChecker = { - getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), - getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), - getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount, - getTypeCount: () => typeCount, - getRelationCacheSizes: () => ({ - assignable: assignableRelation.size, - identity: identityRelation.size, - subtype: subtypeRelation.size, - strictSubtype: strictSubtypeRelation.size, - }), - isUndefinedSymbol: symbol => symbol === undefinedSymbol, - isArgumentsSymbol: symbol => symbol === argumentsSymbol, - isUnknownSymbol: symbol => symbol === unknownSymbol, - getMergedSymbol, - getDiagnostics, - getGlobalDiagnostics, - getTypeOfSymbolAtLocation: (symbol, location) => { - location = getParseTreeNode(location); - return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType; - }, - getSymbolsOfParameterPropertyDeclaration: (parameterIn, parameterName) => { - const parameter = getParseTreeNode(parameterIn, isParameter); - if (parameter === undefined) return Debug.fail("Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node."); - return getSymbolsOfParameterPropertyDeclaration(parameter, escapeLeadingUnderscores(parameterName)); - }, - getDeclaredTypeOfSymbol, - getPropertiesOfType, - getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)), - getPrivateIdentifierPropertyOfType: (leftType: Type, name: string, location: Node) => { - const node = getParseTreeNode(location); - if (!node) { - return undefined; - } - const propName = escapeLeadingUnderscores(name); - const lexicallyScopedIdentifier = lookupSymbolForPrivateIdentifierDeclaration(propName, node); - return lexicallyScopedIdentifier ? getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedIdentifier) : undefined; - }, - getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType(type, escapeLeadingUnderscores(name)), - getIndexInfoOfType, - getSignaturesOfType, - getIndexTypeOfType, - getBaseTypes, - getBaseTypeOfLiteralType, - getWidenedType, - getTypeFromTypeNode: nodeIn => { - const node = getParseTreeNode(nodeIn, isTypeNode); - return node ? getTypeFromTypeNode(node) : errorType; - }, - getParameterType: getTypeAtPosition, - getPromisedTypeOfPromise, - getReturnTypeOfSignature, - isNullableType, - getNullableType, - getNonNullableType, - getNonOptionalType: removeOptionalTypeMarker, - getTypeArguments, - typeToTypeNode: nodeBuilder.typeToTypeNode, - indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, - signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration, - symbolToEntityName: nodeBuilder.symbolToEntityName, - symbolToExpression: nodeBuilder.symbolToExpression, - symbolToTypeParameterDeclarations: nodeBuilder.symbolToTypeParameterDeclarations, - symbolToParameterDeclaration: nodeBuilder.symbolToParameterDeclaration, - typeParameterToDeclaration: nodeBuilder.typeParameterToDeclaration, - getSymbolsInScope: (location, meaning) => { - location = getParseTreeNode(location); - return location ? getSymbolsInScope(location, meaning) : []; - }, - getSymbolAtLocation: node => { - node = getParseTreeNode(node); - return node ? getSymbolAtLocation(node) : undefined; - }, - getShorthandAssignmentValueSymbol: node => { - node = getParseTreeNode(node); - return node ? getShorthandAssignmentValueSymbol(node) : undefined; - }, - getExportSpecifierLocalTargetSymbol: nodeIn => { - const node = getParseTreeNode(nodeIn, isExportSpecifier); - return node ? getExportSpecifierLocalTargetSymbol(node) : undefined; - }, - getExportSymbolOfSymbol(symbol) { - return getMergedSymbol(symbol.exportSymbol || symbol); - }, - getTypeAtLocation: node => { - node = getParseTreeNode(node); - return node ? getTypeOfNode(node) : errorType; - }, - getTypeOfAssignmentPattern: nodeIn => { - const node = getParseTreeNode(nodeIn, isAssignmentPattern); - return node && getTypeOfAssignmentPattern(node) || errorType; - }, - getPropertySymbolOfDestructuringAssignment: locationIn => { - const location = getParseTreeNode(locationIn, isIdentifier); - return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined; - }, - signatureToString: (signature, enclosingDeclaration, flags, kind) => { - return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind); - }, - typeToString: (type, enclosingDeclaration, flags) => { - return typeToString(type, getParseTreeNode(enclosingDeclaration), flags); - }, - symbolToString: (symbol, enclosingDeclaration, meaning, flags) => { - return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags); - }, - typePredicateToString: (predicate, enclosingDeclaration, flags) => { - return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags); - }, - writeSignature: (signature, enclosingDeclaration, flags, kind, writer) => { - return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind, writer); - }, - writeType: (type, enclosingDeclaration, flags, writer) => { - return typeToString(type, getParseTreeNode(enclosingDeclaration), flags, writer); - }, - writeSymbol: (symbol, enclosingDeclaration, meaning, flags, writer) => { - return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags, writer); - }, - writeTypePredicate: (predicate, enclosingDeclaration, flags, writer) => { - return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags, writer); - }, - getAugmentedPropertiesOfType, - getRootSymbols, - getContextualType: (nodeIn: Expression, contextFlags?: ContextFlags) => { - const node = getParseTreeNode(nodeIn, isExpression); - return node ? getContextualType(node, contextFlags) : undefined; - }, - getContextualTypeForObjectLiteralElement: nodeIn => { - const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike); - return node ? getContextualTypeForObjectLiteralElement(node) : undefined; - }, - getContextualTypeForArgumentAtIndex: (nodeIn, argIndex) => { - const node = getParseTreeNode(nodeIn, isCallLikeExpression); - return node && getContextualTypeForArgumentAtIndex(node, argIndex); - }, - getContextualTypeForJsxAttribute: (nodeIn) => { - const node = getParseTreeNode(nodeIn, isJsxAttributeLike); - return node && getContextualTypeForJsxAttribute(node); - }, - isContextSensitive, - getFullyQualifiedName, - getResolvedSignature: (node, candidatesOutArray, argumentCount) => - getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal), - getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) => - getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp), - getExpandedParameters, - hasEffectiveRestParameter, - getConstantValue: nodeIn => { - const node = getParseTreeNode(nodeIn, canHaveConstantValue); - return node ? getConstantValue(node) : undefined; - }, - isValidPropertyAccess: (nodeIn, propertyName) => { - const node = getParseTreeNode(nodeIn, isPropertyAccessOrQualifiedNameOrImportTypeNode); - return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName)); - }, - isValidPropertyAccessForCompletions: (nodeIn, type, property) => { - const node = getParseTreeNode(nodeIn, isPropertyAccessExpression); - return !!node && isValidPropertyAccessForCompletions(node, type, property); - }, - getSignatureFromDeclaration: declarationIn => { - const declaration = getParseTreeNode(declarationIn, isFunctionLike); - return declaration ? getSignatureFromDeclaration(declaration) : undefined; - }, - isImplementationOfOverload: node => { - const parsed = getParseTreeNode(node, isFunctionLike); - return parsed ? isImplementationOfOverload(parsed) : undefined; - }, - getImmediateAliasedSymbol, - getAliasedSymbol: resolveAlias, - getEmitResolver, - getExportsOfModule: getExportsOfModuleAsArray, - getExportsAndPropertiesOfModule, - getSymbolWalker: createGetSymbolWalker( - getRestTypeOfSignature, - getTypePredicateOfSignature, - getReturnTypeOfSignature, - getBaseTypes, - resolveStructuredTypeMembers, - getTypeOfSymbol, - getResolvedSymbol, - getIndexTypeOfStructuredType, - getConstraintOfTypeParameter, - getFirstIdentifier, - getTypeArguments, - ), - getAmbientModules, - getJsxIntrinsicTagNamesAt, - isOptionalParameter: nodeIn => { - const node = getParseTreeNode(nodeIn, isParameter); - return node ? isOptionalParameter(node) : false; - }, - tryGetMemberInModuleExports: (name, symbol) => tryGetMemberInModuleExports(escapeLeadingUnderscores(name), symbol), - tryGetMemberInModuleExportsAndProperties: (name, symbol) => tryGetMemberInModuleExportsAndProperties(escapeLeadingUnderscores(name), symbol), - tryFindAmbientModuleWithoutAugmentations: moduleName => { - // we deliberately exclude augmentations - // since we are only interested in declarations of the module itself - return tryFindAmbientModule(moduleName, /*withAugmentations*/ false); - }, - getApparentType, - getUnionType, - isTypeAssignableTo: (source, target) => { - return isTypeAssignableTo(source, target); - }, - createAnonymousType, - createSignature, - createSymbol, - createIndexInfo, - getAnyType: () => anyType, - getStringType: () => stringType, - getNumberType: () => numberType, - createPromiseType, - createArrayType, - getElementTypeOfArrayType, - getBooleanType: () => booleanType, - getFalseType: (fresh?) => fresh ? falseType : regularFalseType, - getTrueType: (fresh?) => fresh ? trueType : regularTrueType, - getVoidType: () => voidType, - getUndefinedType: () => undefinedType, - getNullType: () => nullType, - getESSymbolType: () => esSymbolType, - getNeverType: () => neverType, - getOptionalType: () => optionalType, - isSymbolAccessible, - isArrayType, - isTupleType, - isArrayLikeType, - isTypeInvalidDueToUnionDiscriminant, - getAllPossiblePropertiesOfTypes, - getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type), - getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), - getSuggestionForNonexistentExport: (node, target) => getSuggestionForNonexistentExport(node, target), - getBaseConstraintOfType, - getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined, - resolveName(name, location, meaning, excludeGlobals) { - return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals); - }, - getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)), - getAccessibleSymbolChain, - getTypePredicateOfSignature, - resolveExternalModuleSymbol, - tryGetThisTypeAt: (node, includeGlobalThis) => { - node = getParseTreeNode(node); - return node && tryGetThisTypeAt(node, includeGlobalThis); - }, - getTypeArgumentConstraint: nodeIn => { - const node = getParseTreeNode(nodeIn, isTypeNode); - return node && getTypeArgumentConstraint(node); - }, - getSuggestionDiagnostics: (file, ct) => { - if (skipTypeChecking(file, compilerOptions, host)) { - return emptyArray; - } - - let diagnostics: DiagnosticWithLocation[] | undefined; - try { - // Record the cancellation token so it can be checked later on during checkSourceElement. - // Do this in a finally block so we can ensure that it gets reset back to nothing after - // this call is done. - cancellationToken = ct; - - // Ensure file is type checked - checkSourceFile(file); - Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked)); - - diagnostics = addRange(diagnostics, suggestionDiagnostics.getDiagnostics(file.fileName)); - checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (containingNode, kind, diag) => { - if (!containsParseError(containingNode) && !unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { - (diagnostics || (diagnostics = [])).push({ ...diag, category: DiagnosticCategory.Suggestion }); - } - }); - - return diagnostics || emptyArray; - } - finally { - cancellationToken = undefined; - } - }, - - runWithCancellationToken: (token, callback) => { - try { - cancellationToken = token; - return callback(checker); - } - finally { - cancellationToken = undefined; - } - }, - - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, - isDeclarationVisible, - }; - - function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined { - const node = getParseTreeNode(nodeIn, isCallLikeExpression); - apparentArgumentCount = argumentCount; - const res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined; - apparentArgumentCount = undefined; - return res; - } - - const tupleTypes = createMap(); - const unionTypes = createMap(); - const intersectionTypes = createMap(); - const literalTypes = createMap(); - const indexedAccessTypes = createMap(); - const substitutionTypes = createMap(); - const evolvingArrayTypes: EvolvingArrayType[] = []; - const undefinedProperties = createMap() as UnderscoreEscapedMap; - - const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String); - const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving); - - const anyType = createIntrinsicType(TypeFlags.Any, "any"); - const autoType = createIntrinsicType(TypeFlags.Any, "any"); - const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); - const errorType = createIntrinsicType(TypeFlags.Any, "error"); - const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType); - const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); - const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); - const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType); - const optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined"); - const nullType = createIntrinsicType(TypeFlags.Null, "null"); - const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType); - const stringType = createIntrinsicType(TypeFlags.String, "string"); - const numberType = createIntrinsicType(TypeFlags.Number, "number"); - const bigintType = createIntrinsicType(TypeFlags.BigInt, "bigint"); - const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType; - const regularFalseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType; - const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType; - const regularTrueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType; - trueType.regularType = regularTrueType; - trueType.freshType = trueType; - regularTrueType.regularType = regularTrueType; - regularTrueType.freshType = trueType; - falseType.regularType = regularFalseType; - falseType.freshType = falseType; - regularFalseType.regularType = regularFalseType; - regularFalseType.freshType = falseType; - const booleanType = createBooleanType([regularFalseType, regularTrueType]); - // Also mark all combinations of fresh/regular booleans as "Boolean" so they print as `boolean` instead of `true | false` - // (The union is cached, so simply doing the marking here is sufficient) - createBooleanType([regularFalseType, trueType]); - createBooleanType([falseType, regularTrueType]); - createBooleanType([falseType, trueType]); - const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); - const voidType = createIntrinsicType(TypeFlags.Void, "void"); - const neverType = createIntrinsicType(TypeFlags.Never, "never"); - const silentNeverType = createIntrinsicType(TypeFlags.Never, "never"); - const nonInferrableType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType); - const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never"); - const unreachableNeverType = createIntrinsicType(TypeFlags.Never, "never"); - const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object"); - const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]); - const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType; - const numberOrBigIntType = getUnionType([numberType, bigintType]); - - const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const emptyJsxObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - emptyJsxObjectType.objectFlags |= ObjectFlags.JsxAttributes; - - const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); - emptyTypeLiteralSymbol.members = createSymbolTable(); - const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, undefined, undefined); - - const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - emptyGenericType.instantiations = createMap(); - - const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated - // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes. - anyFunctionType.objectFlags |= ObjectFlags.NonInferrableType; - - const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - - const markerSuperType = createTypeParameter(); - const markerSubType = createTypeParameter(); - markerSubType.constraint = markerSuperType; - const markerOtherType = createTypeParameter(); - - const noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<>", 0, anyType); - - const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); - const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, errorType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); - const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); - const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); - - const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); - - const iterationTypesCache = createMap(); // cache for common IterationTypes instances - const noIterationTypes: IterationTypes = { - get yieldType(): Type { throw new Error("Not supported"); }, - get returnType(): Type { throw new Error("Not supported"); }, - get nextType(): Type { throw new Error("Not supported"); }, - }; - const anyIterationTypes = createIterationTypes(anyType, anyType, anyType); - const anyIterationTypesExceptNext = createIterationTypes(anyType, anyType, unknownType); - const defaultIterationTypes = createIterationTypes(neverType, anyType, undefinedType); // default iteration types for `Iterator`. - - const asyncIterationTypesResolver: IterationTypesResolver = { - iterableCacheKey: "iterationTypesOfAsyncIterable", - iteratorCacheKey: "iterationTypesOfAsyncIterator", - iteratorSymbolName: "asyncIterator", - getGlobalIteratorType: getGlobalAsyncIteratorType, - getGlobalIterableType: getGlobalAsyncIterableType, - getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType, - getGlobalGeneratorType: getGlobalAsyncGeneratorType, - resolveIterationType: getAwaitedType, - mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method, - mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_async_iterator_must_be_a_method, - mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property, - }; - - const syncIterationTypesResolver: IterationTypesResolver = { - iterableCacheKey: "iterationTypesOfIterable", - iteratorCacheKey: "iterationTypesOfIterator", - iteratorSymbolName: "iterator", - getGlobalIteratorType, - getGlobalIterableType, - getGlobalIterableIteratorType, - getGlobalGeneratorType, - resolveIterationType: (type, _errorNode) => type, - mustHaveANextMethodDiagnostic: Diagnostics.An_iterator_must_have_a_next_method, - mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_iterator_must_be_a_method, - mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property, - }; - - interface DuplicateInfoForSymbol { - readonly firstFileLocations: Node[]; - readonly secondFileLocations: Node[]; - readonly isBlockScoped: boolean; - } - interface DuplicateInfoForFiles { - readonly firstFile: SourceFile; - readonly secondFile: SourceFile; - /** Key is symbol name. */ - readonly conflictingSymbols: Map; - } - /** Key is "/path/to/a.ts|/path/to/b.ts". */ - let amalgamatedDuplicates: Map | undefined; - const reverseMappedCache = createMap(); - let ambientModulesCache: Symbol[] | undefined; - /** - * List of every ambient module with a "*" wildcard. - * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. - * This is only used if there is no exact match. - */ - let patternAmbientModules: PatternAmbientModule[]; - let patternAmbientModuleAugmentations: Map | undefined; - - let globalObjectType: ObjectType; - let globalFunctionType: ObjectType; - let globalCallableFunctionType: ObjectType; - let globalNewableFunctionType: ObjectType; - let globalArrayType: GenericType; - let globalReadonlyArrayType: GenericType; - let globalStringType: ObjectType; - let globalNumberType: ObjectType; - let globalBooleanType: ObjectType; - let globalRegExpType: ObjectType; - let globalThisType: GenericType; - let anyArrayType: Type; - let autoArrayType: Type; - let anyReadonlyArrayType: Type; - let deferredGlobalNonNullableTypeAlias: Symbol; - - // The library files are only loaded when the feature is used. - // This allows users to just specify library files they want to used through --lib - // and they will not get an error from not having unrelated library files - let deferredGlobalESSymbolConstructorSymbol: Symbol | undefined; - let deferredGlobalESSymbolType: ObjectType; - let deferredGlobalTypedPropertyDescriptorType: GenericType; - let deferredGlobalPromiseType: GenericType; - let deferredGlobalPromiseLikeType: GenericType; - let deferredGlobalPromiseConstructorSymbol: Symbol | undefined; - let deferredGlobalPromiseConstructorLikeType: ObjectType; - let deferredGlobalIterableType: GenericType; - let deferredGlobalIteratorType: GenericType; - let deferredGlobalIterableIteratorType: GenericType; - let deferredGlobalGeneratorType: GenericType; - let deferredGlobalIteratorYieldResultType: GenericType; - let deferredGlobalIteratorReturnResultType: GenericType; - let deferredGlobalAsyncIterableType: GenericType; - let deferredGlobalAsyncIteratorType: GenericType; - let deferredGlobalAsyncIterableIteratorType: GenericType; - let deferredGlobalAsyncGeneratorType: GenericType; - let deferredGlobalTemplateStringsArrayType: ObjectType; - let deferredGlobalImportMetaType: ObjectType; - let deferredGlobalExtractSymbol: Symbol; - let deferredGlobalOmitSymbol: Symbol; - let deferredGlobalBigIntType: ObjectType; - - const allPotentiallyUnusedIdentifiers = createMap(); // key is file name - - let flowLoopStart = 0; - let flowLoopCount = 0; - let sharedFlowCount = 0; - let flowAnalysisDisabled = false; - let flowInvocationCount = 0; - let lastFlowNode: FlowNode | undefined; - let lastFlowNodeReachable: boolean; - let flowTypeCache: Type[] | undefined; - - const emptyStringType = getLiteralType(""); - const zeroType = getLiteralType(0); - const zeroBigIntType = getLiteralType({ negative: false, base10Value: "0" }); - - const resolutionTargets: TypeSystemEntity[] = []; - const resolutionResults: boolean[] = []; - const resolutionPropertyNames: TypeSystemPropertyName[] = []; - - let suggestionCount = 0; - const maximumSuggestionCount = 10; - const mergedSymbols: Symbol[] = []; - const symbolLinks: SymbolLinks[] = []; - const nodeLinks: NodeLinks[] = []; - const flowLoopCaches: Map[] = []; - const flowLoopNodes: FlowNode[] = []; - const flowLoopKeys: string[] = []; - const flowLoopTypes: Type[][] = []; - const sharedFlowNodes: FlowNode[] = []; - const sharedFlowTypes: FlowType[] = []; - const flowNodeReachable: (boolean | undefined)[] = []; - const potentialThisCollisions: Node[] = []; - const potentialNewTargetCollisions: Node[] = []; - const potentialWeakMapCollisions: Node[] = []; - const awaitedTypeStack: number[] = []; - - const diagnostics = createDiagnosticCollection(); - const suggestionDiagnostics = createDiagnosticCollection(); - - const typeofTypesByName: ReadonlyMap = createMapFromTemplate({ - string: stringType, - number: numberType, - bigint: bigintType, - boolean: booleanType, - symbol: esSymbolType, - undefined: undefinedType - }); - const typeofType = createTypeofType(); - - let _jsxNamespace: __String; - let _jsxFactoryEntity: EntityName | undefined; - let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined; - - const subtypeRelation = createMap(); - const strictSubtypeRelation = createMap(); - const assignableRelation = createMap(); - const comparableRelation = createMap(); - const identityRelation = createMap(); - const enumRelation = createMap(); - - const builtinGlobals = createSymbolTable(); - builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol); - - initializeTypeChecker(); - - return checker; - - function getJsxNamespace(location: Node | undefined): __String { - if (location) { - const file = getSourceFileOfNode(location); - if (file) { - if (file.localJsxNamespace) { - return file.localJsxNamespace; - } - const jsxPragma = file.pragmas.get("jsx"); - if (jsxPragma) { - const chosenpragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma; - file.localJsxFactory = parseIsolatedEntityName(chosenpragma.arguments.factory, languageVersion); - if (file.localJsxFactory) { - return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText; - } - } - } - } - if (!_jsxNamespace) { - _jsxNamespace = "React" as __String; - if (compilerOptions.jsxFactory) { - _jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion); - if (_jsxFactoryEntity) { - _jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).escapedText; - } - } - else if (compilerOptions.reactNamespace) { - _jsxNamespace = escapeLeadingUnderscores(compilerOptions.reactNamespace); - } - } - return _jsxNamespace; - } - - function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) { - // Ensure we have all the type information in place for this file so that all the - // emitter questions of this resolver will return the right information. - getDiagnostics(sourceFile, cancellationToken); - return emitResolver; - } - - function lookupOrIssueError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { - const diagnostic = location - ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) - : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3); - const existing = diagnostics.lookup(diagnostic); - if (existing) { - return existing; - } - else { - diagnostics.add(diagnostic); - return diagnostic; - } - } - - function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { - const diagnostic = location - ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) - : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3); - diagnostics.add(diagnostic); - return diagnostic; - } - - function addErrorOrSuggestion(isError: boolean, diagnostic: DiagnosticWithLocation) { - if (isError) { - diagnostics.add(diagnostic); - } - else { - suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion }); - } - } - function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void { - addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line no-in-operator - } - - function errorAndMaybeSuggestAwait( - location: Node, - maybeMissingAwait: boolean, - message: DiagnosticMessage, - arg0?: string | number | undefined, arg1?: string | number | undefined, arg2?: string | number | undefined, arg3?: string | number | undefined): Diagnostic { - const diagnostic = error(location, message, arg0, arg1, arg2, arg3); - if (maybeMissingAwait) { - const related = createDiagnosticForNode(location, Diagnostics.Did_you_forget_to_use_await); - addRelatedInfo(diagnostic, related); - } - return diagnostic; - } - - function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) { - symbolCount++; - const symbol = (new Symbol(flags | SymbolFlags.Transient, name)); - symbol.checkFlags = checkFlags || 0; - return symbol; - } - - function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags { - let result: SymbolFlags = 0; - if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes; - if (flags & SymbolFlags.FunctionScopedVariable) result |= SymbolFlags.FunctionScopedVariableExcludes; - if (flags & SymbolFlags.Property) result |= SymbolFlags.PropertyExcludes; - if (flags & SymbolFlags.EnumMember) result |= SymbolFlags.EnumMemberExcludes; - if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes; - if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes; - if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes; - if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes; - if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes; - if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes; - if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes; - if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes; - if (flags & SymbolFlags.SetAccessor) result |= SymbolFlags.SetAccessorExcludes; - if (flags & SymbolFlags.TypeParameter) result |= SymbolFlags.TypeParameterExcludes; - if (flags & SymbolFlags.TypeAlias) result |= SymbolFlags.TypeAliasExcludes; - if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes; - return result; - } - - function recordMergedSymbol(target: Symbol, source: Symbol) { - if (!source.mergeId) { - source.mergeId = nextMergeId; - nextMergeId++; - } - mergedSymbols[source.mergeId] = target; - } - - function cloneSymbol(symbol: Symbol): Symbol { - const result = createSymbol(symbol.flags, symbol.escapedName); - result.declarations = symbol.declarations ? symbol.declarations.slice() : []; - result.parent = symbol.parent; - if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; - if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; - if (symbol.members) result.members = cloneMap(symbol.members); - if (symbol.exports) result.exports = cloneMap(symbol.exports); - recordMergedSymbol(result, symbol); - return result; - } - - /** - * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it. - * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it. - */ - function mergeSymbol(target: Symbol, source: Symbol, unidirectional = false): Symbol { - if (!(target.flags & getExcludedSymbolFlags(source.flags)) || - (source.flags | target.flags) & SymbolFlags.Assignment) { - if (source === target) { - // This can happen when an export assigned namespace exports something also erroneously exported at the top level - // See `declarationFileNoCrashOnExtraExportModifier` for an example - return target; - } - if (!(target.flags & SymbolFlags.Transient)) { - const resolvedTarget = resolveSymbol(target); - if (resolvedTarget === unknownSymbol) { - return source; - } - target = cloneSymbol(resolvedTarget); - } - // Javascript static-property-assignment declarations always merge, even though they are also values - if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) { - // reset flag when merging instantiated module into value module that has only const enums - target.constEnumOnlyModule = false; - } - target.flags |= source.flags; - if (source.valueDeclaration && - (!target.valueDeclaration || - isAssignmentDeclaration(target.valueDeclaration) && !isAssignmentDeclaration(source.valueDeclaration) || - isEffectiveModuleDeclaration(target.valueDeclaration) && !isEffectiveModuleDeclaration(source.valueDeclaration))) { - // other kinds of value declarations take precedence over modules and assignment declarations - target.valueDeclaration = source.valueDeclaration; - } - addRange(target.declarations, source.declarations); - if (source.members) { - if (!target.members) target.members = createSymbolTable(); - mergeSymbolTable(target.members, source.members, unidirectional); - } - if (source.exports) { - if (!target.exports) target.exports = createSymbolTable(); - mergeSymbolTable(target.exports, source.exports, unidirectional); - } - if (!unidirectional) { - recordMergedSymbol(target, source); - } - } - else if (target.flags & SymbolFlags.NamespaceModule) { - // Do not report an error when merging `var globalThis` with the built-in `globalThis`, - // as we will already report a "Declaration name conflicts..." error, and this error - // won't make much sense. - if (target !== globalThisSymbol) { - error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target)); - } - } - else { // error - const isEitherEnum = !!(target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum); - const isEitherBlockScoped = !!(target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable); - const message = isEitherEnum - ? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations - : isEitherBlockScoped - ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 - : Diagnostics.Duplicate_identifier_0; - const sourceSymbolFile = source.declarations && getSourceFileOfNode(source.declarations[0]); - const targetSymbolFile = target.declarations && getSourceFileOfNode(target.declarations[0]); - const symbolName = symbolToString(source); - - // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch - if (sourceSymbolFile && targetSymbolFile && amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile !== targetSymbolFile) { - const firstFile = comparePaths(sourceSymbolFile.path, targetSymbolFile.path) === Comparison.LessThan ? sourceSymbolFile : targetSymbolFile; - const secondFile = firstFile === sourceSymbolFile ? targetSymbolFile : sourceSymbolFile; - const filesDuplicates = getOrUpdate(amalgamatedDuplicates, `${firstFile.path}|${secondFile.path}`, () => - ({ firstFile, secondFile, conflictingSymbols: createMap() })); - const conflictingSymbolInfo = getOrUpdate(filesDuplicates.conflictingSymbols, symbolName, () => - ({ isBlockScoped: isEitherBlockScoped, firstFileLocations: [], secondFileLocations: [] })); - addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source); - addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target); - } - else { - addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target); - addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source); - } - } - return target; - - function addDuplicateLocations(locs: Node[], symbol: Symbol): void { - for (const decl of symbol.declarations) { - pushIfUnique(locs, (getExpandoInitializer(decl, /*isPrototypeAssignment*/ false) ? getNameOfExpando(decl) : getNameOfDeclaration(decl)) || decl); - } - } - } - - function addDuplicateDeclarationErrorsForSymbols(target: Symbol, message: DiagnosticMessage, symbolName: string, source: Symbol) { - forEach(target.declarations, node => { - const errorNode = (getExpandoInitializer(node, /*isPrototypeAssignment*/ false) ? getNameOfExpando(node) : getNameOfDeclaration(node)) || node; - addDuplicateDeclarationError(errorNode, message, symbolName, source.declarations); - }); - } - - function addDuplicateDeclarationError(errorNode: Node, message: DiagnosticMessage, symbolName: string, relatedNodes: readonly Node[] | undefined) { - const err = lookupOrIssueError(errorNode, message, symbolName); - for (const relatedNode of relatedNodes || emptyArray) { - err.relatedInformation = err.relatedInformation || []; - if (length(err.relatedInformation) >= 5) continue; - addRelatedInfo(err, !length(err.relatedInformation) ? createDiagnosticForNode(relatedNode, Diagnostics._0_was_also_declared_here, symbolName) : createDiagnosticForNode(relatedNode, Diagnostics.and_here)); - } - } - - function combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined { - if (!hasEntries(first)) return second; - if (!hasEntries(second)) return first; - const combined = createSymbolTable(); - mergeSymbolTable(combined, first); - mergeSymbolTable(combined, second); - return combined; - } - - function mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false) { - source.forEach((sourceSymbol, id) => { - const targetSymbol = target.get(id); - target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : sourceSymbol); - }); - } - - function mergeModuleAugmentation(moduleName: StringLiteral | Identifier): void { - const moduleAugmentation = moduleName.parent; - if (moduleAugmentation.symbol.declarations[0] !== moduleAugmentation) { - // this is a combined symbol for multiple augmentations within the same file. - // its symbol already has accumulated information for all declarations - // so we need to add it just once - do the work only for first declaration - Debug.assert(moduleAugmentation.symbol.declarations.length > 1); - return; - } - - if (isGlobalScopeAugmentation(moduleAugmentation)) { - mergeSymbolTable(globals, moduleAugmentation.symbol.exports!); - } - else { - // find a module that about to be augmented - // do not validate names of augmentations that are defined in ambient context - const moduleNotFoundError = !(moduleName.parent.parent.flags & NodeFlags.Ambient) - ? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found - : undefined; - let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true); - if (!mainModule) { - return; - } - // obtain item referenced by 'export=' - mainModule = resolveExternalModuleSymbol(mainModule); - if (mainModule.flags & SymbolFlags.Namespace) { - // If we're merging an augmentation to a pattern ambient module, we want to - // perform the merge unidirectionally from the augmentation ('a.foo') to - // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you - // all the exports both from the pattern and from the augmentation, but - // 'getMergedSymbol()' on *.foo only gives you exports from *.foo. - if (some(patternAmbientModules, module => mainModule === module.symbol)) { - const merged = mergeSymbol(moduleAugmentation.symbol, mainModule, /*unidirectional*/ true); - if (!patternAmbientModuleAugmentations) { - patternAmbientModuleAugmentations = createMap(); - } - // moduleName will be a StringLiteral since this is not `declare global`. - patternAmbientModuleAugmentations.set((moduleName as StringLiteral).text, merged); - } - else { - mergeSymbol(mainModule, moduleAugmentation.symbol); - } - } - else { - // moduleName will be a StringLiteral since this is not `declare global`. - error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, (moduleName as StringLiteral).text); - } - } - } - - function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { - source.forEach((sourceSymbol, id) => { - const targetSymbol = target.get(id); - if (targetSymbol) { - // Error on redeclarations - forEach(targetSymbol.declarations, addDeclarationDiagnostic(unescapeLeadingUnderscores(id), message)); - } - else { - target.set(id, sourceSymbol); - } - }); - - function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) { - return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id)); - } - } - - function getSymbolLinks(symbol: Symbol): SymbolLinks { - if (symbol.flags & SymbolFlags.Transient) return symbol; - const id = getSymbolId(symbol); - return symbolLinks[id] || (symbolLinks[id] = {}); - } - - function getNodeLinks(node: Node): NodeLinks { - const nodeId = getNodeId(node); - return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 } as NodeLinks); - } - - function isGlobalSourceFile(node: Node) { - return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node); - } - - function getSymbol(symbols: SymbolTable, name: __String, meaning: SymbolFlags): Symbol | undefined { - if (meaning) { - const symbol = symbols.get(name); - if (symbol) { - Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); - if (symbol.flags & meaning) { - return symbol; - } - if (symbol.flags & SymbolFlags.Alias) { - const target = resolveAlias(symbol); - // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors - if (target === unknownSymbol || target.flags & meaning) { - return symbol; - } - } - } - } - // return undefined if we can't find a symbol. - } - - /** - * Get symbols that represent parameter-property-declaration as parameter and as property declaration - * @param parameter a parameterDeclaration node - * @param parameterName a name of the parameter to get the symbols for. - * @return a tuple of two symbols - */ - function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: __String): [Symbol, Symbol] { - const constructorDeclaration = parameter.parent; - const classDeclaration = parameter.parent.parent; - - const parameterSymbol = getSymbol(constructorDeclaration.locals!, parameterName, SymbolFlags.Value); - const propertySymbol = getSymbol(getMembersOfSymbol(classDeclaration.symbol), parameterName, SymbolFlags.Value); - - if (parameterSymbol && propertySymbol) { - return [parameterSymbol, propertySymbol]; - } - - return Debug.fail("There should exist two symbols, one as property declaration and one as parameter declaration"); - } - - function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean { - const declarationFile = getSourceFileOfNode(declaration); - const useFile = getSourceFileOfNode(usage); - if (declarationFile !== useFile) { - if ((moduleKind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) || - (!compilerOptions.outFile && !compilerOptions.out) || - isInTypeQuery(usage) || - declaration.flags & NodeFlags.Ambient) { - // nodes are in different files and order cannot be determined - return true; - } - // declaration is after usage - // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) - if (isUsedInFunctionOrInstanceProperty(usage, declaration)) { - return true; - } - const sourceFiles = host.getSourceFiles(); - return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile); - } - - if (declaration.pos <= usage.pos) { - // declaration is before usage - if (declaration.kind === SyntaxKind.BindingElement) { - // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2]) - const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement; - if (errorBindingElement) { - return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) || - declaration.pos < errorBindingElement.pos; - } - // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a) - return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage); - } - else if (declaration.kind === SyntaxKind.VariableDeclaration) { - // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a) - return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration as VariableDeclaration, usage); - } - else if (isClassDeclaration(declaration)) { - // still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} }) - return !findAncestor(usage, n => isComputedPropertyName(n) && n.parent.parent === declaration); - } - else if (isPropertyDeclaration(declaration)) { - // still might be illegal if a self-referencing property initializer (eg private x = this.x) - return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage); - } - return true; - } - - - // declaration is after usage, but it can still be legal if usage is deferred: - // 1. inside an export specifier - // 2. inside a function - // 3. inside an instance property initializer, a reference to a non-instance property - // 4. inside a static property initializer, a reference to a static method in the same class - // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ) - // or if usage is in a type context: - // 1. inside a type query (typeof in type position) - // 2. inside a jsdoc comment - if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) { - // export specifiers do not use the variable, they only make it available for use - return true; - } - // When resolving symbols for exports, the `usage` location passed in can be the export site directly - if (usage.kind === SyntaxKind.ExportAssignment && (usage as ExportAssignment).isExportEquals) { - return true; - } - - const container = getEnclosingBlockScopeContainer(declaration); - return !!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isUsedInFunctionOrInstanceProperty(usage, declaration, container); - - function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { - const container = getEnclosingBlockScopeContainer(declaration); - - switch (declaration.parent.parent.kind) { - case SyntaxKind.VariableStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForOfStatement: - // variable statement/for/for-of statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - if (isSameScopeDescendentOf(usage, declaration, container)) { - return true; - } - break; - } - - // ForIn/ForOf case - use site should not be used in expression part - const grandparent = declaration.parent.parent; - return isForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.expression, container); - } - - function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node, container?: Node): boolean { - return !!findAncestor(usage, current => { - if (current === container) { - return "quit"; - } - if (isFunctionLike(current)) { - return true; - } - - const initializerOfProperty = current.parent && - current.parent.kind === SyntaxKind.PropertyDeclaration && - (current.parent).initializer === current; - - if (initializerOfProperty) { - if (hasModifier(current.parent, ModifierFlags.Static)) { - if (declaration.kind === SyntaxKind.MethodDeclaration) { - return true; - } - } - else { - const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !hasModifier(declaration, ModifierFlags.Static); - if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { - return true; - } - } - } - return false; - }); - } - - function isPropertyImmediatelyReferencedWithinDeclaration(declaration: PropertyDeclaration, usage: Node) { - // always legal if usage is after declaration - if (usage.end > declaration.end) { - return false; - } - - // still might be legal if usage is deferred (e.g. x: any = () => this.x) - // otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x) - const ancestorChangingReferenceScope = findAncestor(usage, (node: Node) => { - if (node === declaration) { - return "quit"; - } - - switch (node.kind) { - case SyntaxKind.ArrowFunction: - case SyntaxKind.PropertyDeclaration: - return true; - case SyntaxKind.Block: - switch (node.parent.kind) { - case SyntaxKind.GetAccessor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.SetAccessor: - return true; - default: - return false; - } - default: - return false; - } - }); - - return ancestorChangingReferenceScope === undefined; - } - } - - /** - * Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and - * the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with - * the given name can be found. - * - * @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters. - */ - function resolveName( - location: Node | undefined, - name: __String, - meaning: SymbolFlags, - nameNotFoundMessage: DiagnosticMessage | undefined, - nameArg: __String | Identifier | undefined, - isUse: boolean, - excludeGlobals = false, - suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol | undefined { - return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSymbol, suggestedNameNotFoundMessage); - } - - function resolveNameHelper( - location: Node | undefined, - name: __String, - meaning: SymbolFlags, - nameNotFoundMessage: DiagnosticMessage | undefined, - nameArg: __String | Identifier | undefined, - isUse: boolean, - excludeGlobals: boolean, - lookup: typeof getSymbol, - suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol | undefined { - const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location - let result: Symbol | undefined; - let lastLocation: Node | undefined; - let lastSelfReferenceLocation: Node | undefined; - let propertyWithInvalidInitializer: Node | undefined; - let associatedDeclarationForContainingInitializer: ParameterDeclaration | BindingElement | undefined; - let withinDeferredContext = false; - const errorLocation = location; - let grandparent: Node; - let isInExternalModule = false; - - loop: while (location) { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if (location.locals && !isGlobalSourceFile(location)) { - if (result = lookup(location.locals, name, meaning)) { - let useResult = true; - if (isFunctionLike(location) && lastLocation && lastLocation !== (location).body) { - // symbol lookup restrictions for function-like declarations - // - Type parameters of a function are in scope in the entire function declaration, including the parameter - // list and return type. However, local types are only in scope in the function body. - // - parameters are only in the scope of function body - // This restriction does not apply to JSDoc comment types because they are parented - // at a higher level than type parameters would normally be - if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDocComment) { - useResult = result.flags & SymbolFlags.TypeParameter - // type parameters are visible in parameter list, return type and type parameter list - ? lastLocation === (location).type || - lastLocation.kind === SyntaxKind.Parameter || - lastLocation.kind === SyntaxKind.TypeParameter - // local types not visible outside the function body - : false; - } - if (meaning & result.flags & SymbolFlags.Variable) { - // expression inside parameter will lookup as normal variable scope when targeting es2015+ - const functionLocation = location; - if (compilerOptions.target && compilerOptions.target >= ScriptTarget.ES2015 && isParameter(lastLocation) && - functionLocation.body && result.valueDeclaration.pos >= functionLocation.body.pos && result.valueDeclaration.end <= functionLocation.body.end) { - useResult = false; - } - else if (result.flags & SymbolFlags.FunctionScopedVariable) { - // parameters are visible only inside function body, parameter list and return type - // technically for parameter list case here we might mix parameters and variables declared in function, - // however it is detected separately when checking initializers of parameters - // to make sure that they reference no variables declared after them. - useResult = - lastLocation.kind === SyntaxKind.Parameter || - ( - lastLocation === (location).type && - !!findAncestor(result.valueDeclaration, isParameter) - ); - } - } - } - else if (location.kind === SyntaxKind.ConditionalType) { - // A type parameter declared using 'infer T' in a conditional type is visible only in - // the true branch of the conditional type. - useResult = lastLocation === (location).trueType; - } - - if (useResult) { - break loop; - } - else { - result = undefined; - } - } - } - withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation); - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule(location)) break; - isInExternalModule = true; - // falls through - case SyntaxKind.ModuleDeclaration: - const moduleExports = getSymbolOfNode(location as SourceFile | ModuleDeclaration).exports || emptySymbols; - if (location.kind === SyntaxKind.SourceFile || (isModuleDeclaration(location) && location.flags & NodeFlags.Ambient && !isGlobalScopeAugmentation(location))) { - - // It's an external module. First see if the module has an export default and if the local - // name of that export default matches. - if (result = moduleExports.get(InternalSymbolName.Default)) { - const localSymbol = getLocalSymbolForExportDefault(result); - if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) { - break loop; - } - result = undefined; - } - - // Because of module/namespace merging, a module's exports are in scope, - // yet we never want to treat an export specifier as putting a member in scope. - // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope. - // Two things to note about this: - // 1. We have to check this without calling getSymbol. The problem with calling getSymbol - // on an export specifier is that it might find the export specifier itself, and try to - // resolve it as an alias. This will cause the checker to consider the export specifier - // a circular alias reference when it might not be. - // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* - // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, - // which is not the desired behavior. - const moduleExport = moduleExports.get(name); - if (moduleExport && - moduleExport.flags === SymbolFlags.Alias && - (getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))) { - break; - } - } - - // ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs) - if (name !== InternalSymbolName.Default && (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember))) { - if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations.some(isJSDocTypeAlias)) { - result = undefined; - } - else { - break loop; - } - } - break; - case SyntaxKind.EnumDeclaration: - if (result = lookup(getSymbolOfNode(location)!.exports!, name, meaning & SymbolFlags.EnumMember)) { - break loop; - } - break; - case SyntaxKind.PropertyDeclaration: - // TypeScript 1.0 spec (April 2014): 8.4.1 - // Initializer expressions for instance member variables are evaluated in the scope - // of the class constructor body but are not permitted to reference parameters or - // local variables of the constructor. This effectively means that entities from outer scopes - // by the same name as a constructor parameter or local variable are inaccessible - // in initializer expressions for instance member variables. - if (!hasModifier(location, ModifierFlags.Static)) { - const ctor = findConstructorDeclaration(location.parent as ClassLikeDeclaration); - if (ctor && ctor.locals) { - if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) { - // Remember the property node, it will be used later to report appropriate error - propertyWithInvalidInitializer = location; - } - } - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals - // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would - // trigger resolving late-bound names, which we may already be in the process of doing while we're here! - if (result = lookup(getSymbolOfNode(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols, name, meaning & SymbolFlags.Type)) { - if (!isTypeParameterSymbolDeclaredInContainer(result, location)) { - // ignore type parameters not declared in this container - result = undefined; - break; - } - if (lastLocation && hasModifier(lastLocation, ModifierFlags.Static)) { - // TypeScript 1.0 spec (April 2014): 3.4.1 - // The scope of a type parameter extends over the entire declaration with which the type - // parameter list is associated, with the exception of static member declarations in classes. - error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters); - return undefined; - } - break loop; - } - if (location.kind === SyntaxKind.ClassExpression && meaning & SymbolFlags.Class) { - const className = (location).name; - if (className && name === className.escapedText) { - result = location.symbol; - break loop; - } - } - break; - case SyntaxKind.ExpressionWithTypeArguments: - // The type parameters of a class are not in scope in the base class expression. - if (lastLocation === (location).expression && (location.parent).token === SyntaxKind.ExtendsKeyword) { - const container = location.parent.parent; - if (isClassLike(container) && (result = lookup(getSymbolOfNode(container).members!, name, meaning & SymbolFlags.Type))) { - if (nameNotFoundMessage) { - error(errorLocation, Diagnostics.Base_class_expressions_cannot_reference_class_type_parameters); - } - return undefined; - } - } - break; - // It is not legal to reference a class's own type parameters from a computed property name that - // belongs to the class. For example: - // - // function foo() { return '' } - // class C { // <-- Class's own type parameter T - // [foo()]() { } // <-- Reference to T from class's own computed property - // } - // - case SyntaxKind.ComputedPropertyName: - grandparent = location.parent.parent; - if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) { - // A reference to this grandparent's type parameters would be an error - if (result = lookup(getSymbolOfNode(grandparent as ClassLikeDeclaration | InterfaceDeclaration).members!, name, meaning & SymbolFlags.Type)) { - error(errorLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type); - return undefined; - } - } - break; - case SyntaxKind.ArrowFunction: - // when targeting ES6 or higher there is no 'arguments' in an arrow function - // for lower compile targets the resolved symbol is used to emit an error - if (compilerOptions.target! >= ScriptTarget.ES2015) { - break; - } - // falls through - case SyntaxKind.MethodDeclaration: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionDeclaration: - if (meaning & SymbolFlags.Variable && name === "arguments") { - result = argumentsSymbol; - break loop; - } - break; - case SyntaxKind.FunctionExpression: - if (meaning & SymbolFlags.Variable && name === "arguments") { - result = argumentsSymbol; - break loop; - } - - if (meaning & SymbolFlags.Function) { - const functionName = (location).name; - if (functionName && name === functionName.escapedText) { - result = location.symbol; - break loop; - } - } - break; - case SyntaxKind.Decorator: - // Decorators are resolved at the class declaration. Resolving at the parameter - // or member would result in looking up locals in the method. - // - // function y() {} - // class C { - // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter. - // } - // - if (location.parent && location.parent.kind === SyntaxKind.Parameter) { - location = location.parent; - } - // - // function y() {} - // class C { - // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method. - // } - // - - // class Decorators are resolved outside of the class to avoid referencing type parameters of that class. - // - // type T = number; - // declare function y(x: T): any; - // @param(1 as T) // <-- T should resolve to the type alias outside of class C - // class C {} - if (location.parent && (isClassElement(location.parent) || location.parent.kind === SyntaxKind.ClassDeclaration)) { - location = location.parent; - } - break; - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - // js type aliases do not resolve names from their host, so skip past it - location = getJSDocHost(location); - break; - case SyntaxKind.Parameter: - if (lastLocation && lastLocation === (location as ParameterDeclaration).initializer) { - associatedDeclarationForContainingInitializer = location as ParameterDeclaration; - } - break; - case SyntaxKind.BindingElement: - if (lastLocation && lastLocation === (location as BindingElement).initializer) { - const root = getRootDeclaration(location); - if (root.kind === SyntaxKind.Parameter) { - associatedDeclarationForContainingInitializer = location as BindingElement; - } - } - break; - } - if (isSelfReferenceLocation(location)) { - lastSelfReferenceLocation = location; - } - lastLocation = location; - location = location.parent; - } - - // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. - // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself. - // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. - if (isUse && result && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) { - result.isReferenced! |= meaning; - } - - if (!result) { - if (lastLocation) { - Debug.assert(lastLocation.kind === SyntaxKind.SourceFile); - if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports" && meaning & lastLocation.symbol.flags) { - return lastLocation.symbol; - } - } - - if (!excludeGlobals) { - result = lookup(globals, name, meaning); - } - } - if (!result) { - if (originalLocation && isInJSFile(originalLocation) && originalLocation.parent) { - if (isRequireCall(originalLocation.parent, /*checkArgumentIsStringLiteralLike*/ false)) { - return requireSymbol; - } - } - } - if (!result) { - if (nameNotFoundMessage) { - if (!errorLocation || - !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg!) && // TODO: GH#18217 - !checkAndReportErrorForExtendingInterface(errorLocation) && - !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && - !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && - !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) && - !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) { - let suggestion: Symbol | undefined; - if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) { - suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning); - if (suggestion) { - const suggestionName = symbolToString(suggestion); - const diagnostic = error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName); - if (suggestion.valueDeclaration) { - addRelatedInfo( - diagnostic, - createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName) - ); - } - } - } - if (!suggestion) { - error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg!)); - } - suggestionCount++; - } - } - return undefined; - } - - // Perform extra checks only if error reporting was requested - if (nameNotFoundMessage) { - if (propertyWithInvalidInitializer) { - // We have a match, but the reference occurred within a property initializer and the identifier also binds - // to a local variable in the constructor where the code will be emitted. - const propertyName = (propertyWithInvalidInitializer).name; - error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, - declarationNameToString(propertyName), diagnosticName(nameArg!)); - return undefined; - } - - // Only check for block-scoped variable if we have an error location and are looking for the - // name with variable meaning - // For example, - // declare module foo { - // interface bar {} - // } - // const foo/*1*/: foo/*2*/.bar; - // The foo at /*1*/ and /*2*/ will share same symbol with two meanings: - // block-scoped variable and namespace module. However, only when we - // try to resolve name in /*1*/ which is used in variable position, - // we want to check for block-scoped - if (errorLocation && - (meaning & SymbolFlags.BlockScopedVariable || - ((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value))) { - const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result); - if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) { - checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation); - } - } - - // If we're in an external module, we can't reference value symbols created from UMD export declarations - if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value && !(originalLocation!.flags & NodeFlags.JSDoc)) { - const merged = getMergedSymbol(result); - if (length(merged.declarations) && every(merged.declarations, d => isNamespaceExportDeclaration(d) || isSourceFile(d) && !!d.symbol.globalExports)) { - errorOrSuggestion(!compilerOptions.allowUmdGlobalAccess, errorLocation!, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, unescapeLeadingUnderscores(name)); - } - } - - // If we're in a parameter initializer, we can't reference the values of the parameter whose initializer we're within or parameters to the right - if (result && associatedDeclarationForContainingInitializer && !withinDeferredContext && (meaning & SymbolFlags.Value) === SymbolFlags.Value) { - const candidate = getMergedSymbol(getLateBoundSymbol(result)); - const root = (getRootDeclaration(associatedDeclarationForContainingInitializer) as ParameterDeclaration); - // A parameter initializer or binding pattern initializer within a parameter cannot refer to itself - if (candidate === getSymbolOfNode(associatedDeclarationForContainingInitializer)) { - error(errorLocation, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, declarationNameToString(associatedDeclarationForContainingInitializer.name)); - } - // And it cannot refer to any declarations which come after it - else if (candidate.valueDeclaration && candidate.valueDeclaration.pos > associatedDeclarationForContainingInitializer.pos && root.parent.locals && lookup(root.parent.locals, candidate.escapedName, meaning) === candidate) { - error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializer.name), declarationNameToString(errorLocation)); - } - } - } - return result; - } - - function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean { - if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) { - // initializers in instance property declaration of class like entities are executed in constructor and thus deferred - return isTypeQueryNode(location) || (( - isFunctionLikeDeclaration(location) || - (location.kind === SyntaxKind.PropertyDeclaration && !hasModifier(location, ModifierFlags.Static)) - ) && (!lastLocation || lastLocation !== (location as FunctionLike | PropertyDeclaration).name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred - } - if (lastLocation && lastLocation === (location as FunctionExpression | ArrowFunction).name) { - return false; - } - // generator functions and async functions are not inlined in control flow when immediately invoked - if ((location as FunctionExpression | ArrowFunction).asteriskToken || hasModifier(location, ModifierFlags.Async)) { - return true; - } - return !getImmediatelyInvokedFunctionExpression(location); - } - - function isSelfReferenceLocation(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }` - return true; - default: - return false; - } - } - - function diagnosticName(nameArg: __String | Identifier | PrivateIdentifier) { - return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier); - } - - function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) { - for (const decl of symbol.declarations) { - if (decl.kind === SyntaxKind.TypeParameter) { - const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent; - if (parent === container) { - return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags!, isJSDocTypeAlias)); // TODO: GH#18217 - } - } - } - - return false; - } - - function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: __String, nameArg: __String | Identifier): boolean { - if (!isIdentifier(errorLocation) || errorLocation.escapedText !== name || isTypeReferenceIdentifier(errorLocation) || isInTypeQuery(errorLocation)) { - return false; - } - - const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ false); - let location = container; - while (location) { - if (isClassLike(location.parent)) { - const classSymbol = getSymbolOfNode(location.parent); - if (!classSymbol) { - break; - } - - // Check to see if a static member exists. - const constructorType = getTypeOfSymbol(classSymbol); - if (getPropertyOfType(constructorType, name)) { - error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, diagnosticName(nameArg), symbolToString(classSymbol)); - return true; - } - - // No static member is present. - // Check if we're in an instance method and look for a relevant instance member. - if (location === container && !hasModifier(location, ModifierFlags.Static)) { - const instanceType = (getDeclaredTypeOfSymbol(classSymbol)).thisType!; // TODO: GH#18217 - if (getPropertyOfType(instanceType, name)) { - error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, diagnosticName(nameArg)); - return true; - } - } - } - - location = location.parent; - } - return false; - } - - - function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { - const expression = getEntityNameForExtendingInterface(errorLocation); - if (expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) { - error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); - return true; - } - return false; - } - /** - * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, - * but returns undefined if that expression is not an EntityNameExpression. - */ - function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PropertyAccessExpression: - return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; - case SyntaxKind.ExpressionWithTypeArguments: - if (isEntityNameExpression((node).expression)) { - return (node).expression; - } - // falls through - default: - return undefined; - } - } - - function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { - const namespaceMeaning = SymbolFlags.Namespace | (isInJSFile(errorLocation) ? SymbolFlags.Value : 0); - if (meaning === namespaceMeaning) { - const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~namespaceMeaning, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); - const parent = errorLocation.parent; - if (symbol) { - if (isQualifiedName(parent)) { - Debug.assert(parent.left === errorLocation, "Should only be resolving left side of qualified name as a namespace"); - const propName = parent.right.escapedText; - const propType = getPropertyOfType(getDeclaredTypeOfSymbol(symbol), propName); - if (propType) { - error( - parent, - Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, - unescapeLeadingUnderscores(name), - unescapeLeadingUnderscores(propName), - ); - return true; - } - } - error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name)); - return true; - } - } - - return false; - } - - function checkAndReportErrorForUsingValueAsType(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { - if (meaning & (SymbolFlags.Type & ~SymbolFlags.Namespace)) { - const symbol = resolveSymbol(resolveName(errorLocation, name, ~SymbolFlags.Type & SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); - if (symbol && !(symbol.flags & SymbolFlags.Namespace)) { - error(errorLocation, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here, unescapeLeadingUnderscores(name)); - return true; - } - } - return false; - } - - function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { - if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) { - if (name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never") { - error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name)); - return true; - } - const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); - if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) { - const message = isES2015OrLaterConstructorName(name) - ? Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later - : Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here; - error(errorLocation, message, unescapeLeadingUnderscores(name)); - return true; - } - } - return false; - } - - function isES2015OrLaterConstructorName(n: __String) { - switch (n) { - case "Promise": - case "Symbol": - case "Map": - case "WeakMap": - case "Set": - case "WeakSet": - return true; - } - return false; - } - - function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { - if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) { - const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); - if (symbol) { - error( - errorLocation, - isTypeOnlyEnumAlias(symbol) - ? Diagnostics.Enum_0_cannot_be_used_as_a_value_because_only_its_type_has_been_imported - : Diagnostics.Cannot_use_namespace_0_as_a_value, - unescapeLeadingUnderscores(name)); - return true; - } - } - else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) { - const symbol = resolveSymbol(resolveName(errorLocation, name, (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); - if (symbol) { - error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name)); - return true; - } - } - return false; - } - - function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { - Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum)); - if (result.flags & (SymbolFlags.Function | SymbolFlags.FunctionScopedVariable | SymbolFlags.Assignment) && result.flags & SymbolFlags.Class) { - // constructor functions aren't block scoped - return; - } - // Block-scoped variables cannot be used before their definition - const declaration = find( - result.declarations, - d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration)); - - if (declaration === undefined) return Debug.fail("checkResolvedBlockScopedVariable could not find block-scoped declaration"); - - if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) { - let diagnosticMessage; - const declarationName = declarationNameToString(getNameOfDeclaration(declaration)); - if (result.flags & SymbolFlags.BlockScopedVariable) { - diagnosticMessage = error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName); - } - else if (result.flags & SymbolFlags.Class) { - diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName); - } - else if (result.flags & SymbolFlags.RegularEnum) { - diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName); - } - else { - Debug.assert(!!(result.flags & SymbolFlags.ConstEnum)); - if (compilerOptions.preserveConstEnums) { - diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName); - } - } - - if (diagnosticMessage) { - addRelatedInfo(diagnosticMessage, - createDiagnosticForNode(declaration, Diagnostics._0_is_declared_here, declarationName) - ); - } - } - } - - /* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached. - * If at any point current node is equal to 'parent' node - return true. - * Return false if 'stopAt' node is reached or isFunctionLike(current) === true. - */ - function isSameScopeDescendentOf(initial: Node, parent: Node | undefined, stopAt: Node): boolean { - return !!parent && !!findAncestor(initial, n => n === stopAt || isFunctionLike(n) ? "quit" : n === parent); - } - - function getAnyImportSyntax(node: Node): AnyImportSyntax | undefined { - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - return node as ImportEqualsDeclaration; - case SyntaxKind.ImportClause: - return (node as ImportClause).parent; - case SyntaxKind.NamespaceImport: - return (node as NamespaceImport).parent.parent; - case SyntaxKind.ImportSpecifier: - return (node as ImportSpecifier).parent.parent.parent; - default: - return undefined; - } - } - - function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined { - return find(symbol.declarations, isAliasSymbolDeclaration); - } - - function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol | undefined { - if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) { - return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node))); - } - return getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, dontResolveAlias); - } - - function resolveExportByName(moduleSymbol: Symbol, name: __String, dontResolveAlias: boolean) { - const exportValue = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals); - return exportValue - ? getPropertyOfType(getTypeOfSymbol(exportValue), name) - : resolveSymbol(moduleSymbol.exports!.get(name), dontResolveAlias); - } - - function isSyntacticDefault(node: Node) { - return ((isExportAssignment(node) && !node.isExportEquals) || hasModifier(node, ModifierFlags.Default) || isExportSpecifier(node)); - } - - function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean) { - if (!allowSyntheticDefaultImports) { - return false; - } - // Declaration files (and ambient modules) - if (!file || file.isDeclarationFile) { - // Definitely cannot have a synthetic default if they have a syntactic default member specified - const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration - if (defaultExportSymbol && some(defaultExportSymbol.declarations, isSyntacticDefault)) { - return false; - } - // It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member - // So we check a bit more, - if (resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), dontResolveAlias)) { - // If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code), - // it definitely is a module and does not have a synthetic default - return false; - } - // There are _many_ declaration files not written with esmodules in mind that still get compiled into a format with __esModule set - // Meaning there may be no default at runtime - however to be on the permissive side, we allow access to a synthetic default member - // as there is no marker to indicate if the accompanying JS has `__esModule` or not, or is even native esm - return true; - } - // TypeScript files never have a synthetic default (as they are always emitted with an __esModule marker) _unless_ they contain an export= statement - if (!isSourceFileJS(file)) { - return hasExportAssignmentSymbol(moduleSymbol); - } - // JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker - return !file.externalModuleIndicator && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), dontResolveAlias); - } - - function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol | undefined { - const moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); - - if (moduleSymbol) { - let exportDefaultSymbol: Symbol | undefined; - if (isShorthandAmbientModuleSymbol(moduleSymbol)) { - exportDefaultSymbol = moduleSymbol; - } - else { - exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, dontResolveAlias); - } - - const file = find(moduleSymbol.declarations, isSourceFile); - const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias); - if (!exportDefaultSymbol && !hasSyntheticDefault) { - if (hasExportAssignmentSymbol(moduleSymbol)) { - const compilerOptionName = moduleKind >= ModuleKind.ES2015 ? "allowSyntheticDefaultImports" : "esModuleInterop"; - const exportEqualsSymbol = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals); - const exportAssignment = exportEqualsSymbol!.valueDeclaration; - const err = error(node.name, Diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, symbolToString(moduleSymbol), compilerOptionName); - - addRelatedInfo(err, createDiagnosticForNode( - exportAssignment, - Diagnostics.This_module_is_declared_with_using_export_and_can_only_be_used_with_a_default_import_when_using_the_0_flag, - compilerOptionName - )); - } - else { - if (moduleSymbol.exports && moduleSymbol.exports.has(node.symbol.escapedName)) { - error( - node.name, - Diagnostics.Module_0_has_no_default_export_Did_you_mean_to_use_import_1_from_0_instead, - symbolToString(moduleSymbol), - symbolToString(node.symbol), - ); - } - else { - error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); - } - - } - } - else if (hasSyntheticDefault) { - // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present - return maybeTypeOnly( - resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || - resolveSymbol(moduleSymbol, dontResolveAlias)); - } - return maybeTypeOnly(exportDefaultSymbol); - } - - function maybeTypeOnly(symbol: Symbol | undefined) { - if (symbol && node.isTypeOnly && node.name) { - return createTypeOnlyImportOrExport(node.name, symbol); - } - return symbol; - } - } - - function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined { - const moduleSpecifier = node.parent.parent.moduleSpecifier; - const moduleSymbol = resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); - return moduleSymbol && node.parent.isTypeOnly ? createTypeOnlySymbol(moduleSymbol) : moduleSymbol; - } - - function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined { - const moduleSpecifier = node.parent.moduleSpecifier; - return moduleSpecifier && resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); - } - - // This function creates a synthetic symbol that combines the value side of one symbol with the - // type/namespace side of another symbol. Consider this example: - // - // declare module graphics { - // interface Point { - // x: number; - // y: number; - // } - // } - // declare var graphics: { - // Point: new (x: number, y: number) => graphics.Point; - // } - // declare module "graphics" { - // export = graphics; - // } - // - // An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point' - // property with the type/namespace side interface 'Point'. - function combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol { - if (valueSymbol === unknownSymbol && typeSymbol === unknownSymbol) { - return unknownSymbol; - } - if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) { - return valueSymbol; - } - const result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.escapedName); - result.declarations = deduplicate(concatenate(valueSymbol.declarations, typeSymbol.declarations), equateValues); - result.parent = valueSymbol.parent || typeSymbol.parent; - if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration; - if (typeSymbol.members) result.members = typeSymbol.members; - if (valueSymbol.exports) result.exports = valueSymbol.exports; - return result; - } - - function getExportOfModule(symbol: Symbol, name: __String, dontResolveAlias: boolean): Symbol | undefined { - if (symbol.flags & SymbolFlags.Module) { - return resolveSymbol(getExportsOfSymbol(symbol).get(name)!, dontResolveAlias); - } - } - - function getPropertyOfVariable(symbol: Symbol, name: __String): Symbol | undefined { - if (symbol.flags & SymbolFlags.Variable) { - const typeAnnotation = (symbol.valueDeclaration).type; - if (typeAnnotation) { - return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name)); - } - } - } - - function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier, dontResolveAlias = false): Symbol | undefined { - const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier!)!; // TODO: GH#18217 - const name = specifier.propertyName || specifier.name; - const suppressInteropError = name.escapedText === InternalSymbolName.Default && !!(compilerOptions.allowSyntheticDefaultImports || compilerOptions.esModuleInterop); - const targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier!, dontResolveAlias, suppressInteropError); - if (targetSymbol) { - if (name.escapedText) { - if (isShorthandAmbientModuleSymbol(moduleSymbol)) { - return moduleSymbol; - } - - let symbolFromVariable: Symbol | undefined; - // First check if module was specified with "export=". If so, get the member from the resolved type - if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get(InternalSymbolName.ExportEquals)) { - symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText); - } - else { - symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText); - } - // if symbolFromVariable is export - get its final target - symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); - let symbolFromModule = getExportOfModule(targetSymbol, name.escapedText, dontResolveAlias); - // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default - if (!symbolFromModule && allowSyntheticDefaultImports && name.escapedText === InternalSymbolName.Default) { - symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); - } - const symbol = symbolFromModule && symbolFromVariable && symbolFromModule !== symbolFromVariable ? - combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : - symbolFromModule || symbolFromVariable; - if (!symbol) { - const moduleName = getFullyQualifiedName(moduleSymbol, node); - const declarationName = declarationNameToString(name); - const suggestion = getSuggestedSymbolForNonexistentModule(name, targetSymbol); - if (suggestion !== undefined) { - const suggestionName = symbolToString(suggestion); - const diagnostic = error(name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2, moduleName, declarationName, suggestionName); - if (suggestion.valueDeclaration) { - addRelatedInfo(diagnostic, - createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName) - ); - } - } - else { - if (moduleSymbol.exports && moduleSymbol.exports.has(InternalSymbolName.Default)) { - error( - name, - Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead, - moduleName, - declarationName - ); - } - else { - error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName); - } - } - } - return symbol; - } - } - } - - function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined { - const resolved = getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); - if (resolved && node.parent.parent.isTypeOnly) { - return createTypeOnlyImportOrExport(node.name, resolved); - } - return resolved; - } - - function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol { - return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias); - } - - /** - * Creates a type alias symbol with a target symbol for type-only imports and exports. - * The symbol for `A` in `export type { A }` or `export type { A } from "./mod"` has - * `TypeFlags.Alias` so that alias resolution works as usual, but once the target `A` - * has been resolved, we essentially want to pretend we have a type alias to that target. - */ - function createTypeOnlyImportOrExport(sourceNode: ExportSpecifier | Identifier, target: Symbol) { - const symbol = createTypeOnlySymbol(target); - if (!symbol && target !== unknownSymbol) { - const identifier = isExportSpecifier(sourceNode) ? sourceNode.name : sourceNode; - const nameText = idText(identifier); - const diagnostic = error( - identifier, - Diagnostics.Type_only_0_must_reference_a_type_but_1_is_a_value, - isExportSpecifier(sourceNode) ? "export" : "import", - nameText); - const targetDeclaration = target.valueDeclaration ?? target.declarations?.[0]; - if (targetDeclaration) { - addRelatedInfo(diagnostic, createDiagnosticForNode( - targetDeclaration, - Diagnostics._0_is_declared_here, - nameText)); - } - } - - return symbol; - } - - function createTypeOnlySymbol(target: Symbol): Symbol | undefined { - if (target.flags & SymbolFlags.ValueModule) { - return createNamespaceModuleForModule(target); - } - if (target.flags & SymbolFlags.Enum) { - return createNamespaceModuleForEnum(target); - } - if (!(target.flags & SymbolFlags.Value)) { - return target; - } - if (target.flags & SymbolFlags.Type) { - const alias = createSymbol(SymbolFlags.TypeAlias, target.escapedName); - alias.declarations = emptyArray; - alias.immediateTarget = target; - return alias; - } - } - - function createNamespaceModuleForEnum(enumSymbol: Symbol) { - Debug.assert(!!(enumSymbol.flags & SymbolFlags.Enum)); - const symbol = createSymbol(SymbolFlags.NamespaceModule | SymbolFlags.TypeAlias, enumSymbol.escapedName); - symbol.immediateTarget = enumSymbol; - symbol.declarations = enumSymbol.declarations; - if (enumSymbol.exports) { - symbol.exports = createSymbolTable(); - enumSymbol.exports.forEach((exportSymbol, key) => { - symbol.exports!.set(key, Debug.assertDefined(createTypeOnlySymbol(exportSymbol))); - }); - } - return symbol; - } - - function createNamespaceModuleForModule(moduleSymbol: Symbol) { - Debug.assert(!!(moduleSymbol.flags & SymbolFlags.ValueModule)); - const filtered = createSymbol(SymbolFlags.NamespaceModule, moduleSymbol.escapedName); - filtered.declarations = moduleSymbol.declarations; - if (moduleSymbol.exports) { - filtered.exports = createSymbolTable(); - moduleSymbol.exports.forEach((exportSymbol, key) => { - const typeOnlyExport = createTypeOnlySymbol(exportSymbol); - if (typeOnlyExport) { - filtered.exports!.set(key, typeOnlyExport); - } - }); - } - return filtered; - } - - function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { - const target = node.parent.parent.moduleSpecifier ? - getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : - resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias); - return target && node.parent.parent.isTypeOnly ? createTypeOnlyImportOrExport(node, target) : target; - } - - function getTargetOfExportAssignment(node: ExportAssignment | BinaryExpression, dontResolveAlias: boolean): Symbol | undefined { - const expression = (isExportAssignment(node) ? node.expression : node.right) as EntityNameExpression | ClassExpression; - return getTargetOfAliasLikeExpression(expression, dontResolveAlias); - } - - function getTargetOfAliasLikeExpression(expression: Expression, dontResolveAlias: boolean) { - if (isClassExpression(expression)) { - return checkExpressionCached(expression).symbol; - } - if (!isEntityName(expression) && !isEntityNameExpression(expression)) { - return undefined; - } - const aliasLike = resolveEntityName(expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontResolveAlias); - if (aliasLike) { - return aliasLike; - } - checkExpressionCached(expression); - return getNodeLinks(expression).resolvedSymbol; - } - - function getTargetOfPropertyAssignment(node: PropertyAssignment, dontRecursivelyResolve: boolean): Symbol | undefined { - const expression = node.initializer; - return getTargetOfAliasLikeExpression(expression, dontRecursivelyResolve); - } - - function getTargetOfPropertyAccessExpression(node: PropertyAccessExpression, dontRecursivelyResolve: boolean): Symbol | undefined { - if (!(isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken)) { - return undefined; - } - - return getTargetOfAliasLikeExpression(node.parent.right, dontRecursivelyResolve); - } - - function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve = false): Symbol | undefined { - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - return getTargetOfImportEqualsDeclaration(node, dontRecursivelyResolve); - case SyntaxKind.ImportClause: - return getTargetOfImportClause(node, dontRecursivelyResolve); - case SyntaxKind.NamespaceImport: - return getTargetOfNamespaceImport(node, dontRecursivelyResolve); - case SyntaxKind.NamespaceExport: - return getTargetOfNamespaceExport(node, dontRecursivelyResolve); - case SyntaxKind.ImportSpecifier: - return getTargetOfImportSpecifier(node, dontRecursivelyResolve); - case SyntaxKind.ExportSpecifier: - return getTargetOfExportSpecifier(node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve); - case SyntaxKind.ExportAssignment: - case SyntaxKind.BinaryExpression: - return getTargetOfExportAssignment((node), dontRecursivelyResolve); - case SyntaxKind.NamespaceExportDeclaration: - return getTargetOfNamespaceExportDeclaration(node, dontRecursivelyResolve); - case SyntaxKind.ShorthandPropertyAssignment: - return resolveEntityName((node as ShorthandPropertyAssignment).name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontRecursivelyResolve); - case SyntaxKind.PropertyAssignment: - return getTargetOfPropertyAssignment(node as PropertyAssignment, dontRecursivelyResolve); - case SyntaxKind.PropertyAccessExpression: - return getTargetOfPropertyAccessExpression(node as PropertyAccessExpression, dontRecursivelyResolve); - default: - return Debug.fail(); - } - } - - /** - * Indicates that a symbol is an alias that does not merge with a local declaration. - * OR Is a JSContainer which may merge an alias with a local declaration - */ - function isNonLocalAlias(symbol: Symbol | undefined, excludes = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace): symbol is Symbol { - if (!symbol) return false; - return (symbol.flags & (SymbolFlags.Alias | excludes)) === SymbolFlags.Alias || !!(symbol.flags & SymbolFlags.Alias && symbol.flags & SymbolFlags.Assignment); - } - - function resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol; - function resolveSymbol(symbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined; - function resolveSymbol(symbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined { - return !dontResolveAlias && isNonLocalAlias(symbol) ? resolveAlias(symbol) : symbol; - } - - function resolveAlias(symbol: Symbol): Symbol { - Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here."); - const links = getSymbolLinks(symbol); - if (!links.target) { - links.target = resolvingSymbol; - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - const target = getTargetOfAliasDeclaration(node); - if (links.target === resolvingSymbol) { - links.target = target || unknownSymbol; - } - else { - error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol)); - } - } - else if (links.target === resolvingSymbol) { - links.target = unknownSymbol; - } - return links.target; - } - - function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) { - const symbol = getSymbolOfNode(node); - const target = resolveAlias(symbol); - if (target) { - const markAlias = target === unknownSymbol || - ((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target)); - - if (markAlias) { - markAliasSymbolAsReferenced(symbol); - } - } - } - - // When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until - // we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of - // the alias as an expression (which recursively takes us back here if the target references another alias). - function markAliasSymbolAsReferenced(symbol: Symbol) { - const links = getSymbolLinks(symbol); - if (!links.referenced) { - links.referenced = true; - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - // We defer checking of the reference of an `import =` until the import itself is referenced, - // This way a chain of imports can be elided if ultimately the final input is only used in a type - // position. - if (isInternalModuleImportEqualsDeclaration(node)) { - const target = resolveSymbol(symbol); - if (target === unknownSymbol || target.flags & SymbolFlags.Value) { - // import foo = - checkExpressionCached(node.moduleReference); - } - } - } - } - - // This function is only for imports with entity names - function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, dontResolveAlias?: boolean): Symbol | undefined { - // There are three things we might try to look for. In the following examples, - // the search term is enclosed in |...|: - // - // import a = |b|; // Namespace - // import a = |b.c|; // Value, type, namespace - // import a = |b.c|.d; // Namespace - if (entityName.kind === SyntaxKind.Identifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName)) { - entityName = entityName.parent; - } - // Check for case 1 and 3 in the above example - if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) { - return resolveEntityName(entityName, SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); - } - else { - // Case 2 in above example - // entityName.kind could be a QualifiedName or a Missing identifier - Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration); - return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); - } - } - - function getFullyQualifiedName(symbol: Symbol, containingLocation?: Node): string { - return symbol.parent ? getFullyQualifiedName(symbol.parent, containingLocation) + "." + symbolToString(symbol) : symbolToString(symbol, containingLocation, /*meaning*/ undefined, SymbolFormatFlags.DoNotIncludeSymbolChain | SymbolFormatFlags.AllowAnyNodeKind); - } - - /** - * Resolves a qualified name and any involved aliases. - */ - function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean, location?: Node): Symbol | undefined { - if (nodeIsMissing(name)) { - return undefined; - } - - const namespaceMeaning = SymbolFlags.Namespace | (isInJSFile(name) ? meaning & SymbolFlags.Value : 0); - let symbol: Symbol | undefined; - if (name.kind === SyntaxKind.Identifier) { - const message = meaning === namespaceMeaning ? Diagnostics.Cannot_find_namespace_0 : getCannotFindNameDiagnosticForName(getFirstIdentifier(name)); - const symbolFromJSPrototype = isInJSFile(name) ? resolveEntityNameFromAssignmentDeclaration(name, meaning) : undefined; - symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, name, /*isUse*/ true); - if (!symbol) { - return symbolFromJSPrototype; - } - } - else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { - const left = name.kind === SyntaxKind.QualifiedName ? name.left : name.expression; - const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name; - let namespace = resolveEntityName(left, namespaceMeaning, ignoreErrors, /*dontResolveAlias*/ false, location); - if (!namespace || nodeIsMissing(right)) { - return undefined; - } - else if (namespace === unknownSymbol) { - return namespace; - } - if (isInJSFile(name)) { - if (namespace.valueDeclaration && - isVariableDeclaration(namespace.valueDeclaration) && - namespace.valueDeclaration.initializer && - isCommonJsRequire(namespace.valueDeclaration.initializer)) { - const moduleName = (namespace.valueDeclaration.initializer as CallExpression).arguments[0] as StringLiteral; - const moduleSym = resolveExternalModuleName(moduleName, moduleName); - if (moduleSym) { - const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym); - if (resolvedModuleSymbol) { - namespace = resolvedModuleSymbol; - } - } - } - } - symbol = getSymbol(getExportsOfSymbol(namespace), right.escapedText, meaning); - if (!symbol) { - if (!ignoreErrors) { - error(right, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right)); - } - return undefined; - } - } - else { - throw Debug.assertNever(name, "Unknown entity name kind."); - } - Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); - return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); - } - - /** - * 1. For prototype-property methods like `A.prototype.m = function () ...`, try to resolve names in the scope of `A` too. - * Note that prototype-property assignment to locations outside the current file (eg globals) doesn't work, so - * name resolution won't work either. - * 2. For property assignments like `{ x: function f () { } }`, try to resolve names in the scope of `f` too. - */ - function resolveEntityNameFromAssignmentDeclaration(name: Identifier, meaning: SymbolFlags) { - if (isJSDocTypeReference(name.parent)) { - const secondaryLocation = getAssignmentDeclarationLocation(name.parent); - if (secondaryLocation) { - return resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true); - } - } - } - - function getAssignmentDeclarationLocation(node: TypeReferenceNode): Node | undefined { - const typeAlias = findAncestor(node, node => !(isJSDocNode(node) || node.flags & NodeFlags.JSDoc) ? "quit" : isJSDocTypeAlias(node)); - if (typeAlias) { - return; - } - const host = getJSDocHost(node); - if (isExpressionStatement(host) && - isBinaryExpression(host.expression) && - getAssignmentDeclarationKind(host.expression) === AssignmentDeclarationKind.PrototypeProperty) { - // X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration - const symbol = getSymbolOfNode(host.expression.left); - if (symbol) { - return getDeclarationOfJSPrototypeContainer(symbol); - } - } - if ((isObjectLiteralMethod(host) || isPropertyAssignment(host)) && - isBinaryExpression(host.parent.parent) && - getAssignmentDeclarationKind(host.parent.parent) === AssignmentDeclarationKind.Prototype) { - // X.prototype = { /** @param {K} p */m() { } } <-- look for K on X's declaration - const symbol = getSymbolOfNode(host.parent.parent.left); - if (symbol) { - return getDeclarationOfJSPrototypeContainer(symbol); - } - } - const sig = getHostSignatureFromJSDocHost(host); - if (sig) { - const symbol = getSymbolOfNode(sig); - return symbol && symbol.valueDeclaration; - } - } - - function getDeclarationOfJSPrototypeContainer(symbol: Symbol) { - const decl = symbol.parent!.valueDeclaration; - if (!decl) { - return undefined; - } - const initializer = isAssignmentDeclaration(decl) ? getAssignedExpandoInitializer(decl) : - hasOnlyExpressionInitializer(decl) ? getDeclaredExpandoInitializer(decl) : - undefined; - return initializer || decl; - } - - /** - * Get the real symbol of a declaration with an expando initializer. - * - * Normally, declarations have an associated symbol, but when a declaration has an expando - * initializer, the expando's symbol is the one that has all the members merged into it. - */ - function getExpandoSymbol(symbol: Symbol): Symbol | undefined { - const decl = symbol.valueDeclaration; - if (!decl || !isInJSFile(decl) || symbol.flags & SymbolFlags.TypeAlias) { - return undefined; - } - const init = isVariableDeclaration(decl) ? getDeclaredExpandoInitializer(decl) : getAssignedExpandoInitializer(decl); - if (init) { - const initSymbol = getSymbolOfNode(init); - if (initSymbol) { - return mergeJSSymbols(initSymbol, symbol); - } - } - } - - - function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression, ignoreErrors?: boolean): Symbol | undefined { - return resolveExternalModuleNameWorker(location, moduleReferenceExpression, ignoreErrors ? undefined : Diagnostics.Cannot_find_module_0); - } - - function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage | undefined, isForAugmentation = false): Symbol | undefined { - return isStringLiteralLike(moduleReferenceExpression) - ? resolveExternalModule(location, moduleReferenceExpression.text, moduleNotFoundError, moduleReferenceExpression, isForAugmentation) - : undefined; - } - - function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node, isForAugmentation = false): Symbol | undefined { - if (startsWith(moduleReference, "@types/")) { - const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1; - const withoutAtTypePrefix = removePrefix(moduleReference, "@types/"); - error(errorNode, diag, withoutAtTypePrefix, moduleReference); - } - - const ambientModule = tryFindAmbientModule(moduleReference, /*withAugmentations*/ true); - if (ambientModule) { - return ambientModule; - } - const currentSourceFile = getSourceFileOfNode(location); - const resolvedModule = getResolvedModule(currentSourceFile, moduleReference)!; // TODO: GH#18217 - const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule); - const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName); - if (sourceFile) { - if (sourceFile.symbol) { - if (resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) { - errorOnImplicitAnyModule(/*isError*/ false, errorNode, resolvedModule, moduleReference); - } - // merged symbol is module declaration symbol combined with all augmentations - return getMergedSymbol(sourceFile.symbol); - } - if (moduleNotFoundError) { - // report errors only if it was requested - error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName); - } - return undefined; - } - - if (patternAmbientModules) { - const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference); - if (pattern) { - // If the module reference matched a pattern ambient module ('*.foo') but there's also a - // module augmentation by the specific name requested ('a.foo'), we store the merged symbol - // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports - // from a.foo. - const augmentation = patternAmbientModuleAugmentations && patternAmbientModuleAugmentations.get(moduleReference); - if (augmentation) { - return getMergedSymbol(augmentation); - } - return getMergedSymbol(pattern.symbol); - } - } - - // May be an untyped module. If so, ignore resolutionDiagnostic. - if (resolvedModule && !resolutionExtensionIsTSOrJson(resolvedModule.extension) && resolutionDiagnostic === undefined || resolutionDiagnostic === Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) { - if (isForAugmentation) { - const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented; - error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName); - } - else { - errorOnImplicitAnyModule(/*isError*/ noImplicitAny && !!moduleNotFoundError, errorNode, resolvedModule, moduleReference); - } - // Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first. - return undefined; - } - - if (moduleNotFoundError) { - // See if this was possibly a projectReference redirect - if (resolvedModule) { - const redirect = host.getProjectReferenceRedirect(resolvedModule.resolvedFileName); - if (redirect) { - error(errorNode, Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, resolvedModule.resolvedFileName); - return undefined; - } - } - - if (resolutionDiagnostic) { - error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); - } - else { - const tsExtension = tryExtractTSExtension(moduleReference); - if (tsExtension) { - const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead; - error(errorNode, diag, tsExtension, removeExtension(moduleReference, tsExtension)); - } - else if (!compilerOptions.resolveJsonModule && - fileExtensionIs(moduleReference, Extension.Json) && - getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeJs && - hasJsonModuleEmitEnabled(compilerOptions)) { - error(errorNode, Diagnostics.Cannot_find_module_0_Consider_using_resolveJsonModule_to_import_module_with_json_extension, moduleReference); - } - else { - error(errorNode, moduleNotFoundError, moduleReference); - } - } - } - return undefined; - } - - function errorOnImplicitAnyModule(isError: boolean, errorNode: Node, { packageId, resolvedFileName }: ResolvedModuleFull, moduleReference: string): void { - const errorInfo = !isExternalModuleNameRelative(moduleReference) && packageId - ? typesPackageExists(packageId.name) - ? chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, - packageId.name, mangleScopedPackageName(packageId.name)) - : chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics.Try_npm_install_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, - moduleReference, - mangleScopedPackageName(packageId.name)) - : undefined; - errorOrSuggestion(isError, errorNode, chainDiagnosticMessages( - errorInfo, - Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type, - moduleReference, - resolvedFileName)); - } - function typesPackageExists(packageName: string): boolean { - return getPackagesSet().has(getTypesPackageName(packageName)); - } - - function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol; - function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined; - function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol { - if (moduleSymbol) { - const exportEquals = resolveSymbol(moduleSymbol.exports!.get(InternalSymbolName.ExportEquals), dontResolveAlias); - const exported = getCommonJsExportEquals(getMergedSymbol(exportEquals), getMergedSymbol(moduleSymbol)); - return getMergedSymbol(exported) || moduleSymbol; - } - return undefined!; - } - - function getCommonJsExportEquals(exported: Symbol | undefined, moduleSymbol: Symbol): Symbol | undefined { - if (!exported || exported === unknownSymbol || exported === moduleSymbol || moduleSymbol.exports!.size === 1 || exported.flags & SymbolFlags.Alias) { - return exported; - } - const links = getSymbolLinks(exported); - if (links.cjsExportMerged) { - return links.cjsExportMerged; - } - const merged = exported.flags & SymbolFlags.Transient ? exported : cloneSymbol(exported); - merged.flags = merged.flags | SymbolFlags.ValueModule; - if (merged.exports === undefined) { - merged.exports = createSymbolTable(); - } - moduleSymbol.exports!.forEach((s, name) => { - if (name === InternalSymbolName.ExportEquals) return; - merged.exports!.set(name, merged.exports!.has(name) ? mergeSymbol(merged.exports!.get(name)!, s) : s); - }); - getSymbolLinks(merged).cjsExportMerged = merged; - return links.cjsExportMerged = merged; - } - - // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' - // references a symbol that is at least declared as a module or a variable. The target of the 'export =' may - // combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable). - function resolveESModuleSymbol(moduleSymbol: Symbol | undefined, referencingLocation: Node, dontResolveAlias: boolean, suppressInteropError: boolean): Symbol | undefined { - const symbol = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias); - - if (!dontResolveAlias && symbol) { - if (!suppressInteropError && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable)) && !getDeclarationOfKind(symbol, SyntaxKind.SourceFile)) { - const compilerOptionName = moduleKind >= ModuleKind.ES2015 - ? "allowSyntheticDefaultImports" - : "esModuleInterop"; - - error(referencingLocation, Diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, compilerOptionName); - - return symbol; - } - - if (compilerOptions.esModuleInterop) { - const referenceParent = referencingLocation.parent; - if ( - (isImportDeclaration(referenceParent) && getNamespaceDeclarationNode(referenceParent)) || - isImportCall(referenceParent) - ) { - const type = getTypeOfSymbol(symbol); - let sigs = getSignaturesOfStructuredType(type, SignatureKind.Call); - if (!sigs || !sigs.length) { - sigs = getSignaturesOfStructuredType(type, SignatureKind.Construct); - } - if (sigs && sigs.length) { - const moduleType = getTypeWithSyntheticDefaultImportType(type, symbol, moduleSymbol!); - // Create a new symbol which has the module's type less the call and construct signatures - const result = createSymbol(symbol.flags, symbol.escapedName); - result.declarations = symbol.declarations ? symbol.declarations.slice() : []; - result.parent = symbol.parent; - result.target = symbol; - result.originatingImport = referenceParent; - if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; - if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; - if (symbol.members) result.members = cloneMap(symbol.members); - if (symbol.exports) result.exports = cloneMap(symbol.exports); - const resolvedModuleType = resolveStructuredTypeMembers(moduleType as StructuredType); // Should already be resolved from the signature checks above - result.type = createAnonymousType(result, resolvedModuleType.members, emptyArray, emptyArray, resolvedModuleType.stringIndexInfo, resolvedModuleType.numberIndexInfo); - return result; - } - } - } - } - return symbol; - } - - function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports!.get(InternalSymbolName.ExportEquals) !== undefined; - } - - function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { - return symbolsToArray(getExportsOfModule(moduleSymbol)); - } - - function getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] { - const exports = getExportsOfModuleAsArray(moduleSymbol); - const exportEquals = resolveExternalModuleSymbol(moduleSymbol); - if (exportEquals !== moduleSymbol) { - addRange(exports, getPropertiesOfType(getTypeOfSymbol(exportEquals))); - } - return exports; - } - - function tryGetMemberInModuleExports(memberName: __String, moduleSymbol: Symbol): Symbol | undefined { - const symbolTable = getExportsOfModule(moduleSymbol); - if (symbolTable) { - return symbolTable.get(memberName); - } - } - - function tryGetMemberInModuleExportsAndProperties(memberName: __String, moduleSymbol: Symbol): Symbol | undefined { - const symbol = tryGetMemberInModuleExports(memberName, moduleSymbol); - if (symbol) { - return symbol; - } - - const exportEquals = resolveExternalModuleSymbol(moduleSymbol); - if (exportEquals === moduleSymbol) { - return undefined; - } - - const type = getTypeOfSymbol(exportEquals); - return type.flags & TypeFlags.Primitive || - getObjectFlags(type) & ObjectFlags.Class || - isArrayOrTupleLikeType(type) - ? undefined - : getPropertyOfType(type, memberName); - } - - function getExportsOfSymbol(symbol: Symbol): SymbolTable { - return symbol.flags & SymbolFlags.LateBindingContainer ? getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKind.resolvedExports) : - symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : - symbol.exports || emptySymbols; - } - - function getExportsOfModule(moduleSymbol: Symbol): SymbolTable { - const links = getSymbolLinks(moduleSymbol); - return links.resolvedExports || (links.resolvedExports = getExportsOfModuleWorker(moduleSymbol)); - } - - interface ExportCollisionTracker { - specifierText: string; - exportsWithDuplicate: ExportDeclaration[]; - } - - type ExportCollisionTrackerTable = UnderscoreEscapedMap; - - /** - * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument - * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables - */ - function extendExportSymbols(target: SymbolTable, source: SymbolTable | undefined, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) { - if (!source) return; - source.forEach((sourceSymbol, id) => { - if (id === InternalSymbolName.Default) return; - - const targetSymbol = target.get(id); - if (!targetSymbol) { - target.set(id, sourceSymbol); - if (lookupTable && exportNode) { - lookupTable.set(id, { - specifierText: getTextOfNode(exportNode.moduleSpecifier!) - } as ExportCollisionTracker); - } - } - else if (lookupTable && exportNode && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { - const collisionTracker = lookupTable.get(id)!; - if (!collisionTracker.exportsWithDuplicate) { - collisionTracker.exportsWithDuplicate = [exportNode]; - } - else { - collisionTracker.exportsWithDuplicate.push(exportNode); - } - } - }); - } - - function getExportsOfModuleWorker(moduleSymbol: Symbol): SymbolTable { - const visitedSymbols: Symbol[] = []; - - // A module defined by an 'export=' consists of one export that needs to be resolved - moduleSymbol = resolveExternalModuleSymbol(moduleSymbol); - - return visit(moduleSymbol) || emptySymbols; - - // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, - // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. - function visit(symbol: Symbol | undefined): SymbolTable | undefined { - if (!(symbol && symbol.exports && pushIfUnique(visitedSymbols, symbol))) { - return; - } - const symbols = cloneMap(symbol.exports); - // All export * declarations are collected in an __export symbol by the binder - const exportStars = symbol.exports.get(InternalSymbolName.ExportStar); - if (exportStars) { - const nestedSymbols = createSymbolTable(); - const lookupTable = createMap() as ExportCollisionTrackerTable; - for (const node of exportStars.declarations) { - const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); - const exportedSymbols = visit(resolvedModule); - extendExportSymbols( - nestedSymbols, - exportedSymbols, - lookupTable, - node as ExportDeclaration - ); - } - lookupTable.forEach(({ exportsWithDuplicate }, id) => { - // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.has(id)) { - return; - } - for (const node of exportsWithDuplicate) { - diagnostics.add(createDiagnosticForNode( - node, - Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, - lookupTable.get(id)!.specifierText, - unescapeLeadingUnderscores(id) - )); - } - }); - extendExportSymbols(symbols, nestedSymbols); - } - return symbols; - } - } - - function getMergedSymbol(symbol: Symbol): Symbol; - function getMergedSymbol(symbol: Symbol | undefined): Symbol | undefined; - function getMergedSymbol(symbol: Symbol | undefined): Symbol | undefined { - let merged: Symbol; - return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol; - } - - function getSymbolOfNode(node: Declaration): Symbol; - function getSymbolOfNode(node: Node): Symbol | undefined; - function getSymbolOfNode(node: Node): Symbol | undefined { - return getMergedSymbol(node.symbol && getLateBoundSymbol(node.symbol)); - } - - function getParentOfSymbol(symbol: Symbol): Symbol | undefined { - return getMergedSymbol(symbol.parent && getLateBoundSymbol(symbol.parent)); - } - - function getAlternativeContainingModules(symbol: Symbol, enclosingDeclaration: Node): Symbol[] { - const containingFile = getSourceFileOfNode(enclosingDeclaration); - const id = "" + getNodeId(containingFile); - const links = getSymbolLinks(symbol); - let results: Symbol[] | undefined; - if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(id))) { - return results; - } - if (containingFile && containingFile.imports) { - // Try to make an import using an import already in the enclosing file, if possible - for (const importRef of containingFile.imports) { - if (nodeIsSynthesized(importRef)) continue; // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error - const resolvedModule = resolveExternalModuleName(enclosingDeclaration, importRef, /*ignoreErrors*/ true); - if (!resolvedModule) continue; - const ref = getAliasForSymbolInContainer(resolvedModule, symbol); - if (!ref) continue; - results = append(results, resolvedModule); - } - if (length(results)) { - (links.extendedContainersByFile || (links.extendedContainersByFile = createMap())).set(id, results!); - return results!; - } - } - if (links.extendedContainers) { - return links.extendedContainers; - } - // No results from files already being imported by this file - expand search (expensive, but not location-specific, so cached) - const otherFiles = host.getSourceFiles(); - for (const file of otherFiles) { - if (!isExternalModule(file)) continue; - const sym = getSymbolOfNode(file); - const ref = getAliasForSymbolInContainer(sym, symbol); - if (!ref) continue; - results = append(results, sym); - } - return links.extendedContainers = results || emptyArray; - } - - /** - * Attempts to find the symbol corresponding to the container a symbol is in - usually this - * is just its' `.parent`, but for locals, this value is `undefined` - */ - function getContainersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined): Symbol[] | undefined { - const container = getParentOfSymbol(symbol); - // Type parameters end up in the `members` lists but are not externally visible - if (container && !(symbol.flags & SymbolFlags.TypeParameter)) { - const additionalContainers = mapDefined(container.declarations, fileSymbolIfFileSymbolExportEqualsContainer); - const reexportContainers = enclosingDeclaration && getAlternativeContainingModules(symbol, enclosingDeclaration); - if (enclosingDeclaration && getAccessibleSymbolChain(container, enclosingDeclaration, SymbolFlags.Namespace, /*externalOnly*/ false)) { - return concatenate(concatenate([container], additionalContainers), reexportContainers); // This order expresses a preference for the real container if it is in scope - } - const res = append(additionalContainers, container); - return concatenate(res, reexportContainers); - } - const candidates = mapDefined(symbol.declarations, d => { - if (!isAmbientModule(d) && d.parent && hasNonGlobalAugmentationExternalModuleSymbol(d.parent)) { - return getSymbolOfNode(d.parent); - } - if (isClassExpression(d) && isBinaryExpression(d.parent) && d.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAccessExpression(d.parent.left) && isEntityNameExpression(d.parent.left.expression)) { - if (isModuleExportsAccessExpression(d.parent.left) || isExportsIdentifier(d.parent.left.expression)) { - return getSymbolOfNode(getSourceFileOfNode(d)); - } - checkExpressionCached(d.parent.left.expression); - return getNodeLinks(d.parent.left.expression).resolvedSymbol; - } - }); - if (!length(candidates)) { - return undefined; - } - return mapDefined(candidates, candidate => getAliasForSymbolInContainer(candidate, symbol) ? candidate : undefined); - - function fileSymbolIfFileSymbolExportEqualsContainer(d: Declaration) { - const fileSymbol = getExternalModuleContainer(d); - const exported = fileSymbol && fileSymbol.exports && fileSymbol.exports.get(InternalSymbolName.ExportEquals); - return exported && container && getSymbolIfSameReference(exported, container) ? fileSymbol : undefined; - } - } - - function getAliasForSymbolInContainer(container: Symbol, symbol: Symbol) { - if (container === getParentOfSymbol(symbol)) { - // fast path, `symbol` is either already the alias or isn't aliased - return symbol; - } - // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return - // the container itself as the alias for the symbol - const exportEquals = container.exports && container.exports.get(InternalSymbolName.ExportEquals); - if (exportEquals && getSymbolIfSameReference(exportEquals, symbol)) { - return container; - } - const exports = getExportsOfSymbol(container); - const quick = exports.get(symbol.escapedName); - if (quick && getSymbolIfSameReference(quick, symbol)) { - return quick; - } - return forEachEntry(exports, exported => { - if (getSymbolIfSameReference(exported, symbol)) { - return exported; - } - }); - } - - /** - * Checks if two symbols, through aliasing and/or merging, refer to the same thing - */ - function getSymbolIfSameReference(s1: Symbol, s2: Symbol) { - if (getMergedSymbol(resolveSymbol(getMergedSymbol(s1))) === getMergedSymbol(resolveSymbol(getMergedSymbol(s2)))) { - return s1; - } - } - - function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol; - function getExportSymbolOfValueSymbolIfExported(symbol: Symbol | undefined): Symbol | undefined; - function getExportSymbolOfValueSymbolIfExported(symbol: Symbol | undefined): Symbol | undefined { - return getMergedSymbol(symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0 ? symbol.exportSymbol : symbol); - } - - function symbolIsValue(symbol: Symbol): boolean { - return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value); - } - - function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined { - const members = node.members; - for (const member of members) { - if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member).body)) { - return member; - } - } - } - - function createType(flags: TypeFlags): Type { - const result = new Type(checker, flags); - typeCount++; - result.id = typeCount; - return result; - } - - function createIntrinsicType(kind: TypeFlags, intrinsicName: string, objectFlags: ObjectFlags = 0): IntrinsicType { - const type = createType(kind); - type.intrinsicName = intrinsicName; - type.objectFlags = objectFlags; - return type; - } - - function createBooleanType(trueFalseTypes: readonly Type[]): IntrinsicType & UnionType { - const type = getUnionType(trueFalseTypes); - type.flags |= TypeFlags.Boolean; - type.intrinsicName = "boolean"; - return type; - } - - function createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType { - const type = createType(TypeFlags.Object); - type.objectFlags = objectFlags; - type.symbol = symbol!; - type.members = undefined; - type.properties = undefined; - type.callSignatures = undefined; - type.constructSignatures = undefined; - type.stringIndexInfo = undefined; - type.numberIndexInfo = undefined; - return type; - } - - function createTypeofType() { - return getUnionType(arrayFrom(typeofEQFacts.keys(), getLiteralType)); - } - - function createTypeParameter(symbol?: Symbol) { - const type = createType(TypeFlags.TypeParameter); - if (symbol) type.symbol = symbol; - return type; - } - - // A reserved member name starts with two underscores, but the third character cannot be an underscore, - // @, or #. A third underscore indicates an escaped form of an identifier that started - // with at least two underscores. The @ character indicates that the name is denoted by a well known ES - // Symbol instance and the # character indicates that the name is a PrivateIdentifier. - function isReservedMemberName(name: __String) { - return (name as string).charCodeAt(0) === CharacterCodes._ && - (name as string).charCodeAt(1) === CharacterCodes._ && - (name as string).charCodeAt(2) !== CharacterCodes._ && - (name as string).charCodeAt(2) !== CharacterCodes.at && - (name as string).charCodeAt(2) !== CharacterCodes.hash; - } - - function getNamedMembers(members: SymbolTable): Symbol[] { - let result: Symbol[] | undefined; - members.forEach((symbol, id) => { - if (!isReservedMemberName(id) && symbolIsValue(symbol)) { - (result || (result = [])).push(symbol); - } - }); - return result || emptyArray; - } - - function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: readonly Signature[], constructSignatures: readonly Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): ResolvedType { - (type).members = members; - (type).properties = members === emptySymbols ? emptyArray : getNamedMembers(members); - (type).callSignatures = callSignatures; - (type).constructSignatures = constructSignatures; - (type).stringIndexInfo = stringIndexInfo; - (type).numberIndexInfo = numberIndexInfo; - return type; - } - - function createAnonymousType(symbol: Symbol | undefined, members: SymbolTable, callSignatures: readonly Signature[], constructSignatures: readonly Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): ResolvedType { - return setStructuredTypeMembers(createObjectType(ObjectFlags.Anonymous, symbol), - members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); - } - - function forEachSymbolTableInScope(enclosingDeclaration: Node | undefined, callback: (symbolTable: SymbolTable) => T): T { - let result: T; - for (let location = enclosingDeclaration; location; location = location.parent) { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if (location.locals && !isGlobalSourceFile(location)) { - if (result = callback(location.locals)) { - return result; - } - } - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule(location)) { - break; - } - // falls through - case SyntaxKind.ModuleDeclaration: - const sym = getSymbolOfNode(location as ModuleDeclaration); - // `sym` may not have exports if this module declaration is backed by the symbol for a `const` that's being rewritten - // into a namespace - in such cases, it's best to just let the namespace appear empty (the const members couldn't have referred - // to one another anyway) - if (result = callback(sym.exports || emptySymbols)) { - return result; - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - // Type parameters are bound into `members` lists so they can merge across declarations - // This is troublesome, since in all other respects, they behave like locals :cries: - // TODO: the below is shared with similar code in `resolveName` - in fact, rephrasing all this symbol - // lookup logic in terms of `resolveName` would be nice - // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals - // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would - // trigger resolving late-bound names, which we may already be in the process of doing while we're here! - let table: UnderscoreEscapedMap | undefined; - // TODO: Should this filtered table be cached in some way? - (getSymbolOfNode(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols).forEach((memberSymbol, key) => { - if (memberSymbol.flags & (SymbolFlags.Type & ~SymbolFlags.Assignment)) { - (table || (table = createSymbolTable())).set(key, memberSymbol); - } - }); - if (table && (result = callback(table))) { - return result; - } - break; - } - } - - return callback(globals); - } - - function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) { - // If we are looking in value space, the parent meaning is value, other wise it is namespace - return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace; - } - - function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap: Map = createMap()): Symbol[] | undefined { - if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) { - return undefined; - } - - const id = "" + getSymbolId(symbol); - let visitedSymbolTables = visitedSymbolTablesMap.get(id); - if (!visitedSymbolTables) { - visitedSymbolTablesMap.set(id, visitedSymbolTables = []); - } - return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable); - - /** - * @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) - */ - function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean): Symbol[] | undefined { - if (!pushIfUnique(visitedSymbolTables!, symbols)) { - return undefined; - } - - const result = trySymbolTable(symbols, ignoreQualification); - visitedSymbolTables!.pop(); - return result; - } - - function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) { - // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible - return !needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning) || - // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too - !!getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing, visitedSymbolTablesMap); - } - - function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol, ignoreQualification?: boolean) { - return (symbol === (resolvedAliasSymbol || symbolFromSymbolTable) || getMergedSymbol(symbol) === getMergedSymbol(resolvedAliasSymbol || symbolFromSymbolTable)) && - // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) - // and if symbolFromSymbolTable or alias resolution matches the symbol, - // check the symbol can be qualified, it is only then this symbol is accessible - !some(symbolFromSymbolTable.declarations, hasNonGlobalAugmentationExternalModuleSymbol) && - (ignoreQualification || canQualifySymbol(getMergedSymbol(symbolFromSymbolTable), meaning)); - } - - function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined): Symbol[] | undefined { - // If symbol is directly available by its name in the symbol table - if (isAccessible(symbols.get(symbol!.escapedName)!, /*resolvedAliasSymbol*/ undefined, ignoreQualification)) { - return [symbol!]; - } - - // Check if symbol is any of the aliases in scope - const result = forEachEntry(symbols, symbolFromSymbolTable => { - if (symbolFromSymbolTable.flags & SymbolFlags.Alias - && symbolFromSymbolTable.escapedName !== InternalSymbolName.ExportEquals - && symbolFromSymbolTable.escapedName !== InternalSymbolName.Default - && !(isUMDExportSymbol(symbolFromSymbolTable) && enclosingDeclaration && isExternalModule(getSourceFileOfNode(enclosingDeclaration))) - // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name - && (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) - // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ - // See similar comment in `resolveName` for details - && (ignoreQualification || !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) - ) { - - const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable); - const candidate = getCandidateListForSymbol(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification); - if (candidate) { - return candidate; - } - } - if (symbolFromSymbolTable.escapedName === symbol!.escapedName && symbolFromSymbolTable.exportSymbol) { - if (isAccessible(getMergedSymbol(symbolFromSymbolTable.exportSymbol), /*aliasSymbol*/ undefined, ignoreQualification)) { - return [symbol!]; - } - } - }); - - // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that - return result || (symbols === globals ? getCandidateListForSymbol(globalThisSymbol, globalThisSymbol, ignoreQualification) : undefined); - } - - function getCandidateListForSymbol(symbolFromSymbolTable: Symbol, resolvedImportedSymbol: Symbol, ignoreQualification: boolean | undefined) { - if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification)) { - return [symbolFromSymbolTable]; - } - - // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain - // but only if the symbolFromSymbolTable can be qualified - const candidateTable = getExportsOfSymbol(resolvedImportedSymbol); - const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable, /*ignoreQualification*/ true); - if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) { - return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); - } - } - } - - function needsQualification(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags) { - let qualify = false; - forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { - // If symbol of this name is not available in the symbol table we are ok - let symbolFromSymbolTable = getMergedSymbol(symbolTable.get(symbol.escapedName)); - if (!symbolFromSymbolTable) { - // Continue to the next symbol table - return false; - } - // If the symbol with this name is present it should refer to the symbol - if (symbolFromSymbolTable === symbol) { - // No need to qualify - return true; - } - - // Qualify if the symbol from symbol table has same meaning as expected - symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; - if (symbolFromSymbolTable.flags & meaning) { - qualify = true; - return true; - } - - // Continue to the next symbol table - return false; - }); - - return qualify; - } - - function isPropertyOrMethodDeclarationSymbol(symbol: Symbol) { - if (symbol.declarations && symbol.declarations.length) { - for (const declaration of symbol.declarations) { - switch (declaration.kind) { - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - continue; - default: - return false; - } - } - return true; - } - return false; - } - - function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean { - const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false); - return access.accessibility === SymbolAccessibility.Accessible; - } - - function isValueSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean { - const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ false); - return access.accessibility === SymbolAccessibility.Accessible; - } - - function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult | undefined { - if (!length(symbols)) return; - - let hadAccessibleChain: Symbol | undefined; - for (const symbol of symbols!) { - // Symbol is accessible if it by itself is accessible - const accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/ false); - if (accessibleSymbolChain) { - hadAccessibleChain = symbol; - const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible); - if (hasAccessibleDeclarations) { - return hasAccessibleDeclarations; - } - } - else { - if (some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { - // Any meaning of a module symbol is always accessible via an `import` type - return { - accessibility: SymbolAccessibility.Accessible - }; - } - } - - // If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible. - // It could be a qualified symbol and hence verify the path - // e.g.: - // module m { - // export class c { - // } - // } - // const x: typeof m.c - // In the above example when we start with checking if typeof m.c symbol is accessible, - // we are going to see if c can be accessed in scope directly. - // But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible - // It is accessible if the parent m is accessible because then m.c can be accessed through qualification - - let containers = getContainersOfSymbol(symbol, enclosingDeclaration); - // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct - // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, - // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. - const firstDecl: Node = first(symbol.declarations); - if (!length(containers) && meaning & SymbolFlags.Value && firstDecl && isObjectLiteralExpression(firstDecl)) { - if (firstDecl.parent && isVariableDeclaration(firstDecl.parent) && firstDecl === firstDecl.parent.initializer) { - containers = [getSymbolOfNode(firstDecl.parent)]; - } - } - const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible); - if (parentResult) { - return parentResult; - } - } - - if (hadAccessibleChain) { - return { - accessibility: SymbolAccessibility.NotAccessible, - errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning), - errorModuleName: hadAccessibleChain !== initialSymbol ? symbolToString(hadAccessibleChain, enclosingDeclaration, SymbolFlags.Namespace) : undefined, - }; - } - } - - /** - * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested - * - * @param symbol a Symbol to check if accessible - * @param enclosingDeclaration a Node containing reference to the symbol - * @param meaning a SymbolFlags to check if such meaning of the symbol is accessible - * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible - */ - function isSymbolAccessible(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult { - if (symbol && enclosingDeclaration) { - const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible); - if (result) { - return result; - } - - // This could be a symbol that is not exported in the external module - // or it could be a symbol from different external module that is not aliased and hence cannot be named - const symbolExternalModule = forEach(symbol.declarations, getExternalModuleContainer); - if (symbolExternalModule) { - const enclosingExternalModule = getExternalModuleContainer(enclosingDeclaration); - if (symbolExternalModule !== enclosingExternalModule) { - // name from different external module that is not visible - return { - accessibility: SymbolAccessibility.CannotBeNamed, - errorSymbolName: symbolToString(symbol, enclosingDeclaration, meaning), - errorModuleName: symbolToString(symbolExternalModule) - }; - } - } - - // Just a local name that is not accessible - return { - accessibility: SymbolAccessibility.NotAccessible, - errorSymbolName: symbolToString(symbol, enclosingDeclaration, meaning), - }; - } - - return { accessibility: SymbolAccessibility.Accessible }; - } - - function getExternalModuleContainer(declaration: Node) { - const node = findAncestor(declaration, hasExternalModuleSymbol); - return node && getSymbolOfNode(node); - } - - function hasExternalModuleSymbol(declaration: Node) { - return isAmbientModule(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(declaration)); - } - - function hasNonGlobalAugmentationExternalModuleSymbol(declaration: Node) { - return isModuleWithStringLiteralName(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(declaration)); - } - - function hasVisibleDeclarations(symbol: Symbol, shouldComputeAliasToMakeVisible: boolean): SymbolVisibilityResult | undefined { - let aliasesToMakeVisible: LateVisibilityPaintedStatement[] | undefined; - if (!every(filter(symbol.declarations, d => d.kind !== SyntaxKind.Identifier), getIsDeclarationVisible)) { - return undefined; - } - return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible }; - - function getIsDeclarationVisible(declaration: Declaration) { - if (!isDeclarationVisible(declaration)) { - // Mark the unexported alias as visible if its parent is visible - // because these kind of aliases can be used to name types in declaration file - - const anyImportSyntax = getAnyImportSyntax(declaration); - if (anyImportSyntax && - !hasModifier(anyImportSyntax, ModifierFlags.Export) && // import clause without export - isDeclarationVisible(anyImportSyntax.parent)) { - return addVisibleAlias(declaration, anyImportSyntax); - } - else if (isVariableDeclaration(declaration) && isVariableStatement(declaration.parent.parent) && - !hasModifier(declaration.parent.parent, ModifierFlags.Export) && // unexported variable statement - isDeclarationVisible(declaration.parent.parent.parent)) { - return addVisibleAlias(declaration, declaration.parent.parent); - } - else if (isLateVisibilityPaintedStatement(declaration) // unexported top-level statement - && !hasModifier(declaration, ModifierFlags.Export) - && isDeclarationVisible(declaration.parent)) { - return addVisibleAlias(declaration, declaration); - } - - // Declaration is not visible - return false; - } - - return true; - } - - function addVisibleAlias(declaration: Declaration, aliasingStatement: LateVisibilityPaintedStatement) { - // In function "buildTypeDisplay" where we decide whether to write type-alias or serialize types, - // we want to just check if type- alias is accessible or not but we don't care about emitting those alias at that time - // since we will do the emitting later in trackSymbol. - if (shouldComputeAliasToMakeVisible) { - getNodeLinks(declaration).isVisible = true; - aliasesToMakeVisible = appendIfUnique(aliasesToMakeVisible, aliasingStatement); - } - return true; - } - } - - function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult { - // get symbol of the first identifier of the entityName - let meaning: SymbolFlags; - if (entityName.parent.kind === SyntaxKind.TypeQuery || - isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent) || - entityName.parent.kind === SyntaxKind.ComputedPropertyName) { - // Typeof value - meaning = SymbolFlags.Value | SymbolFlags.ExportValue; - } - else if (entityName.kind === SyntaxKind.QualifiedName || entityName.kind === SyntaxKind.PropertyAccessExpression || - entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration) { - // Left identifier from type reference or TypeAlias - // Entity name of the import declaration - meaning = SymbolFlags.Namespace; - } - else { - // Type Reference or TypeAlias entity = Identifier - meaning = SymbolFlags.Type; - } - - const firstIdentifier = getFirstIdentifier(entityName); - const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false); - - // Verify if the symbol is accessible - return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || { - accessibility: SymbolAccessibility.NotAccessible, - errorSymbolName: getTextOfNode(firstIdentifier), - errorNode: firstIdentifier - }; - } - - function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags: SymbolFormatFlags = SymbolFormatFlags.AllowAnyNodeKind, writer?: EmitTextWriter): string { - let nodeFlags = NodeBuilderFlags.IgnoreErrors; - if (flags & SymbolFormatFlags.UseOnlyExternalAliasing) { - nodeFlags |= NodeBuilderFlags.UseOnlyExternalAliasing; - } - if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) { - nodeFlags |= NodeBuilderFlags.WriteTypeParametersInQualifiedName; - } - if (flags & SymbolFormatFlags.UseAliasDefinedOutsideCurrentScope) { - nodeFlags |= NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope; - } - if (flags & SymbolFormatFlags.DoNotIncludeSymbolChain) { - nodeFlags |= NodeBuilderFlags.DoNotIncludeSymbolChain; - } - const builder = flags & SymbolFormatFlags.AllowAnyNodeKind ? nodeBuilder.symbolToExpression : nodeBuilder.symbolToEntityName; - return writer ? symbolToStringWorker(writer).getText() : usingSingleLineStringWriter(symbolToStringWorker); - - function symbolToStringWorker(writer: EmitTextWriter) { - const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 - const printer = createPrinter({ removeComments: true }); - const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); - return writer; - } - } - - function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags = TypeFormatFlags.None, kind?: SignatureKind, writer?: EmitTextWriter): string { - return writer ? signatureToStringWorker(writer).getText() : usingSingleLineStringWriter(signatureToStringWorker); - - function signatureToStringWorker(writer: EmitTextWriter) { - let sigOutput: SyntaxKind; - if (flags & TypeFormatFlags.WriteArrowStyleSignature) { - sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructorType : SyntaxKind.FunctionType; - } - else { - sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructSignature : SyntaxKind.CallSignature; - } - const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); - const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true }); - const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217 - return writer; - } - } - - function typeToString(type: Type, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, writer: EmitTextWriter = createTextWriter("")): string { - const noTruncation = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation; - const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | (noTruncation ? NodeBuilderFlags.NoTruncation : 0), writer); - if (typeNode === undefined) return Debug.fail("should always get typenode"); - const options = { removeComments: true }; - const printer = createPrinter(options); - const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); - const result = writer.getText(); - - const maxLength = noTruncation ? undefined : defaultMaximumTruncationLength * 2; - if (maxLength && result && result.length >= maxLength) { - return result.substr(0, maxLength - "...".length) + "..."; - } - return result; - } - - function getTypeNamesForErrorDisplay(left: Type, right: Type): [string, string] { - let leftStr = symbolValueDeclarationIsContextSensitive(left.symbol) ? typeToString(left, left.symbol.valueDeclaration) : typeToString(left); - let rightStr = symbolValueDeclarationIsContextSensitive(right.symbol) ? typeToString(right, right.symbol.valueDeclaration) : typeToString(right); - if (leftStr === rightStr) { - leftStr = typeToString(left, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); - rightStr = typeToString(right, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); - } - return [leftStr, rightStr]; - } - - function symbolValueDeclarationIsContextSensitive(symbol: Symbol): boolean { - return symbol && symbol.valueDeclaration && isExpression(symbol.valueDeclaration) && !isContextSensitive(symbol.valueDeclaration); - } - - function toNodeBuilderFlags(flags = TypeFormatFlags.None): NodeBuilderFlags { - return flags & TypeFormatFlags.NodeBuilderFlagsMask; - } - - function createNodeBuilder() { - return { - typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => typeToTypeNodeHelper(type, context)), - indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, context)), - signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => signatureToSignatureDeclarationHelper(signature, kind, context)), - symbolToEntityName: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => symbolToName(symbol, context, meaning, /*expectsIdentifier*/ false)), - symbolToExpression: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => symbolToExpression(symbol, context, meaning)), - symbolToTypeParameterDeclarations: (symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => typeParametersToTypeParameterDeclarations(symbol, context)), - symbolToParameterDeclaration: (symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => symbolToParameterDeclaration(symbol, context)), - typeParameterToDeclaration: (parameter: TypeParameter, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => - withContext(enclosingDeclaration, flags, tracker, context => typeParameterToDeclaration(parameter, context)), - symbolTableToDeclarationStatements: (symbolTable: SymbolTable, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker, bundled?: boolean) => - withContext(enclosingDeclaration, flags, tracker, context => symbolTableToDeclarationStatements(symbolTable, context, bundled)), - }; - - function withContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined, tracker: SymbolTracker | undefined, cb: (context: NodeBuilderContext) => T): T | undefined { - Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0); - const context: NodeBuilderContext = { - enclosingDeclaration, - flags: flags || NodeBuilderFlags.None, - // If no full tracker is provided, fake up a dummy one with a basic limited-functionality moduleResolverHost - tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? { - getCommonSourceDirectory: (host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "", - getSourceFiles: () => host.getSourceFiles(), - getCurrentDirectory: maybeBind(host, host.getCurrentDirectory), - getProbableSymlinks: maybeBind(host, host.getProbableSymlinks), - } : undefined }, - encounteredError: false, - visitedTypes: undefined, - symbolDepth: undefined, - inferTypeParameters: undefined, - approximateLength: 0 - }; - const resultingNode = cb(context); - return context.encounteredError ? undefined : resultingNode; - } - - function checkTruncationLength(context: NodeBuilderContext): boolean { - if (context.truncating) return context.truncating; - return context.truncating = !(context.flags & NodeBuilderFlags.NoTruncation) && context.approximateLength > defaultMaximumTruncationLength; - } - - function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { - if (cancellationToken && cancellationToken.throwIfCancellationRequested) { - cancellationToken.throwIfCancellationRequested(); - } - const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; - context.flags &= ~NodeBuilderFlags.InTypeAlias; - - if (!type) { - context.encounteredError = true; - return undefined!; // TODO: GH#18217 - } - - if (type.flags & TypeFlags.Any) { - context.approximateLength += 3; - return createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - if (type.flags & TypeFlags.Unknown) { - return createKeywordTypeNode(SyntaxKind.UnknownKeyword); - } - if (type.flags & TypeFlags.String) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.StringKeyword); - } - if (type.flags & TypeFlags.Number) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.NumberKeyword); - } - if (type.flags & TypeFlags.BigInt) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.BigIntKeyword); - } - if (type.flags & TypeFlags.Boolean) { - context.approximateLength += 7; - return createKeywordTypeNode(SyntaxKind.BooleanKeyword); - } - if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) { - const parentSymbol = getParentOfSymbol(type.symbol)!; - const parentName = symbolToTypeNode(parentSymbol, context, SymbolFlags.Type); - const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type - ? parentName - : appendReferenceToType( - parentName as TypeReferenceNode | ImportTypeNode, - createTypeReferenceNode(symbolName(type.symbol), /*typeArguments*/ undefined) - ); - return enumLiteralName; - } - if (type.flags & TypeFlags.EnumLike) { - return symbolToTypeNode(type.symbol, context, SymbolFlags.Type); - } - if (type.flags & TypeFlags.StringLiteral) { - context.approximateLength += ((type).value.length + 2); - return createLiteralTypeNode(setEmitFlags(createLiteral((type).value), EmitFlags.NoAsciiEscaping)); - } - if (type.flags & TypeFlags.NumberLiteral) { - const value = (type).value; - context.approximateLength += ("" + value).length; - return createLiteralTypeNode(value < 0 ? createPrefix(SyntaxKind.MinusToken, createLiteral(-value)) : createLiteral(value)); - } - if (type.flags & TypeFlags.BigIntLiteral) { - context.approximateLength += (pseudoBigIntToString((type).value).length) + 1; - return createLiteralTypeNode((createLiteral((type).value))); - } - if (type.flags & TypeFlags.BooleanLiteral) { - context.approximateLength += (type).intrinsicName.length; - return (type).intrinsicName === "true" ? createTrue() : createFalse(); - } - if (type.flags & TypeFlags.UniqueESSymbol) { - if (!(context.flags & NodeBuilderFlags.AllowUniqueESSymbolType)) { - if (isValueSymbolAccessible(type.symbol, context.enclosingDeclaration)) { - context.approximateLength += 6; - return symbolToTypeNode(type.symbol, context, SymbolFlags.Value); - } - if (context.tracker.reportInaccessibleUniqueSymbolError) { - context.tracker.reportInaccessibleUniqueSymbolError(); - } - } - context.approximateLength += 13; - return createTypeOperatorNode(SyntaxKind.UniqueKeyword, createKeywordTypeNode(SyntaxKind.SymbolKeyword)); - } - if (type.flags & TypeFlags.Void) { - context.approximateLength += 4; - return createKeywordTypeNode(SyntaxKind.VoidKeyword); - } - if (type.flags & TypeFlags.Undefined) { - context.approximateLength += 9; - return createKeywordTypeNode(SyntaxKind.UndefinedKeyword); - } - if (type.flags & TypeFlags.Null) { - context.approximateLength += 4; - return createKeywordTypeNode(SyntaxKind.NullKeyword); - } - if (type.flags & TypeFlags.Never) { - context.approximateLength += 5; - return createKeywordTypeNode(SyntaxKind.NeverKeyword); - } - if (type.flags & TypeFlags.ESSymbol) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.SymbolKeyword); - } - if (type.flags & TypeFlags.NonPrimitive) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.ObjectKeyword); - } - if (isThisTypeParameter(type)) { - if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) { - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) { - context.encounteredError = true; - } - if (context.tracker.reportInaccessibleThisError) { - context.tracker.reportInaccessibleThisError(); - } - } - context.approximateLength += 4; - return createThis(); - } - - if (!inTypeAlias && type.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration))) { - const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context); - if (isReservedMemberName(type.aliasSymbol.escapedName) && !(type.aliasSymbol.flags & SymbolFlags.Class)) return createTypeReferenceNode(createIdentifier(""), typeArgumentNodes); - return symbolToTypeNode(type.aliasSymbol, context, SymbolFlags.Type, typeArgumentNodes); - } - - const objectFlags = getObjectFlags(type); - - if (objectFlags & ObjectFlags.Reference) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - return (type).node ? visitAndTransformType(type, typeReferenceToTypeNode) : typeReferenceToTypeNode(type); - } - if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { - if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) { - context.approximateLength += (symbolName(type.symbol).length + 6); - return createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined)); - } - if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && - type.flags & TypeFlags.TypeParameter && - !isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) { - const name = typeParameterToName(type, context); - context.approximateLength += idText(name).length; - return createTypeReferenceNode(createIdentifier(idText(name)), /*typeArguments*/ undefined); - } - // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. - return type.symbol - ? symbolToTypeNode(type.symbol, context, SymbolFlags.Type) - : createTypeReferenceNode(createIdentifier("?"), /*typeArguments*/ undefined); - } - if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { - const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; - if (length(types) === 1) { - return typeToTypeNodeHelper(types[0], context); - } - const typeNodes = mapToTypeNodes(types, context, /*isBareList*/ true); - if (typeNodes && typeNodes.length > 0) { - const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); - return unionOrIntersectionTypeNode; - } - else { - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) { - context.encounteredError = true; - } - return undefined!; // TODO: GH#18217 - } - } - if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - // The type is an object literal type. - return createAnonymousTypeNode(type); - } - if (type.flags & TypeFlags.Index) { - const indexedType = (type).type; - context.approximateLength += 6; - const indexTypeNode = typeToTypeNodeHelper(indexedType, context); - return createTypeOperatorNode(indexTypeNode); - } - if (type.flags & TypeFlags.IndexedAccess) { - const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); - const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); - context.approximateLength += 2; - return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); - } - if (type.flags & TypeFlags.Conditional) { - const checkTypeNode = typeToTypeNodeHelper((type).checkType, context); - const saveInferTypeParameters = context.inferTypeParameters; - context.inferTypeParameters = (type).root.inferTypeParameters; - const extendsTypeNode = typeToTypeNodeHelper((type).extendsType, context); - context.inferTypeParameters = saveInferTypeParameters; - const trueTypeNode = typeToTypeNodeHelper(getTrueTypeFromConditionalType(type), context); - const falseTypeNode = typeToTypeNodeHelper(getFalseTypeFromConditionalType(type), context); - context.approximateLength += 15; - return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode); - } - if (type.flags & TypeFlags.Substitution) { - return typeToTypeNodeHelper((type).typeVariable, context); - } - - return Debug.fail("Should be unreachable."); - - function createMappedTypeNodeFromType(type: MappedType) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - const readonlyToken = type.declaration.readonlyToken ? createToken(type.declaration.readonlyToken.kind) : undefined; - const questionToken = type.declaration.questionToken ? createToken(type.declaration.questionToken.kind) : undefined; - let appropriateConstraintTypeNode: TypeNode; - if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - // We have a { [P in keyof T]: X } - // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType` - appropriateConstraintTypeNode = createTypeOperatorNode(typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context)); - } - else { - appropriateConstraintTypeNode = typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context); - } - const typeParameterNode = typeParameterToDeclarationWithConstraint(getTypeParameterFromMappedType(type), context, appropriateConstraintTypeNode); - const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context); - const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); - context.approximateLength += 10; - return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); - } - - function createAnonymousTypeNode(type: ObjectType): TypeNode { - const typeId = "" + type.id; - const symbol = type.symbol; - if (symbol) { - if (isJSConstructor(symbol.valueDeclaration)) { - // Instance and static types share the same symbol; only add 'typeof' for the static side. - const isInstanceType = type === getDeclaredTypeOfClassOrInterface(symbol) ? SymbolFlags.Type : SymbolFlags.Value; - return symbolToTypeNode(symbol, context, isInstanceType); - } - // Always use 'typeof T' for type of class, enum, and module objects - else if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) && !(symbol.valueDeclaration.kind === SyntaxKind.ClassExpression && context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral) || - symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) || - shouldWriteTypeOfFunctionSymbol()) { - return symbolToTypeNode(symbol, context, SymbolFlags.Value); - } - else if (context.visitedTypes && context.visitedTypes.has(typeId)) { - // If type is an anonymous type literal in a type alias declaration, use type alias name - const typeAlias = getTypeAliasForTypeLiteral(type); - if (typeAlias) { - // The specified symbol flags need to be reinterpreted as type flags - return symbolToTypeNode(typeAlias, context, SymbolFlags.Type); - } - else { - return createElidedInformationPlaceholder(context); - } - } - else { - return visitAndTransformType(type, createTypeNodeFromObjectType); - } - } - else { - // Anonymous types without a symbol are never circular. - return createTypeNodeFromObjectType(type); - } - function shouldWriteTypeOfFunctionSymbol() { - const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method - some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static)); - const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && - (symbol.parent || // is exported function symbol - forEach(symbol.declarations, declaration => - declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock)); - if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { - // typeof is allowed only for static/non local functions - return (!!(context.flags & NodeBuilderFlags.UseTypeOfFunction) || (context.visitedTypes && context.visitedTypes.has(typeId))) && // it is type of the symbol uses itself recursively - (!(context.flags & NodeBuilderFlags.UseStructuralFallback) || isValueSymbolAccessible(symbol, context.enclosingDeclaration)); // And the build is going to succeed without visibility error or there is no structural fallback allowed - } - } - } - - function visitAndTransformType(type: Type, transform: (type: Type) => T) { - const typeId = "" + type.id; - const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class; - const id = getObjectFlags(type) & ObjectFlags.Reference && (type).node ? "N" + getNodeId((type).node!) : - type.symbol ? (isConstructorObject ? "+" : "") + getSymbolId(type.symbol) : - undefined; - // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead - // of types allows us to catch circular references to instantiations of the same anonymous type - if (!context.visitedTypes) { - context.visitedTypes = createMap(); - } - if (id && !context.symbolDepth) { - context.symbolDepth = createMap(); - } - - let depth: number | undefined; - if (id) { - depth = context.symbolDepth!.get(id) || 0; - if (depth > 10) { - return createElidedInformationPlaceholder(context); - } - context.symbolDepth!.set(id, depth + 1); - } - context.visitedTypes.set(typeId, true); - const result = transform(type); - context.visitedTypes.delete(typeId); - if (id) { - context.symbolDepth!.set(id, depth!); - } - return result; - } - - function createTypeNodeFromObjectType(type: ObjectType): TypeNode { - if (isGenericMappedType(type)) { - return createMappedTypeNodeFromType(type); - } - - const resolved = resolveStructuredTypeMembers(type); - if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { - if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - context.approximateLength += 2; - return setEmitFlags(createTypeLiteralNode(/*members*/ undefined), EmitFlags.SingleLine); - } - - if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { - const signature = resolved.callSignatures[0]; - const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); - return signatureNode; - - } - - if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { - const signature = resolved.constructSignatures[0]; - const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context); - return signatureNode; - } - } - - const savedFlags = context.flags; - context.flags |= NodeBuilderFlags.InObjectTypeLiteral; - const members = createTypeNodesFromResolvedType(resolved); - context.flags = savedFlags; - const typeLiteralNode = createTypeLiteralNode(members); - context.approximateLength += 2; - return setEmitFlags(typeLiteralNode, (context.flags & NodeBuilderFlags.MultilineObjectLiterals) ? 0 : EmitFlags.SingleLine); - } - - function typeReferenceToTypeNode(type: TypeReference) { - const typeArguments: readonly Type[] = getTypeArguments(type); - if (type.target === globalArrayType || type.target === globalReadonlyArrayType) { - if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) { - const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context); - return createTypeReferenceNode(type.target === globalArrayType ? "Array" : "ReadonlyArray", [typeArgumentNode]); - } - const elementType = typeToTypeNodeHelper(typeArguments[0], context); - const arrayType = createArrayTypeNode(elementType); - return type.target === globalArrayType ? arrayType : createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, arrayType); - } - else if (type.target.objectFlags & ObjectFlags.Tuple) { - if (typeArguments.length > 0) { - const arity = getTypeReferenceArity(type); - const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context); - const hasRestElement = (type.target).hasRestElement; - if (tupleConstituentNodes) { - for (let i = (type.target).minLength; i < Math.min(arity, tupleConstituentNodes.length); i++) { - tupleConstituentNodes[i] = hasRestElement && i === arity - 1 ? - createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) : - createOptionalTypeNode(tupleConstituentNodes[i]); - } - const tupleTypeNode = createTupleTypeNode(tupleConstituentNodes); - return (type.target).readonly ? createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, tupleTypeNode) : tupleTypeNode; - } - } - if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) { - const tupleTypeNode = createTupleTypeNode([]); - return (type.target).readonly ? createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, tupleTypeNode) : tupleTypeNode; - } - context.encounteredError = true; - return undefined!; // TODO: GH#18217 - } - else if (context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral && - type.symbol.valueDeclaration && - isClassLike(type.symbol.valueDeclaration) && - !isValueSymbolAccessible(type.symbol, context.enclosingDeclaration) - ) { - return createAnonymousTypeNode(type); - } - else { - const outerTypeParameters = type.target.outerTypeParameters; - let i = 0; - let resultType: TypeReferenceNode | ImportTypeNode | undefined; - if (outerTypeParameters) { - const length = outerTypeParameters.length; - while (i < length) { - // Find group of type arguments for type parameters with the same declaring container. - const start = i; - const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i])!; - do { - i++; - } while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent); - // When type parameters are their own type arguments for the whole group (i.e. we have - // the default outer type arguments), we don't show the group. - if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const typeArgumentSlice = mapToTypeNodes(typeArguments.slice(start, i), context); - const flags = context.flags; - context.flags |= NodeBuilderFlags.ForbidIndexedAccessSymbolReferences; - const ref = symbolToTypeNode(parent, context, SymbolFlags.Type, typeArgumentSlice) as TypeReferenceNode | ImportTypeNode; - context.flags = flags; - resultType = !resultType ? ref : appendReferenceToType(resultType, ref as TypeReferenceNode); - } - } - } - let typeArgumentNodes: readonly TypeNode[] | undefined; - if (typeArguments.length > 0) { - const typeParameterCount = (type.target.typeParameters || emptyArray).length; - typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context); - } - const flags = context.flags; - context.flags |= NodeBuilderFlags.ForbidIndexedAccessSymbolReferences; - const finalRef = symbolToTypeNode(type.symbol, context, SymbolFlags.Type, typeArgumentNodes); - context.flags = flags; - return !resultType ? finalRef : appendReferenceToType(resultType, finalRef as TypeReferenceNode); - } - } - - - function appendReferenceToType(root: TypeReferenceNode | ImportTypeNode, ref: TypeReferenceNode): TypeReferenceNode | ImportTypeNode { - if (isImportTypeNode(root)) { - // first shift type arguments - const innerParams = root.typeArguments; - if (root.qualifier) { - (isIdentifier(root.qualifier) ? root.qualifier : root.qualifier.right).typeArguments = innerParams; - } - root.typeArguments = ref.typeArguments; - // then move qualifiers - const ids = getAccessStack(ref); - for (const id of ids) { - root.qualifier = root.qualifier ? createQualifiedName(root.qualifier, id) : id; - } - return root; - } - else { - // first shift type arguments - const innerParams = root.typeArguments; - (isIdentifier(root.typeName) ? root.typeName : root.typeName.right).typeArguments = innerParams; - root.typeArguments = ref.typeArguments; - // then move qualifiers - const ids = getAccessStack(ref); - for (const id of ids) { - root.typeName = createQualifiedName(root.typeName, id); - } - return root; - } - } - - function getAccessStack(ref: TypeReferenceNode): Identifier[] { - let state = ref.typeName; - const ids = []; - while (!isIdentifier(state)) { - ids.unshift(state.right); - state = state.left; - } - ids.unshift(state); - return ids; - } - - function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] | undefined { - if (checkTruncationLength(context)) { - return [createPropertySignature(/*modifiers*/ undefined, "...", /*questionToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined)]; - } - const typeElements: TypeElement[] = []; - for (const signature of resolvedType.callSignatures) { - typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, context)); - } - for (const signature of resolvedType.constructSignatures) { - typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context)); - } - if (resolvedType.stringIndexInfo) { - let indexSignature: IndexSignatureDeclaration; - if (resolvedType.objectFlags & ObjectFlags.ReverseMapped) { - indexSignature = indexInfoToIndexSignatureDeclarationHelper(createIndexInfo(anyType, resolvedType.stringIndexInfo.isReadonly, resolvedType.stringIndexInfo.declaration), IndexKind.String, context); - indexSignature.type = createElidedInformationPlaceholder(context); - } - else { - indexSignature = indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context); - } - typeElements.push(indexSignature); - } - if (resolvedType.numberIndexInfo) { - typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context)); - } - - const properties = resolvedType.properties; - if (!properties) { - return typeElements; - } - - let i = 0; - for (const propertySymbol of properties) { - i++; - if (context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral) { - if (propertySymbol.flags & SymbolFlags.Prototype) { - continue; - } - if (getDeclarationModifierFlagsFromSymbol(propertySymbol) & (ModifierFlags.Private | ModifierFlags.Protected) && context.tracker.reportPrivateInBaseOfClassExpression) { - context.tracker.reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(propertySymbol.escapedName)); - } - } - if (checkTruncationLength(context) && (i + 2 < properties.length - 1)) { - typeElements.push(createPropertySignature(/*modifiers*/ undefined, `... ${properties.length - i} more ...`, /*questionToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined)); - addPropertyToElementList(properties[properties.length - 1], context, typeElements); - break; - } - addPropertyToElementList(propertySymbol, context, typeElements); - - } - return typeElements.length ? typeElements : undefined; - } - } - - function createElidedInformationPlaceholder(context: NodeBuilderContext) { - context.approximateLength += 3; - if (!(context.flags & NodeBuilderFlags.NoTruncation)) { - return createTypeReferenceNode(createIdentifier("..."), /*typeArguments*/ undefined); - } - return createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - - function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) { - const propertyIsReverseMapped = !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped); - const propertyType = propertyIsReverseMapped && context.flags & NodeBuilderFlags.InReverseMappedType ? - anyType : getTypeOfSymbol(propertySymbol); - const saveEnclosingDeclaration = context.enclosingDeclaration; - context.enclosingDeclaration = undefined; - if (context.tracker.trackSymbol && getCheckFlags(propertySymbol) & CheckFlags.Late) { - const decl = first(propertySymbol.declarations); - if (hasLateBindableName(decl)) { - if (isBinaryExpression(decl)) { - const name = getNameOfDeclaration(decl); - if (name && isElementAccessExpression(name) && isPropertyAccessEntityNameExpression(name.argumentExpression)) { - trackComputedName(name.argumentExpression, saveEnclosingDeclaration, context); - } - } - else { - trackComputedName(decl.name.expression, saveEnclosingDeclaration, context); - } - } - } - context.enclosingDeclaration = saveEnclosingDeclaration; - const propertyName = getPropertyNameNodeForSymbol(propertySymbol, context); - context.approximateLength += (symbolName(propertySymbol).length + 1); - const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; - if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length && !isReadonlySymbol(propertySymbol)) { - const signatures = getSignaturesOfType(filterType(propertyType, t => !(t.flags & TypeFlags.Undefined)), SignatureKind.Call); - for (const signature of signatures) { - const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context); - methodDeclaration.name = propertyName; - methodDeclaration.questionToken = optionalToken; - typeElements.push(preserveCommentsOn(methodDeclaration)); - } - } - else { - const savedFlags = context.flags; - context.flags |= propertyIsReverseMapped ? NodeBuilderFlags.InReverseMappedType : 0; - let propertyTypeNode: TypeNode; - if (propertyIsReverseMapped && !!(savedFlags & NodeBuilderFlags.InReverseMappedType)) { - propertyTypeNode = createElidedInformationPlaceholder(context); - } - else { - propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - context.flags = savedFlags; - - const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined; - if (modifiers) { - context.approximateLength += 9; - } - const propertySignature = createPropertySignature( - modifiers, - propertyName, - optionalToken, - propertyTypeNode, - /*initializer*/ undefined); - - typeElements.push(preserveCommentsOn(propertySignature)); - } - - function preserveCommentsOn(node: T) { - if (some(propertySymbol.declarations, d => d.kind === SyntaxKind.JSDocPropertyTag)) { - const d = find(propertySymbol.declarations, d => d.kind === SyntaxKind.JSDocPropertyTag)! as JSDocPropertyTag; - const commentText = d.comment; - if (commentText) { - setSyntheticLeadingComments(node, [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }]); - } - } - else if (propertySymbol.valueDeclaration) { - // Copy comments to node for declaration emit - setCommentRange(node, propertySymbol.valueDeclaration); - } - return node; - } - } - - function mapToTypeNodes(types: readonly Type[] | undefined, context: NodeBuilderContext, isBareList?: boolean): TypeNode[] | undefined { - if (some(types)) { - if (checkTruncationLength(context)) { - if (!isBareList) { - return [createTypeReferenceNode("...", /*typeArguments*/ undefined)]; - } - else if (types.length > 2) { - return [ - typeToTypeNodeHelper(types[0], context), - createTypeReferenceNode(`... ${types.length - 2} more ...`, /*typeArguments*/ undefined), - typeToTypeNodeHelper(types[types.length - 1], context) - ]; - } - } - const result = []; - let i = 0; - for (const type of types) { - i++; - if (checkTruncationLength(context) && (i + 2 < types.length - 1)) { - result.push(createTypeReferenceNode(`... ${types.length - i} more ...`, /*typeArguments*/ undefined)); - const typeNode = typeToTypeNodeHelper(types[types.length - 1], context); - if (typeNode) { - result.push(typeNode); - } - break; - } - context.approximateLength += 2; // Account for whitespace + separator - const typeNode = typeToTypeNodeHelper(type, context); - if (typeNode) { - result.push(typeNode); - } - } - - return result; - } - } - - function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { - const name = getNameFromIndexInfo(indexInfo) || "x"; - const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); - - const indexingParameter = createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - name, - /*questionToken*/ undefined, - indexerTypeNode, - /*initializer*/ undefined); - const typeNode = typeToTypeNodeHelper(indexInfo.type || anyType, context); - if (!indexInfo.type && !(context.flags & NodeBuilderFlags.AllowEmptyIndexInfoType)) { - context.encounteredError = true; - } - context.approximateLength += (name.length + 4); - return createIndexSignature( - /*decorators*/ undefined, - indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined, - [indexingParameter], - typeNode); - } - - function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, context: NodeBuilderContext): SignatureDeclaration { - let typeParameters: TypeParameterDeclaration[] | undefined; - let typeArguments: TypeNode[] | undefined; - if (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature && signature.target && signature.mapper && signature.target.typeParameters) { - typeArguments = signature.target.typeParameters.map(parameter => typeToTypeNodeHelper(instantiateType(parameter, signature.mapper), context)); - } - else { - typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); - } - - const parameters = getExpandedParameters(signature).map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor)); - if (signature.thisParameter) { - const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context); - parameters.unshift(thisParameter); - } - - let returnTypeNode: TypeNode | undefined; - const typePredicate = getTypePredicateOfSignature(signature); - if (typePredicate) { - const assertsModifier = typePredicate.kind === TypePredicateKind.AssertsThis || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? - createToken(SyntaxKind.AssertsKeyword) : - undefined; - const parameterName = typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? - setEmitFlags(createIdentifier(typePredicate.parameterName), EmitFlags.NoAsciiEscaping) : - createThisTypeNode(); - const typeNode = typePredicate.type && typeToTypeNodeHelper(typePredicate.type, context); - returnTypeNode = createTypePredicateNodeWithModifier(assertsModifier, parameterName, typeNode); - } - else { - const returnType = getReturnTypeOfSignature(signature); - returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context); - } - if (context.flags & NodeBuilderFlags.SuppressAnyReturnType) { - if (returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) { - returnTypeNode = undefined; - } - } - else if (!returnTypeNode) { - returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum - return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments); - } - - function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration { - const savedContextFlags = context.flags; - context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic - const name = typeParameterToName(type, context); - const defaultParameter = getDefaultFromTypeParameter(type); - const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); - context.flags = savedContextFlags; - return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); - } - - function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintOfTypeParameter(type)): TypeParameterDeclaration { - const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); - return typeParameterToDeclarationWithConstraint(type, context, constraintNode); - } - - function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean): ParameterDeclaration { - let parameterDeclaration: ParameterDeclaration | JSDocParameterTag | undefined = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); - if (!parameterDeclaration && !isTransientSymbol(parameterSymbol)) { - parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.JSDocParameterTag); - } - - let parameterType = getTypeOfSymbol(parameterSymbol); - if (parameterDeclaration && isRequiredInitializedParameter(parameterDeclaration)) { - parameterType = getOptionalType(parameterType); - } - const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); - - const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && preserveModifierFlags && parameterDeclaration && parameterDeclaration.modifiers ? parameterDeclaration.modifiers.map(getSynthesizedClone) : undefined; - const isRest = parameterDeclaration && isRestParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.RestParameter; - const dotDotDotToken = isRest ? createToken(SyntaxKind.DotDotDotToken) : undefined; - const name = parameterDeclaration ? parameterDeclaration.name ? - parameterDeclaration.name.kind === SyntaxKind.Identifier ? setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : - parameterDeclaration.name.kind === SyntaxKind.QualifiedName ? setEmitFlags(getSynthesizedClone(parameterDeclaration.name.right), EmitFlags.NoAsciiEscaping) : - cloneBindingName(parameterDeclaration.name) : - symbolName(parameterSymbol) : - symbolName(parameterSymbol); - const isOptional = parameterDeclaration && isOptionalParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.OptionalParameter; - const questionToken = isOptional ? createToken(SyntaxKind.QuestionToken) : undefined; - const parameterNode = createParameter( - /*decorators*/ undefined, - modifiers, - dotDotDotToken, - name, - questionToken, - parameterTypeNode, - /*initializer*/ undefined); - context.approximateLength += symbolName(parameterSymbol).length + 3; - return parameterNode; - - function cloneBindingName(node: BindingName): BindingName { - return elideInitializerAndSetEmitFlags(node); - function elideInitializerAndSetEmitFlags(node: Node): Node { - if (context.tracker.trackSymbol && isComputedPropertyName(node) && isLateBindableName(node)) { - trackComputedName(node.expression, context.enclosingDeclaration, context); - } - const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags)!; - const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited); - if (clone.kind === SyntaxKind.BindingElement) { - (clone).initializer = undefined; - } - return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping); - } - } - } - - function trackComputedName(accessExpression: EntityNameOrEntityNameExpression, enclosingDeclaration: Node | undefined, context: NodeBuilderContext) { - if (!context.tracker.trackSymbol) return; - // get symbol of the first identifier of the entityName - const firstIdentifier = getFirstIdentifier(accessExpression); - const name = resolveName(firstIdentifier, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); - if (name) { - context.tracker.trackSymbol(name, enclosingDeclaration, SymbolFlags.Value); - } - } - - function lookupSymbolChain(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, yieldModuleSymbol?: boolean) { - context.tracker.trackSymbol!(symbol, context.enclosingDeclaration, meaning); // TODO: GH#18217 - return lookupSymbolChainWorker(symbol, context, meaning, yieldModuleSymbol); - } - - function lookupSymbolChainWorker(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, yieldModuleSymbol?: boolean) { - // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. - let chain: Symbol[]; - const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; - if (!isTypeParameter && (context.enclosingDeclaration || context.flags & NodeBuilderFlags.UseFullyQualifiedType) && !(context.flags & NodeBuilderFlags.DoNotIncludeSymbolChain)) { - chain = Debug.assertDefined(getSymbolChain(symbol, meaning, /*endOfChain*/ true)); - Debug.assert(chain && chain.length > 0); - } - else { - chain = [symbol]; - } - return chain; - - /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ - function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined { - let accessibleSymbolChain = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, !!(context.flags & NodeBuilderFlags.UseOnlyExternalAliasing)); - let parentSpecifiers: (string | undefined)[]; - if (!accessibleSymbolChain || - needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { - - // Go up and add our parent. - const parents = getContainersOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol, context.enclosingDeclaration); - if (length(parents)) { - parentSpecifiers = parents!.map(symbol => - some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol) - ? getSpecifierForModuleSymbol(symbol, context) - : undefined); - const indices = parents!.map((_, i) => i); - indices.sort(sortByBestName); - const sortedParents = indices.map(i => parents![i]); - for (const parent of sortedParents) { - const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); - if (parentChain) { - if (parent.exports && parent.exports.get(InternalSymbolName.ExportEquals) && - getSymbolIfSameReference(parent.exports.get(InternalSymbolName.ExportEquals)!, symbol)) { - // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent - // No need to lookup an alias for the symbol in itself - accessibleSymbolChain = parentChain; - break; - } - accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [getAliasForSymbolInContainer(parent, symbol) || symbol]); - break; - } - } - } - } - - if (accessibleSymbolChain) { - return accessibleSymbolChain; - } - if ( - // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. - endOfChain || - // If a parent symbol is an anonymous type, don't write it. - !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) { - // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) - if (!endOfChain && !yieldModuleSymbol && !!forEach(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { - return; - } - return [symbol]; - } - - function sortByBestName(a: number, b: number) { - const specifierA = parentSpecifiers[a]; - const specifierB = parentSpecifiers[b]; - if (specifierA && specifierB) { - const isBRelative = pathIsRelative(specifierB); - if (pathIsRelative(specifierA) === isBRelative) { - // Both relative or both non-relative, sort by number of parts - return moduleSpecifiers.countPathComponents(specifierA) - moduleSpecifiers.countPathComponents(specifierB); - } - if (isBRelative) { - // A is non-relative, B is relative: prefer A - return -1; - } - // A is relative, B is non-relative: prefer B - return 1; - } - return 0; - } - } - } - - function typeParametersToTypeParameterDeclarations(symbol: Symbol, context: NodeBuilderContext) { - let typeParameterNodes: NodeArray | undefined; - const targetSymbol = getTargetSymbol(symbol); - if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) { - typeParameterNodes = createNodeArray(map(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), tp => typeParameterToDeclaration(tp, context))); - } - return typeParameterNodes; - } - - function lookupTypeParameterNodes(chain: Symbol[], index: number, context: NodeBuilderContext) { - Debug.assert(chain && 0 <= index && index < chain.length); - const symbol = chain[index]; - const symbolId = "" + getSymbolId(symbol); - if (context.typeParameterSymbolList && context.typeParameterSymbolList.get(symbolId)) { - return undefined; - } - (context.typeParameterSymbolList || (context.typeParameterSymbolList = createMap())).set(symbolId, true); - let typeParameterNodes: readonly TypeNode[] | readonly TypeParameterDeclaration[] | undefined; - if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index < (chain.length - 1)) { - const parentSymbol = symbol; - const nextSymbol = chain[index + 1]; - if (getCheckFlags(nextSymbol) & CheckFlags.Instantiated) { - const params = getTypeParametersOfClassOrInterface( - parentSymbol.flags & SymbolFlags.Alias ? resolveAlias(parentSymbol) : parentSymbol - ); - typeParameterNodes = mapToTypeNodes(map(params, (nextSymbol as TransientSymbol).mapper!), context); - } - else { - typeParameterNodes = typeParametersToTypeParameterDeclarations(symbol, context); - } - } - return typeParameterNodes; - } - - /** - * Given A[B][C][D], finds A[B] - */ - function getTopmostIndexedAccessType(top: IndexedAccessTypeNode): IndexedAccessTypeNode { - if (isIndexedAccessTypeNode(top.objectType)) { - return getTopmostIndexedAccessType(top.objectType); - } - return top; - } - - function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext) { - const file = getDeclarationOfKind(symbol, SyntaxKind.SourceFile); - if (file && file.moduleName !== undefined) { - // Use the amd name if it is available - return file.moduleName; - } - if (!file) { - if (context.tracker.trackReferencedAmbientModule) { - const ambientDecls = filter(symbol.declarations, isAmbientModule); - if (length(ambientDecls)) { - for (const decl of ambientDecls) { - context.tracker.trackReferencedAmbientModule(decl, symbol); - } - } - } - if (ambientModuleSymbolRegex.test(symbol.escapedName as string)) { - return (symbol.escapedName as string).substring(1, (symbol.escapedName as string).length - 1); - } - } - if (!context.enclosingDeclaration || !context.tracker.moduleResolverHost) { - // If there's no context declaration, we can't lookup a non-ambient specifier, so we just use the symbol name - if (ambientModuleSymbolRegex.test(symbol.escapedName as string)) { - return (symbol.escapedName as string).substring(1, (symbol.escapedName as string).length - 1); - } - return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!).fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full - } - const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration)); - const links = getSymbolLinks(symbol); - let specifier = links.specifierCache && links.specifierCache.get(contextFile.path); - if (!specifier) { - const isBundle = (compilerOptions.out || compilerOptions.outFile); - // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports, - // just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this - // using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative - // specifier preference - const { moduleResolverHost } = context.tracker; - const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions; - specifier = first(moduleSpecifiers.getModuleSpecifiers( - symbol, - specifierCompilerOptions, - contextFile, - moduleResolverHost, - host.getSourceFiles(), - { importModuleSpecifierPreference: isBundle ? "non-relative" : "relative" }, - host.redirectTargetsMap, - )); - links.specifierCache = links.specifierCache || createMap(); - links.specifierCache.set(contextFile.path, specifier); - } - return specifier; - } - - function symbolToTypeNode(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, overrideTypeArguments?: readonly TypeNode[]): TypeNode { - const chain = lookupSymbolChain(symbol, context, meaning, !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope)); // If we're using aliases outside the current scope, dont bother with the module - - const isTypeOf = meaning === SymbolFlags.Value; - if (some(chain[0].declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { - // module is root, must use `ImportTypeNode` - const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined; - const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context); - const specifier = getSpecifierForModuleSymbol(chain[0], context); - if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeJs && specifier.indexOf("/node_modules/") >= 0) { - // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error - // since declaration files with these kinds of references are liable to fail when published :( - context.encounteredError = true; - if (context.tracker.reportLikelyUnsafeImportRequiredError) { - context.tracker.reportLikelyUnsafeImportRequiredError(specifier); - } - } - const lit = createLiteralTypeNode(createLiteral(specifier)); - if (context.tracker.trackExternalModuleSymbolOfImportTypeNode) context.tracker.trackExternalModuleSymbolOfImportTypeNode(chain[0]); - context.approximateLength += specifier.length + 10; // specifier + import("") - if (!nonRootParts || isEntityName(nonRootParts)) { - if (nonRootParts) { - const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right; - lastId.typeArguments = undefined; - } - return createImportTypeNode(lit, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf); - } - else { - const splitNode = getTopmostIndexedAccessType(nonRootParts); - const qualifier = (splitNode.objectType as TypeReferenceNode).typeName; - return createIndexedAccessTypeNode(createImportTypeNode(lit, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType); - } - } - - const entityName = createAccessFromSymbolChain(chain, chain.length - 1, 0); - if (isIndexedAccessTypeNode(entityName)) { - return entityName; // Indexed accesses can never be `typeof` - } - if (isTypeOf) { - return createTypeQueryNode(entityName); - } - else { - const lastId = isIdentifier(entityName) ? entityName : entityName.right; - const lastTypeArgs = lastId.typeArguments; - lastId.typeArguments = undefined; - return createTypeReferenceNode(entityName, lastTypeArgs as NodeArray); - } - - function createAccessFromSymbolChain(chain: Symbol[], index: number, stopper: number): EntityName | IndexedAccessTypeNode { - const typeParameterNodes = index === (chain.length - 1) ? overrideTypeArguments : lookupTypeParameterNodes(chain, index, context); - const symbol = chain[index]; - - const parent = chain[index - 1]; - let symbolName: string | undefined; - if (index === 0) { - context.flags |= NodeBuilderFlags.InInitialEntityName; - symbolName = getNameOfSymbolAsWritten(symbol, context); - context.approximateLength += (symbolName ? symbolName.length : 0) + 1; - context.flags ^= NodeBuilderFlags.InInitialEntityName; - } - else { - if (parent && getExportsOfSymbol(parent)) { - const exports = getExportsOfSymbol(parent); - forEachEntry(exports, (ex, name) => { - if (getSymbolIfSameReference(ex, symbol) && !isLateBoundName(name) && name !== InternalSymbolName.ExportEquals) { - symbolName = unescapeLeadingUnderscores(name); - return true; - } - }); - } - } - if (!symbolName) { - symbolName = getNameOfSymbolAsWritten(symbol, context); - } - context.approximateLength += symbolName.length + 1; - - if (!(context.flags & NodeBuilderFlags.ForbidIndexedAccessSymbolReferences) && parent && - getMembersOfSymbol(parent) && getMembersOfSymbol(parent).get(symbol.escapedName) && - getSymbolIfSameReference(getMembersOfSymbol(parent).get(symbol.escapedName)!, symbol)) { - // Should use an indexed access - const LHS = createAccessFromSymbolChain(chain, index - 1, stopper); - if (isIndexedAccessTypeNode(LHS)) { - return createIndexedAccessTypeNode(LHS, createLiteralTypeNode(createLiteral(symbolName))); - } - else { - return createIndexedAccessTypeNode(createTypeReferenceNode(LHS, typeParameterNodes as readonly TypeNode[]), createLiteralTypeNode(createLiteral(symbolName))); - } - } - - const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); - identifier.symbol = symbol; - - if (index > stopper) { - const LHS = createAccessFromSymbolChain(chain, index - 1, stopper); - if (!isEntityName(LHS)) { - return Debug.fail("Impossible construct - an export of an indexed access cannot be reachable"); - } - return createQualifiedName(LHS, identifier); - } - return identifier; - } - } - - function typeParameterShadowsNameInScope(escapedName: __String, context: NodeBuilderContext) { - return !!resolveName(context.enclosingDeclaration, escapedName, SymbolFlags.Type, /*nameNotFoundArg*/ undefined, escapedName, /*isUse*/ false); - } - - function typeParameterToName(type: TypeParameter, context: NodeBuilderContext) { - if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && context.typeParameterNames) { - const cached = context.typeParameterNames.get("" + getTypeId(type)); - if (cached) { - return cached; - } - } - let result = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); - if (!(result.kind & SyntaxKind.Identifier)) { - return createIdentifier("(Missing type parameter)"); - } - if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { - const rawtext = result.escapedText as string; - let i = 0; - let text = rawtext; - while ((context.typeParameterNamesByText && context.typeParameterNamesByText.get(text)) || typeParameterShadowsNameInScope(text as __String, context)) { - i++; - text = `${rawtext}_${i}`; - } - if (text !== rawtext) { - result = createIdentifier(text, result.typeArguments); - } - (context.typeParameterNames || (context.typeParameterNames = createMap())).set("" + getTypeId(type), result); - (context.typeParameterNamesByText || (context.typeParameterNamesByText = createMap())).set(result.escapedText as string, true); - } - return result; - } - - function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier; - function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName; - function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName { - const chain = lookupSymbolChain(symbol, context, meaning); - - if (expectsIdentifier && chain.length !== 1 - && !context.encounteredError - && !(context.flags & NodeBuilderFlags.AllowQualifedNameInPlaceOfIdentifier)) { - context.encounteredError = true; - } - return createEntityNameFromSymbolChain(chain, chain.length - 1); - - function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { - const typeParameterNodes = lookupTypeParameterNodes(chain, index, context); - const symbol = chain[index]; - - if (index === 0) { - context.flags |= NodeBuilderFlags.InInitialEntityName; - } - const symbolName = getNameOfSymbolAsWritten(symbol, context); - if (index === 0) { - context.flags ^= NodeBuilderFlags.InInitialEntityName; - } - - const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); - identifier.symbol = symbol; - - return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; - } - } - - function symbolToExpression(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags) { - const chain = lookupSymbolChain(symbol, context, meaning); - - return createExpressionFromSymbolChain(chain, chain.length - 1); - - function createExpressionFromSymbolChain(chain: Symbol[], index: number): Expression { - const typeParameterNodes = lookupTypeParameterNodes(chain, index, context); - const symbol = chain[index]; - - if (index === 0) { - context.flags |= NodeBuilderFlags.InInitialEntityName; - } - let symbolName = getNameOfSymbolAsWritten(symbol, context); - if (index === 0) { - context.flags ^= NodeBuilderFlags.InInitialEntityName; - } - let firstChar = symbolName.charCodeAt(0); - - if (isSingleOrDoubleQuote(firstChar) && some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { - return createLiteral(getSpecifierForModuleSymbol(symbol, context)); - } - const canUsePropertyAccess = firstChar === CharacterCodes.hash ? - symbolName.length > 1 && isIdentifierStart(symbolName.charCodeAt(1), languageVersion) : - isIdentifierStart(firstChar, languageVersion); - if (index === 0 || canUsePropertyAccess) { - const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); - identifier.symbol = symbol; - - return index > 0 ? createPropertyAccess(createExpressionFromSymbolChain(chain, index - 1), identifier) : identifier; - } - else { - if (firstChar === CharacterCodes.openBracket) { - symbolName = symbolName.substring(1, symbolName.length - 1); - firstChar = symbolName.charCodeAt(0); - } - let expression: Expression | undefined; - if (isSingleOrDoubleQuote(firstChar)) { - expression = createLiteral(symbolName.substring(1, symbolName.length - 1).replace(/\\./g, s => s.substring(1))); - (expression as StringLiteral).singleQuote = firstChar === CharacterCodes.singleQuote; - } - else if (("" + +symbolName) === symbolName) { - expression = createLiteral(+symbolName); - } - if (!expression) { - expression = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); - expression.symbol = symbol; - } - return createElementAccess(createExpressionFromSymbolChain(chain, index - 1), expression); - } - } - } - - function isSingleQuotedStringNamed(d: Declaration) { - const name = getNameOfDeclaration(d); - if (name && isStringLiteral(name) && ( - name.singleQuote || - (!nodeIsSynthesized(name) && startsWith(getTextOfNode(name, /*includeTrivia*/ false), "'")) - )) { - return true; - } - return false; - } - - function getPropertyNameNodeForSymbol(symbol: Symbol, context: NodeBuilderContext) { - const singleQuote = !!length(symbol.declarations) && every(symbol.declarations, isSingleQuotedStringNamed); - const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context, singleQuote); - if (fromNameType) { - return fromNameType; - } - if (isKnownSymbol(symbol)) { - return createComputedPropertyName(createPropertyAccess(createIdentifier("Symbol"), (symbol.escapedName as string).substr(3))); - } - const rawName = unescapeLeadingUnderscores(symbol.escapedName); - return createPropertyNameNodeForIdentifierOrLiteral(rawName, singleQuote); - } - - // See getNameForSymbolFromNameType for a stringy equivalent - function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote?: boolean) { - const nameType = symbol.nameType; - if (nameType) { - if (nameType.flags & TypeFlags.StringOrNumberLiteral) { - const name = "" + (nameType).value; - if (!isIdentifierText(name, compilerOptions.target) && !isNumericLiteralName(name)) { - return createLiteral(name, !!singleQuote); - } - if (isNumericLiteralName(name) && startsWith(name, "-")) { - return createComputedPropertyName(createLiteral(+name)); - } - return createPropertyNameNodeForIdentifierOrLiteral(name); - } - if (nameType.flags & TypeFlags.UniqueESSymbol) { - return createComputedPropertyName(symbolToExpression((nameType).symbol, context, SymbolFlags.Value)); - } - } - } - - function createPropertyNameNodeForIdentifierOrLiteral(name: string, singleQuote?: boolean) { - return isIdentifierText(name, compilerOptions.target) ? createIdentifier(name) : createLiteral(isNumericLiteralName(name) ? +name : name, !!singleQuote); - } - - function cloneNodeBuilderContext(context: NodeBuilderContext): NodeBuilderContext { - const initial: NodeBuilderContext = { ...context }; - // Make type parameters created within this context not consume the name outside this context - // The symbol serializer ends up creating many sibling scopes that all need "separate" contexts when - // it comes to naming things - within a normal `typeToTypeNode` call, the node builder only ever descends - // through the type tree, so the only cases where we could have used distinct sibling scopes was when there - // were multiple generic overloads with similar generated type parameter names - // The effect: - // When we write out - // export const x: (x: T) => T - // export const y: (x: T) => T - // we write it out like that, rather than as - // export const x: (x: T) => T - // export const y: (x: T_1) => T_1 - if (initial.typeParameterNames) { - initial.typeParameterNames = cloneMap(initial.typeParameterNames); - } - if (initial.typeParameterNamesByText) { - initial.typeParameterNamesByText = cloneMap(initial.typeParameterNamesByText); - } - if (initial.typeParameterSymbolList) { - initial.typeParameterSymbolList = cloneMap(initial.typeParameterSymbolList); - } - return initial; - } - - function symbolTableToDeclarationStatements(symbolTable: SymbolTable, context: NodeBuilderContext, bundled?: boolean): Statement[] { - const serializePropertySymbolForClass = makeSerializePropertySymbol(createProperty, SyntaxKind.MethodDeclaration, /*useAcessors*/ true); - const serializePropertySymbolForInterfaceWorker = makeSerializePropertySymbol((_decorators, mods, name, question, type, initializer) => createPropertySignature(mods, name, question, type, initializer), SyntaxKind.MethodSignature, /*useAcessors*/ false); - - // TODO: Use `setOriginalNode` on original declaration names where possible so these declarations see some kind of - // declaration mapping - - // We save the enclosing declaration off here so it's not adjusted by well-meaning declaration - // emit codepaths which want to apply more specific contexts (so we can still refer to the root real declaration - // we're trying to emit from later on) - const enclosingDeclaration = context.enclosingDeclaration!; - let results: Statement[] = []; - const visitedSymbols: Map = createMap(); - let deferredPrivates: Map | undefined; - const oldcontext = context; - context = { - ...oldcontext, - usedSymbolNames: createMap(), - remappedSymbolNames: createMap(), - tracker: { - ...oldcontext.tracker, - trackSymbol: (sym, decl, meaning) => { - const accessibleResult = isSymbolAccessible(sym, decl, meaning, /*computeALiases*/ false); - if (accessibleResult.accessibility === SymbolAccessibility.Accessible) { - // Lookup the root symbol of the chain of refs we'll use to access it and serialize it - const chain = lookupSymbolChainWorker(sym, context, meaning); - if (!(sym.flags & SymbolFlags.Property)) { - includePrivateSymbol(chain[0]); - } - } - else if (oldcontext.tracker && oldcontext.tracker.trackSymbol) { - oldcontext.tracker.trackSymbol(sym, decl, meaning); - } - } - } - }; - if (oldcontext.usedSymbolNames) { - oldcontext.usedSymbolNames.forEach((_, name) => { - context.usedSymbolNames!.set(name, true); - }); - } - forEachEntry(symbolTable, (symbol, name) => { - const baseName = unescapeLeadingUnderscores(name); - void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames` - }); - let addingDeclare = !bundled; - const exportEquals = symbolTable.get(InternalSymbolName.ExportEquals); - if (exportEquals && symbolTable.size > 1 && exportEquals.flags & SymbolFlags.Alias) { - symbolTable = createSymbolTable(); - // Remove extraneous elements from root symbol table (they'll be mixed back in when the target of the `export=` is looked up) - symbolTable.set(InternalSymbolName.ExportEquals, exportEquals); - } - - visitSymbolTable(symbolTable); - return mergeRedundantStatements(results); - - function isIdentifierAndNotUndefined(node: Node | undefined): node is Identifier { - return !!node && node.kind === SyntaxKind.Identifier; - } - - function getNamesOfDeclaration(statement: Statement): Identifier[] { - if (isVariableStatement(statement)) { - return filter(map(statement.declarationList.declarations, getNameOfDeclaration), isIdentifierAndNotUndefined); - } - return filter([getNameOfDeclaration(statement as DeclarationStatement)], isIdentifierAndNotUndefined); - } - - function flattenExportAssignedNamespace(statements: Statement[]) { - const exportAssignment = find(statements, isExportAssignment); - const ns = find(statements, isModuleDeclaration); - if (ns && exportAssignment && exportAssignment.isExportEquals && - isIdentifier(exportAssignment.expression) && isIdentifier(ns.name) && idText(ns.name) === idText(exportAssignment.expression) && - ns.body && isModuleBlock(ns.body)) { - // Pass 0: Correct situations where a module has both an `export = ns` and multiple top-level exports by stripping the export modifiers from - // the top-level exports and exporting them in the targeted ns, as can occur when a js file has both typedefs and `module.export` assignments - const excessExports = filter(statements, s => !!(getModifierFlags(s) & ModifierFlags.Export)); - if (length(excessExports)) { - ns.body.statements = createNodeArray([...ns.body.statements, createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(map(flatMap(excessExports, e => getNamesOfDeclaration(e)), id => createExportSpecifier(/*alias*/ undefined, id))), - /*moduleSpecifier*/ undefined - )]); - } - - - // Pass 1: Flatten `export namespace _exports {} export = _exports;` so long as the `export=` only points at a single namespace declaration - if (!find(statements, s => s !== ns && nodeHasName(s, ns.name as Identifier))) { - results = []; - forEach(ns.body.statements, s => { - addResult(s, ModifierFlags.None); // Recalculates the ambient (and export, if applicable from above) flag - }); - statements = [...filter(statements, s => s !== ns && s !== exportAssignment), ...results]; - } - } - return statements; - } - - function mergeExportDeclarations(statements: Statement[]) { - // Pass 2: Combine all `export {}` declarations - const exports = filter(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[]; - if (length(exports) > 1) { - const nonExports = filter(statements, d => !isExportDeclaration(d) || !!d.moduleSpecifier || !d.exportClause); - statements = [...nonExports, createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(flatMap(exports, e => cast(e.exportClause, isNamedExports).elements)), - /*moduleSpecifier*/ undefined - )]; - } - // Pass 2b: Also combine all `export {} from "..."` declarations as needed - const reexports = filter(statements, d => isExportDeclaration(d) && !!d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[]; - if (length(reexports) > 1) { - const groups = group(reexports, decl => isStringLiteral(decl.moduleSpecifier!) ? ">" + decl.moduleSpecifier.text : ">"); - if (groups.length !== reexports.length) { - for (const group of groups) { - if (group.length > 1) { - // remove group members from statements and then merge group members and add back to statements - statements = [ - ...filter(statements, s => group.indexOf(s as ExportDeclaration) === -1), - createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(flatMap(group, e => cast(e.exportClause, isNamedExports).elements)), - group[0].moduleSpecifier - ) - ]; - } - } - } - } - return statements; - } - - function inlineExportModifiers(statements: Statement[]) { - // Pass 3: Move all `export {}`'s to `export` modifiers where possible - const exportDecl = find(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause) as ExportDeclaration | undefined; - if (exportDecl && exportDecl.exportClause && isNamedExports(exportDecl.exportClause)) { - const replacements = mapDefined(exportDecl.exportClause.elements, e => { - if (!e.propertyName) { - // export {name} - look thru `statements` for `name`, and if all results can take an `export` modifier, do so and filter it - const associated = filter(statements, s => nodeHasName(s, e.name)); - if (length(associated) && every(associated, canHaveExportModifier)) { - forEach(associated, addExportModifier); - return undefined; - } - } - return e; - }); - if (!length(replacements)) { - // all clauses removed, filter the export declaration - statements = filter(statements, s => s !== exportDecl); - } - else { - // some items filtered, others not - update the export declaration - // (mutating because why not, we're building a whole new tree here anyway) - exportDecl.exportClause.elements = createNodeArray(replacements); - } - } - return statements; - } - - function mergeRedundantStatements(statements: Statement[]) { - statements = flattenExportAssignedNamespace(statements); - statements = mergeExportDeclarations(statements); - statements = inlineExportModifiers(statements); - - // Not a cleanup, but as a final step: If there is a mix of `export` and non-`export` declarations, but no `export =` or `export {}` add a `export {};` so - // declaration privacy is respected. - if (enclosingDeclaration && - ((isSourceFile(enclosingDeclaration) && isExternalOrCommonJsModule(enclosingDeclaration)) || isModuleDeclaration(enclosingDeclaration)) && - (!some(statements, isExternalModuleIndicator) || (!hasScopeMarker(statements) && some(statements, needsScopeMarker)))) { - statements.push(createEmptyExports()); - } - return statements; - } - - function canHaveExportModifier(node: Statement) { - return isEnumDeclaration(node) || - isVariableStatement(node) || - isFunctionDeclaration(node) || - isClassDeclaration(node) || - (isModuleDeclaration(node) && !isExternalModuleAugmentation(node) && !isGlobalScopeAugmentation(node)) || - isInterfaceDeclaration(node) || - isTypeDeclaration(node); - } - - function addExportModifier(statement: Statement) { - const flags = (getModifierFlags(statement) | ModifierFlags.Export) & ~ModifierFlags.Ambient; - statement.modifiers = createNodeArray(createModifiersFromModifierFlags(flags)); - statement.modifierFlagsCache = 0; - } - - function visitSymbolTable(symbolTable: SymbolTable, suppressNewPrivateContext?: boolean, propertyAsAlias?: boolean) { - const oldDeferredPrivates = deferredPrivates; - if (!suppressNewPrivateContext) { - deferredPrivates = createMap(); - } - symbolTable.forEach((symbol: Symbol) => { - serializeSymbol(symbol, /*isPrivate*/ false, !!propertyAsAlias); - }); - if (!suppressNewPrivateContext) { - // deferredPrivates will be filled up by visiting the symbol table - // And will continue to iterate as elements are added while visited `deferredPrivates` - // (As that's how a map iterator is defined to work) - deferredPrivates!.forEach((symbol: Symbol) => { - serializeSymbol(symbol, /*isPrivate*/ true, !!propertyAsAlias); - }); - } - deferredPrivates = oldDeferredPrivates; - } - - function serializeSymbol(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean) { - // cache visited list based on merged symbol, since we want to use the unmerged top-level symbol, but - // still skip reserializing it if we encounter the merged product later on - const visitedSym = getMergedSymbol(symbol); - if (visitedSymbols.has("" + getSymbolId(visitedSym))) { - return; // Already printed - } - visitedSymbols.set("" + getSymbolId(visitedSym), true); - // Only actually serialize symbols within the correct enclosing declaration, otherwise do nothing with the out-of-context symbol - const skipMembershipCheck = !isPrivate; // We only call this on exported symbols when we know they're in the correct scope - if (skipMembershipCheck || (!!length(symbol.declarations) && some(symbol.declarations, d => !!findAncestor(d, n => n === enclosingDeclaration)))) { - const oldContext = context; - context = cloneNodeBuilderContext(context); - const result = serializeSymbolWorker(symbol, isPrivate, propertyAsAlias); - context = oldContext; - return result; - } - } - - // Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias - // or a merge of some number of those. - // An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping - // each symbol in only one of the representations - // Also, synthesizing a default export of some kind - // If it's an alias: emit `export default ref` - // If it's a property: emit `export default _default` with a `_default` prop - // If it's a class/interface/function: emit a class/interface/function with a `default` modifier - // These forms can merge, eg (`export default 12; export default interface A {}`) - function serializeSymbolWorker(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean) { - const symbolName = unescapeLeadingUnderscores(symbol.escapedName); - const isDefault = symbol.escapedName === InternalSymbolName.Default; - if (isStringANonContextualKeyword(symbolName) && !isDefault) { - // Oh no. We cannot use this symbol's name as it's name... It's likely some jsdoc had an invalid name like `export` or `default` :( - context.encounteredError = true; - // TODO: Issue error via symbol tracker? - return; // If we need to emit a private with a keyword name, we're done for, since something else will try to refer to it by that name - } - const needsPostExportDefault = isDefault && !!( - symbol.flags & SymbolFlags.ExportDoesNotSupportDefaultModifier - || (symbol.flags & SymbolFlags.Function && length(getPropertiesOfType(getTypeOfSymbol(symbol)))) - ) && !(symbol.flags & SymbolFlags.Alias); // An alias symbol should preclude needing to make an alias ourselves - if (needsPostExportDefault) { - isPrivate = true; - } - const modifierFlags = (!isPrivate ? ModifierFlags.Export : 0) | (isDefault && !needsPostExportDefault ? ModifierFlags.Default : 0); - const isConstMergedWithNS = symbol.flags & SymbolFlags.Module && - symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property) && - symbol.escapedName !== InternalSymbolName.ExportEquals; - const isConstMergedWithNSPrintableAsSignatureMerge = isConstMergedWithNS && isTypeRepresentableAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol); - if (symbol.flags & SymbolFlags.Function || isConstMergedWithNSPrintableAsSignatureMerge) { - serializeAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); - } - if (symbol.flags & SymbolFlags.TypeAlias) { - serializeTypeAlias(symbol, symbolName, modifierFlags); - } - // Need to skip over export= symbols below - json source files get a single `Property` flagged - // symbol of name `export=` which needs to be handled like an alias. It's not great, but it is what it is. - if (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property) - && symbol.escapedName !== InternalSymbolName.ExportEquals - && !(symbol.flags & SymbolFlags.Prototype) - && !(symbol.flags & SymbolFlags.Class) - && !isConstMergedWithNSPrintableAsSignatureMerge) { - serializeVariableOrProperty(symbol, symbolName, isPrivate, needsPostExportDefault, propertyAsAlias, modifierFlags); - } - if (symbol.flags & SymbolFlags.Enum) { - serializeEnum(symbol, symbolName, modifierFlags); - } - if (symbol.flags & SymbolFlags.Class) { - if (symbol.flags & SymbolFlags.Property) { - // Looks like a `module.exports.Sub = class {}` - if we serialize `symbol` as a class, the result will have no members, - // since the classiness is actually from the target of the effective alias the symbol is. yes. A BlockScopedVariable|Class|Property - // _really_ acts like an Alias, and none of a BlockScopedVariable, Class, or Property. This is the travesty of JS binding today. - serializeAsAlias(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); - } - else { - serializeAsClass(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); - } - } - if ((symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && (!isConstMergedWithNS || isTypeOnlyNamespace(symbol))) || isConstMergedWithNSPrintableAsSignatureMerge) { - serializeModule(symbol, symbolName, modifierFlags); - } - if (symbol.flags & SymbolFlags.Interface) { - serializeInterface(symbol, symbolName, modifierFlags); - } - if (symbol.flags & SymbolFlags.Alias) { - serializeAsAlias(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); - } - if (symbol.flags & SymbolFlags.Property && symbol.escapedName === InternalSymbolName.ExportEquals) { - serializeMaybeAliasAssignment(symbol); - } - if (symbol.flags & SymbolFlags.ExportStar) { - // synthesize export * from "moduleReference" - // Straightforward - only one thing to do - make an export declaration - for (const node of symbol.declarations) { - const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); - if (!resolvedModule) continue; - addResult(createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*exportClause*/ undefined, createLiteral(getSpecifierForModuleSymbol(resolvedModule, context))), ModifierFlags.None); - } - } - if (needsPostExportDefault) { - addResult(createExportAssignment(/*decorators*/ undefined, /*modifiers*/ undefined, /*isExportAssignment*/ false, createIdentifier(getInternalSymbolName(symbol, symbolName))), ModifierFlags.None); - } - } - - function includePrivateSymbol(symbol: Symbol) { - if (some(symbol.declarations, isParameterDeclaration)) return; - Debug.assertDefined(deferredPrivates); - getUnusedName(unescapeLeadingUnderscores(symbol.escapedName), symbol); // Call to cache unique name for symbol - deferredPrivates!.set("" + getSymbolId(symbol), symbol); - } - - function isExportingScope(enclosingDeclaration: Node) { - return ((isSourceFile(enclosingDeclaration) && (isExternalOrCommonJsModule(enclosingDeclaration) || isJsonSourceFile(enclosingDeclaration))) || - (isAmbientModule(enclosingDeclaration) && !isGlobalScopeAugmentation(enclosingDeclaration))); - } - - // Prepends a `declare` and/or `export` modifier if the context requires it, and then adds `node` to `result` and returns `node` - // Note: This _mutates_ `node` without using `updateNode` - the assumption being that all nodes should be manufactured fresh by the node builder - function addResult(node: Statement, additionalModifierFlags: ModifierFlags) { - let newModifierFlags: ModifierFlags = ModifierFlags.None; - if (additionalModifierFlags & ModifierFlags.Export && - enclosingDeclaration && - isExportingScope(enclosingDeclaration) && - canHaveExportModifier(node) - ) { - // Classes, namespaces, variables, functions, interfaces, and types should all be `export`ed in a module context if not private - newModifierFlags |= ModifierFlags.Export; - } - if (addingDeclare && !(newModifierFlags & ModifierFlags.Export) && - (!enclosingDeclaration || !(enclosingDeclaration.flags & NodeFlags.Ambient)) && - (isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node) || isModuleDeclaration(node))) { - // Classes, namespaces, variables, enums, and functions all need `declare` modifiers to be valid in a declaration file top-level scope - newModifierFlags |= ModifierFlags.Ambient; - } - if ((additionalModifierFlags & ModifierFlags.Default) && (isClassDeclaration(node) || isInterfaceDeclaration(node) || isFunctionDeclaration(node))) { - newModifierFlags |= ModifierFlags.Default; - } - if (newModifierFlags) { - node.modifiers = createNodeArray(createModifiersFromModifierFlags(newModifierFlags | getModifierFlags(node))); - node.modifierFlagsCache = 0; // Reset computed flags cache - } - results.push(node); - } - - function serializeTypeAlias(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { - const aliasType = getDeclaredTypeOfTypeAlias(symbol); - const typeParams = getSymbolLinks(symbol).typeParameters; - const typeParamDecls = map(typeParams, p => typeParameterToDeclaration(p, context)); - const jsdocAliasDecl = find(symbol.declarations, isJSDocTypeAlias); - const commentText = jsdocAliasDecl ? jsdocAliasDecl.comment || jsdocAliasDecl.parent.comment : undefined; - const oldFlags = context.flags; - context.flags |= NodeBuilderFlags.InTypeAlias; - addResult(setSyntheticLeadingComments( - createTypeAliasDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, getInternalSymbolName(symbol, symbolName), typeParamDecls, typeToTypeNodeHelper(aliasType, context)), - !commentText ? [] : [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }] - ), modifierFlags); - context.flags = oldFlags; - } - - function serializeInterface(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { - const interfaceType = getDeclaredTypeOfClassOrInterface(symbol); - const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context)); - const baseTypes = getBaseTypes(interfaceType); - const baseType = length(baseTypes) ? getIntersectionType(baseTypes) : undefined; - const members = flatMap(getPropertiesOfType(interfaceType), p => serializePropertySymbolForInterface(p, baseType)); - const callSignatures = serializeSignatures(SignatureKind.Call, interfaceType, baseType, SyntaxKind.CallSignature) as CallSignatureDeclaration[]; - const constructSignatures = serializeSignatures(SignatureKind.Construct, interfaceType, baseType, SyntaxKind.ConstructSignature) as ConstructSignatureDeclaration[]; - const indexSignatures = serializeIndexSignatures(interfaceType, baseType); - - const heritageClauses = !length(baseTypes) ? undefined : [createHeritageClause(SyntaxKind.ExtendsKeyword, mapDefined(baseTypes, b => trySerializeAsTypeReference(b)))]; - addResult(createInterfaceDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - getInternalSymbolName(symbol, symbolName), - typeParamDecls, - heritageClauses, - [...indexSignatures, ...constructSignatures, ...callSignatures, ...members] - ), modifierFlags); - } - - function getNamespaceMembersForSerialization(symbol: Symbol) { - return !symbol.exports ? [] : filter(arrayFrom((symbol.exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype"))); - } - - function isTypeOnlyNamespace(symbol: Symbol) { - return every(getNamespaceMembersForSerialization(symbol), m => !(resolveSymbol(m).flags & SymbolFlags.Value)); - } - - function serializeModule(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { - const members = getNamespaceMembersForSerialization(symbol); - // Split NS members up by declaration - members whose parent symbol is the ns symbol vs those whose is not (but were added in later via merging) - const locationMap = arrayToMultiMap(members, m => m.parent && m.parent === symbol ? "real" : "merged"); - const realMembers = locationMap.get("real") || emptyArray; - const mergedMembers = locationMap.get("merged") || emptyArray; - // TODO: `suppressNewPrivateContext` is questionable -we need to simply be emitting privates in whatever scope they were declared in, rather - // than whatever scope we traverse to them in. That's a bit of a complex rewrite, since we're not _actually_ tracking privates at all in advance, - // so we don't even have placeholders to fill in. - if (length(realMembers)) { - const localName = getInternalSymbolName(symbol, symbolName); - serializeAsNamespaceDeclaration(realMembers, localName, modifierFlags, !!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Assignment))); - } - if (length(mergedMembers)) { - const localName = getInternalSymbolName(symbol, symbolName); - const nsBody = createModuleBlock([createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(map(filter(mergedMembers, n => n.escapedName !== InternalSymbolName.ExportEquals), s => { - const name = unescapeLeadingUnderscores(s.escapedName); - const localName = getInternalSymbolName(s, name); - const aliasDecl = s.declarations && getDeclarationOfAliasSymbol(s); - const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true); - includePrivateSymbol(target || s); - const targetName = target ? getInternalSymbolName(target, unescapeLeadingUnderscores(target.escapedName)) : localName; - return createExportSpecifier(name === targetName ? undefined : targetName, name); - })) - )]); - addResult(createModuleDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createIdentifier(localName), - nsBody, - NodeFlags.Namespace - ), ModifierFlags.None); - } - } - - function serializeEnum(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { - addResult(createEnumDeclaration( - /*decorators*/ undefined, - createModifiersFromModifierFlags(isConstEnumSymbol(symbol) ? ModifierFlags.Const : 0), - getInternalSymbolName(symbol, symbolName), - map(filter(getPropertiesOfType(getTypeOfSymbol(symbol)), p => !!(p.flags & SymbolFlags.EnumMember)), p => { - // TODO: Handle computed names - // I hate that to get the initialized value we need to walk back to the declarations here; but there's no - // other way to get the possible const value of an enum member that I'm aware of, as the value is cached - // _on the declaration_, not on the declaration's symbol... - const initializedValue = p.declarations && p.declarations[0] && isEnumMember(p.declarations[0]) && getConstantValue(p.declarations[0] as EnumMember); - return createEnumMember(unescapeLeadingUnderscores(p.escapedName), initializedValue === undefined ? undefined : createLiteral(initializedValue)); - }) - ), modifierFlags); - } - - function serializeVariableOrProperty(symbol: Symbol, symbolName: string, isPrivate: boolean, needsPostExportDefault: boolean, propertyAsAlias: boolean | undefined, modifierFlags: ModifierFlags) { - if (propertyAsAlias) { - serializeMaybeAliasAssignment(symbol); - } - else { - const type = getTypeOfSymbol(symbol); - const localName = getInternalSymbolName(symbol, symbolName); - if (!(symbol.flags & SymbolFlags.Function) && isTypeRepresentableAsFunctionNamespaceMerge(type, symbol)) { - // If the type looks like a function declaration + ns could represent it, and it's type is sourced locally, rewrite it into a function declaration + ns - serializeAsFunctionNamespaceMerge(type, symbol, localName, modifierFlags); - } - else { - // A Class + Property merge is made for a `module.exports.Member = class {}`, and it doesn't serialize well as either a class _or_ a property symbol - in fact, _it behaves like an alias!_ - // `var` is `FunctionScopedVariable`, `const` and `let` are `BlockScopedVariable`, and `module.exports.thing =` is `Property` - const flags = !(symbol.flags & SymbolFlags.BlockScopedVariable) ? undefined - : isConstVariable(symbol) ? NodeFlags.Const - : NodeFlags.Let; - const name = (needsPostExportDefault || !(symbol.flags & SymbolFlags.Property)) ? localName : getUnusedName(localName, symbol); - let textRange: Node | undefined = symbol.declarations && find(symbol.declarations, d => isVariableDeclaration(d)); - if (textRange && isVariableDeclarationList(textRange.parent) && textRange.parent.declarations.length === 1) { - textRange = textRange.parent.parent; - } - const statement = setTextRange(createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([ - createVariableDeclaration(name, serializeTypeForDeclaration(type, symbol)) - ], flags)), textRange); - addResult(statement, name !== localName ? modifierFlags & ~ModifierFlags.Export : modifierFlags); - if (name !== localName && !isPrivate) { - // We rename the variable declaration we generate for Property symbols since they may have a name which - // conflicts with a local declaration. For example, given input: - // ``` - // function g() {} - // module.exports.g = g - // ``` - // In such a situation, we have a local variable named `g`, and a seperate exported variable named `g`. - // Naively, we would emit - // ``` - // function g() {} - // export const g: typeof g; - // ``` - // That's obviously incorrect - the `g` in the type annotation needs to refer to the local `g`, but - // the export declaration shadows it. - // To work around that, we instead write - // ``` - // function g() {} - // const g_1: typeof g; - // export { g_1 as g }; - // ``` - // To create an export named `g` that does _not_ shadow the local `g` - addResult( - createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports([createExportSpecifier(name, localName)]) - ), - ModifierFlags.None - ); - } - } - } - } - - function serializeAsFunctionNamespaceMerge(type: Type, symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { - const signatures = getSignaturesOfType(type, SignatureKind.Call); - for (const sig of signatures) { - // Each overload becomes a separate function declaration, in order - const decl = signatureToSignatureDeclarationHelper(sig, SyntaxKind.FunctionDeclaration, context) as FunctionDeclaration; - decl.name = createIdentifier(localName); - addResult(setTextRange(decl, sig.declaration), modifierFlags); - } - // Module symbol emit will take care of module-y members, provided it has exports - if (!(symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && !!symbol.exports && !!symbol.exports.size)) { - const props = filter(getPropertiesOfType(type), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype"))); - serializeAsNamespaceDeclaration(props, localName, modifierFlags, /*suppressNewPrivateContext*/ true); - } - } - - function serializeAsNamespaceDeclaration(props: readonly Symbol[], localName: string, modifierFlags: ModifierFlags, suppressNewPrivateContext: boolean) { - if (length(props)) { - const localVsRemoteMap = arrayToMultiMap(props, p => - !length(p.declarations) || some(p.declarations, d => - getSourceFileOfNode(d) === getSourceFileOfNode(context.enclosingDeclaration!) - ) ? "local" : "remote" - ); - const localProps = localVsRemoteMap.get("local") || emptyArray; - // handle remote props first - we need to make an `import` declaration that points at the module containing each remote - // prop in the outermost scope (TODO: a namespace within a namespace would need to be appropriately handled by this) - // Example: - // import Foo_1 = require("./exporter"); - // export namespace ns { - // import Foo = Foo_1.Foo; - // export { Foo }; - // export const c: number; - // } - // This is needed because in JS, statements like `const x = require("./f")` support both type and value lookup, even if they're - // normally just value lookup (so it functions kinda like an alias even when it's not an alias) - // _Usually_, we'll simply print the top-level as an alias instead of a `var` in such situations, however is is theoretically - // possible to encounter a situation where a type has members from both the current file and other files - in those situations, - // emit akin to the above would be needed. - - // Add a namespace - const fakespace = createModuleDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createIdentifier(localName), createModuleBlock([]), NodeFlags.Namespace); - fakespace.flags ^= NodeFlags.Synthesized; // unset synthesized so it is usable as an enclosing declaration - fakespace.parent = enclosingDeclaration as SourceFile | NamespaceDeclaration; - fakespace.locals = createSymbolTable(props); - fakespace.symbol = props[0].parent!; - const oldResults = results; - results = []; - const oldAddingDeclare = addingDeclare; - addingDeclare = false; - const subcontext = { ...context, enclosingDeclaration: fakespace }; - const oldContext = context; - context = subcontext; - // TODO: implement handling for the localVsRemoteMap.get("remote") - should be difficult to trigger (see comment above), as only interesting cross-file js merges should make this possible - visitSymbolTable(createSymbolTable(localProps), suppressNewPrivateContext, /*propertyAsAlias*/ true); - context = oldContext; - addingDeclare = oldAddingDeclare; - const declarations = results; - results = oldResults; - fakespace.flags ^= NodeFlags.Synthesized; // reset synthesized - fakespace.parent = undefined!; - fakespace.locals = undefined!; - fakespace.symbol = undefined!; - fakespace.body = createModuleBlock(declarations); - addResult(fakespace, modifierFlags); // namespaces can never be default exported - } - } - - function serializeAsClass(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { - const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context)); - const classType = getDeclaredTypeOfClassOrInterface(symbol); - const baseTypes = getBaseTypes(classType); - const staticType = getTypeOfSymbol(symbol); - const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType); - const heritageClauses = !length(baseTypes) ? undefined : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))]; - const symbolProps = getPropertiesOfType(classType); - const publicSymbolProps = filter(symbolProps, s => { - const valueDecl = s.valueDeclaration; - Debug.assertDefined(valueDecl); - return !(isNamedDeclaration(valueDecl) && isPrivateIdentifier(valueDecl.name)); - }); - const hasPrivateIdentifier = some(symbolProps, s => { - const valueDecl = s.valueDeclaration; - Debug.assertDefined(valueDecl); - return isNamedDeclaration(valueDecl) && isPrivateIdentifier(valueDecl.name); - }); - // Boil down all private properties into a single one. - const privateProperties = hasPrivateIdentifier ? - [createProperty( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createPrivateIdentifier("#private"), - /*questionOrExclamationToken*/ undefined, - /*type*/ undefined, - /*initializer*/ undefined, - )] : - emptyArray; - const publicProperties = flatMap(publicSymbolProps, p => serializePropertySymbolForClass(p, /*isStatic*/ false, baseTypes[0])); - // Consider static members empty if symbol also has function or module meaning - function namespacey emit will handle statics - const staticMembers = symbol.flags & (SymbolFlags.Function | SymbolFlags.ValueModule) - ? [] - : flatMap(filter( - getPropertiesOfType(staticType), - p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" - ), p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType)); - const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[]; - for (const c of constructors) { - // A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration - // `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here - c.type = undefined; - c.typeParameters = undefined; - } - const indexSignatures = serializeIndexSignatures(classType, baseTypes[0]); - addResult(setTextRange(createClassDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - localName, - typeParamDecls, - heritageClauses, - [...indexSignatures, ...staticMembers, ...constructors, ...publicProperties, ...privateProperties] - ), symbol.declarations && filter(symbol.declarations, d => isClassDeclaration(d) || isClassExpression(d))[0]), modifierFlags); - } - - function serializeAsAlias(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { - // synthesize an alias, eg `export { symbolName as Name }` - // need to mark the alias `symbol` points - // at as something we need to serialize as a private declaration as well - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - const target = getMergedSymbol(getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true)); - if (!target) { - return; - } - let verbatimTargetName = unescapeLeadingUnderscores(target.escapedName); - if (verbatimTargetName === InternalSymbolName.ExportEquals && (compilerOptions.esModuleInterop || compilerOptions.allowSyntheticDefaultImports)) { - // target refers to an `export=` symbol that was hoisted into a synthetic default - rename here to match - verbatimTargetName = InternalSymbolName.Default; - } - const targetName = getInternalSymbolName(target, verbatimTargetName); - includePrivateSymbol(target); // the target may be within the same scope - attempt to serialize it first - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - // Could be a local `import localName = ns.member` or - // an external `import localName = require("whatever")` - const isLocalImport = !(target.flags & SymbolFlags.ValueModule); - addResult(createImportEqualsDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createIdentifier(localName), - isLocalImport - ? symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false) - : createExternalModuleReference(createLiteral(getSpecifierForModuleSymbol(symbol, context))) - ), isLocalImport ? modifierFlags : ModifierFlags.None); - break; - case SyntaxKind.NamespaceExportDeclaration: - // export as namespace foo - // TODO: Not part of a file's local or export symbol tables - // Is bound into file.symbol.globalExports instead, which we don't currently traverse - addResult(createNamespaceExportDeclaration(idText((node as NamespaceExportDeclaration).name)), ModifierFlags.None); - break; - case SyntaxKind.ImportClause: - addResult(createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause(createIdentifier(localName), /*namedBindings*/ undefined), - // We use `target.parent || target` below as `target.parent` is unset when the target is a module which has been export assigned - // And then made into a default by the `esModuleInterop` or `allowSyntheticDefaultImports` flag - // In such cases, the `target` refers to the module itself already - createLiteral(getSpecifierForModuleSymbol(target.parent || target, context)) - ), ModifierFlags.None); - break; - case SyntaxKind.NamespaceImport: - addResult(createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause(/*importClause*/ undefined, createNamespaceImport(createIdentifier(localName))), - createLiteral(getSpecifierForModuleSymbol(target, context)) - ), ModifierFlags.None); - break; - case SyntaxKind.NamespaceExport: - addResult(createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamespaceExport(createIdentifier(localName)), - createLiteral(getSpecifierForModuleSymbol(target, context)) - ), ModifierFlags.None); - break; - case SyntaxKind.ImportSpecifier: - addResult(createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause(/*importClause*/ undefined, createNamedImports([ - createImportSpecifier( - localName !== verbatimTargetName ? createIdentifier(verbatimTargetName) : undefined, - createIdentifier(localName) - ) - ])), - createLiteral(getSpecifierForModuleSymbol(target.parent || target, context)) - ), ModifierFlags.None); - break; - case SyntaxKind.ExportSpecifier: - // does not use localName because the symbol name in this case refers to the name in the exports table, - // which we must exactly preserve - const specifier = (node.parent.parent as ExportDeclaration).moduleSpecifier; - // targetName is only used when the target is local, as otherwise the target is an alias that points at - // another file - serializeExportSpecifier( - unescapeLeadingUnderscores(symbol.escapedName), - specifier ? verbatimTargetName : targetName, - specifier && isStringLiteralLike(specifier) ? createLiteral(specifier.text) : undefined - ); - break; - case SyntaxKind.ExportAssignment: - serializeMaybeAliasAssignment(symbol); - break; - case SyntaxKind.BinaryExpression: - case SyntaxKind.PropertyAccessExpression: - // Could be best encoded as though an export specifier or as though an export assignment - // If name is default or export=, do an export assignment - // Otherwise do an export specifier - if (symbol.escapedName === InternalSymbolName.Default || symbol.escapedName === InternalSymbolName.ExportEquals) { - serializeMaybeAliasAssignment(symbol); - } - else { - serializeExportSpecifier(localName, targetName); - } - break; - default: - return Debug.failBadSyntaxKind(node, "Unhandled alias declaration kind in symbol serializer!"); - } - } - - function serializeExportSpecifier(localName: string, targetName: string, specifier?: Expression) { - addResult(createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports([createExportSpecifier(localName !== targetName ? targetName : undefined, localName)]), - specifier - ), ModifierFlags.None); - } - - function serializeMaybeAliasAssignment(symbol: Symbol) { - if (symbol.flags & SymbolFlags.Prototype) { - return; - } - const name = unescapeLeadingUnderscores(symbol.escapedName); - const isExportEquals = name === InternalSymbolName.ExportEquals; - const isDefault = name === InternalSymbolName.Default; - const isExportAssignment = isExportEquals || isDefault; - // synthesize export = ref - // ref should refer to either be a locally scoped symbol which we need to emit, or - // a reference to another namespace/module which we may need to emit an `import` statement for - const aliasDecl = symbol.declarations && getDeclarationOfAliasSymbol(symbol); - // serialize what the alias points to, preserve the declaration's initializer - const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true); - // If the target resolves and resolves to a thing defined in this file, emit as an alias, otherwise emit as a const - if (target && length(target.declarations) && some(target.declarations, d => getSourceFileOfNode(d) === getSourceFileOfNode(enclosingDeclaration))) { - // In case `target` refers to a namespace member, look at the declaration and serialize the leftmost symbol in it - // eg, `namespace A { export class B {} }; exports = A.B;` - // Technically, this is all that's required in the case where the assignment is an entity name expression - const expr = isExportAssignment ? getExportAssignmentExpression(aliasDecl as ExportAssignment | BinaryExpression) : getPropertyAssignmentAliasLikeExpression(aliasDecl as ShorthandPropertyAssignment | PropertyAssignment | PropertyAccessExpression); - const first = isEntityNameExpression(expr) ? getFirstNonModuleExportsIdentifier(expr) : undefined; - const referenced = first && resolveEntityName(first, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, enclosingDeclaration); - if (referenced || target) { - includePrivateSymbol(referenced || target); - } - - // We disable the context's symbol traker for the duration of this name serialization - // as, by virtue of being here, the name is required to print something, and we don't want to - // issue a visibility error on it. Only anonymous classes that an alias points at _would_ issue - // a visibility error here (as they're not visible within any scope), but we want to hoist them - // into the containing scope anyway, so we want to skip the visibility checks. - const oldTrack = context.tracker.trackSymbol; - context.tracker.trackSymbol = noop; - if (isExportAssignment) { - results.push(createExportAssignment( - /*decorators*/ undefined, - /*modifiers*/ undefined, - isExportEquals, - symbolToExpression(target, context, SymbolFlags.All) - )); - } - else { - if (first === expr) { - // serialize as `export {target as name}` - serializeExportSpecifier(name, idText(first)); - } - else if (isClassExpression(expr)) { - serializeExportSpecifier(name, getInternalSymbolName(target, symbolName(target))); - } - else { - // serialize as `import _Ref = t.arg.et; export { _Ref as name }` - const varName = getUnusedName(name, symbol); - addResult(createImportEqualsDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createIdentifier(varName), - symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false) - ), ModifierFlags.None); - serializeExportSpecifier(name, varName); - } - } - context.tracker.trackSymbol = oldTrack; - } - else { - // serialize as an anonymous property declaration - const varName = getUnusedName(name, symbol); - // We have to use `getWidenedType` here since the object within a json file is unwidened within the file - // (Unwidened types can only exist in expression contexts and should never be serialized) - const typeToSerialize = getWidenedType(getTypeOfSymbol(symbol)); - if (isTypeRepresentableAsFunctionNamespaceMerge(typeToSerialize, symbol)) { - // If there are no index signatures and `typeToSerialize` is an object type, emit as a namespace instead of a const - serializeAsFunctionNamespaceMerge(typeToSerialize, symbol, varName, isExportAssignment ? ModifierFlags.None : ModifierFlags.Export); - } - else { - const statement = createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([ - createVariableDeclaration(varName, serializeTypeForDeclaration(typeToSerialize, symbol)) - ], NodeFlags.Const)); - addResult(statement, name === varName ? ModifierFlags.Export : ModifierFlags.None); - } - if (isExportAssignment) { - results.push(createExportAssignment( - /*decorators*/ undefined, - /*modifiers*/ undefined, - isExportEquals, - createIdentifier(varName) - )); - } - else if (name !== varName) { - serializeExportSpecifier(name, varName); - } - } - } - - function isTypeRepresentableAsFunctionNamespaceMerge(typeToSerialize: Type, hostSymbol: Symbol) { - // Only object types which are not constructable, or indexable, whose members all come from the - // context source file, and whose property names are all valid identifiers and not late-bound, _and_ - // whose input is not type annotated (if the input symbol has an annotation we can reuse, we should prefer it) - const ctxSrc = getSourceFileOfNode(context.enclosingDeclaration); - return getObjectFlags(typeToSerialize) & (ObjectFlags.Anonymous | ObjectFlags.Mapped) && - !getIndexInfoOfType(typeToSerialize, IndexKind.String) && - !getIndexInfoOfType(typeToSerialize, IndexKind.Number) && - !!(length(getPropertiesOfType(typeToSerialize)) || length(getSignaturesOfType(typeToSerialize, SignatureKind.Call))) && - !length(getSignaturesOfType(typeToSerialize, SignatureKind.Construct)) && // TODO: could probably serialize as function + ns + class, now that that's OK - !getDeclarationWithTypeAnnotation(hostSymbol) && - !(typeToSerialize.symbol && some(typeToSerialize.symbol.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) && - !some(getPropertiesOfType(typeToSerialize), p => isLateBoundName(p.escapedName)) && - !some(getPropertiesOfType(typeToSerialize), p => some(p.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) && - every(getPropertiesOfType(typeToSerialize), p => isIdentifierText(symbolName(p), languageVersion) && !isStringAKeyword(symbolName(p))); - } - - function makeSerializePropertySymbol(createProperty: ( - decorators: readonly Decorator[] | undefined, - modifiers: readonly Modifier[] | undefined, - name: string | PropertyName, - questionOrExclamationToken: QuestionToken | undefined, - type: TypeNode | undefined, - initializer: Expression | undefined - ) => T, methodKind: SyntaxKind, useAccessors: true): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => (T | AccessorDeclaration | (T | AccessorDeclaration)[]); - function makeSerializePropertySymbol(createProperty: ( - decorators: readonly Decorator[] | undefined, - modifiers: readonly Modifier[] | undefined, - name: string | PropertyName, - questionOrExclamationToken: QuestionToken | undefined, - type: TypeNode | undefined, - initializer: Expression | undefined - ) => T, methodKind: SyntaxKind, useAccessors: false): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => (T | T[]); - function makeSerializePropertySymbol(createProperty: ( - decorators: readonly Decorator[] | undefined, - modifiers: readonly Modifier[] | undefined, - name: string | PropertyName, - questionOrExclamationToken: QuestionToken | undefined, - type: TypeNode | undefined, - initializer: Expression | undefined - ) => T, methodKind: SyntaxKind, useAccessors: boolean): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => (T | AccessorDeclaration | (T | AccessorDeclaration)[]) { - return function serializePropertySymbol(p: Symbol, isStatic: boolean, baseType: Type | undefined) { - const modifierFlags = getDeclarationModifierFlagsFromSymbol(p); - const isPrivate = !!(modifierFlags & ModifierFlags.Private); - if (isStatic && (p.flags & (SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias))) { - // Only value-only-meaning symbols can be correctly encoded as class statics, type/namespace/alias meaning symbols - // need to be merged namespace members - return []; - } - if (p.flags & SymbolFlags.Prototype || (baseType && getPropertyOfType(baseType, p.escapedName) - && isReadonlySymbol(getPropertyOfType(baseType, p.escapedName)!) === isReadonlySymbol(p) - && (p.flags & SymbolFlags.Optional) === (getPropertyOfType(baseType, p.escapedName)!.flags & SymbolFlags.Optional) - && isTypeIdenticalTo(getTypeOfSymbol(p), getTypeOfPropertyOfType(baseType, p.escapedName)!))) { - return []; - } - const flag = modifierFlags | (isStatic ? ModifierFlags.Static : 0); - const name = getPropertyNameNodeForSymbol(p, context); - const firstPropertyLikeDecl = find(p.declarations, or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression)); - if (p.flags & SymbolFlags.Accessor && useAccessors) { - const result: AccessorDeclaration[] = []; - if (p.flags & SymbolFlags.SetAccessor) { - result.push(setTextRange(createSetAccessor( - /*decorators*/ undefined, - createModifiersFromModifierFlags(flag), - name, - [createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - "arg", - /*questionToken*/ undefined, - isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p) - )], - /*body*/ undefined - ), find(p.declarations, isSetAccessor) || firstPropertyLikeDecl)); - } - if (p.flags & SymbolFlags.GetAccessor) { - const isPrivate = modifierFlags & ModifierFlags.Private; - result.push(setTextRange(createGetAccessor( - /*decorators*/ undefined, - createModifiersFromModifierFlags(flag), - name, - [], - isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p), - /*body*/ undefined - ), find(p.declarations, isGetAccessor) || firstPropertyLikeDecl)); - } - return result; - } - // This is an else/if as accessors and properties can't merge in TS, but might in JS - // If this happens, we assume the accessor takes priority, as it imposes more constraints - else if (p.flags & (SymbolFlags.Property | SymbolFlags.Variable)) { - return setTextRange(createProperty( - /*decorators*/ undefined, - createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag), - name, - p.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined, - isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p), - // TODO: https://github.com/microsoft/TypeScript/pull/32372#discussion_r328386357 - // interface members can't have initializers, however class members _can_ - /*initializer*/ undefined - ), find(p.declarations, or(isPropertyDeclaration, isVariableDeclaration)) || firstPropertyLikeDecl); - } - if (p.flags & (SymbolFlags.Method | SymbolFlags.Function)) { - const type = getTypeOfSymbol(p); - const signatures = getSignaturesOfType(type, SignatureKind.Call); - if (flag & ModifierFlags.Private) { - return setTextRange(createProperty( - /*decorators*/ undefined, - createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag), - name, - p.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined, - /*type*/ undefined, - /*initializer*/ undefined - ), find(p.declarations, isFunctionLikeDeclaration) || signatures[0] && signatures[0].declaration || p.declarations[0]); - } - - const results = []; - for (const sig of signatures) { - // Each overload becomes a separate method declaration, in order - const decl = signatureToSignatureDeclarationHelper(sig, methodKind, context) as MethodDeclaration; - decl.name = name; // TODO: Clone - if (flag) { - decl.modifiers = createNodeArray(createModifiersFromModifierFlags(flag)); - } - if (p.flags & SymbolFlags.Optional) { - decl.questionToken = createToken(SyntaxKind.QuestionToken); - } - results.push(setTextRange(decl, sig.declaration)); - } - return results as unknown as T[]; - } - // The `Constructor`'s symbol isn't in the class's properties lists, obviously, since it's a signature on the static - return Debug.fail(`Unhandled class member kind! ${(p as any).__debugFlags || p.flags}`); - }; - } - - function serializePropertySymbolForInterface(p: Symbol, baseType: Type | undefined) { - return serializePropertySymbolForInterfaceWorker(p, /*isStatic*/ false, baseType); - } - - function getDeclarationWithTypeAnnotation(symbol: Symbol) { - return find(symbol.declarations, s => !!getEffectiveTypeAnnotationNode(s) && !!findAncestor(s, n => n === enclosingDeclaration)); - } - - /** - * Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag - * so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym` - */ - function serializeTypeForDeclaration(type: Type, symbol: Symbol) { - const declWithExistingAnnotation = getDeclarationWithTypeAnnotation(symbol); - if (declWithExistingAnnotation && !isFunctionLikeDeclaration(declWithExistingAnnotation)) { - // try to reuse the existing annotation - const existing = getEffectiveTypeAnnotationNode(declWithExistingAnnotation)!; - const transformed = visitNode(existing, visitExistingNodeTreeSymbols); - return transformed === existing ? getMutableClone(existing) : transformed; - } - const oldFlags = context.flags; - if (type.flags & TypeFlags.UniqueESSymbol && - type.symbol === symbol) { - context.flags |= NodeBuilderFlags.AllowUniqueESSymbolType; - } - const result = typeToTypeNodeHelper(type, context); - context.flags = oldFlags; - return result; - - function visitExistingNodeTreeSymbols(node: T): Node { - if (isJSDocAllType(node)) { - return createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - if (isJSDocUnknownType(node)) { - return createKeywordTypeNode(SyntaxKind.UnknownKeyword); - } - if (isJSDocNullableType(node)) { - return createUnionTypeNode([visitNode(node.type, visitExistingNodeTreeSymbols), createKeywordTypeNode(SyntaxKind.NullKeyword)]); - } - if (isJSDocOptionalType(node)) { - return createUnionTypeNode([visitNode(node.type, visitExistingNodeTreeSymbols), createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); - } - if (isJSDocNonNullableType(node)) { - return visitNode(node.type, visitExistingNodeTreeSymbols); - } - if ((isExpressionWithTypeArguments(node) || isTypeReferenceNode(node)) && isJSDocIndexSignature(node)) { - return createTypeLiteralNode([createIndexSignature( - /*decorators*/ undefined, - /*modifiers*/ undefined, - [createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotdotdotToken*/ undefined, - "x", - /*questionToken*/ undefined, - visitNode(node.typeArguments![0], visitExistingNodeTreeSymbols) - )], - visitNode(node.typeArguments![1], visitExistingNodeTreeSymbols) - )]); - } - if (isJSDocFunctionType(node)) { - if (isJSDocConstructSignature(node)) { - let newTypeNode: TypeNode | undefined; - return createConstructorTypeNode( - visitNodes(node.typeParameters, visitExistingNodeTreeSymbols), - mapDefined(node.parameters, (p, i) => p.name && isIdentifier(p.name) && p.name.escapedText === "new" ? (newTypeNode = p.type, undefined) : createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - p.dotDotDotToken, - p.name || p.dotDotDotToken ? `args` : `arg${i}`, - p.questionToken, - visitNode(p.type, visitExistingNodeTreeSymbols), - /*initializer*/ undefined - )), - visitNode(newTypeNode || node.type, visitExistingNodeTreeSymbols) - ); - } - else { - return createFunctionTypeNode( - visitNodes(node.typeParameters, visitExistingNodeTreeSymbols), - map(node.parameters, (p, i) => createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - p.dotDotDotToken, - p.name || p.dotDotDotToken ? `args` : `arg${i}`, - p.questionToken, - visitNode(p.type, visitExistingNodeTreeSymbols), - /*initializer*/ undefined - )), - visitNode(node.type, visitExistingNodeTreeSymbols) - ); - } - } - if (isLiteralImportTypeNode(node)) { - return updateImportTypeNode( - node, - updateLiteralTypeNode(node.argument, rewriteModuleSpecifier(node, node.argument.literal)), - node.qualifier, - visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode), - node.isTypeOf - ); - } - - if (isEntityName(node) || isEntityNameExpression(node)) { - const leftmost = getFirstIdentifier(node); - const sym = resolveEntityName(leftmost, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveALias*/ true); - if (sym) { - includePrivateSymbol(sym); - if (isIdentifier(node) && sym.flags & SymbolFlags.TypeParameter) { - const name = typeParameterToName(getDeclaredTypeOfSymbol(sym), context); - if (idText(name) !== idText(node)) { - return name; - } - return node; - } - } - } - - return visitEachChild(node, visitExistingNodeTreeSymbols, nullTransformationContext); - } - - function rewriteModuleSpecifier(parent: ImportTypeNode, lit: StringLiteral) { - if (bundled) { - if (context.tracker && context.tracker.moduleResolverHost) { - const targetFile = getExternalModuleFileFromDeclaration(parent); - if (targetFile) { - const getCanonicalFileName = createGetCanonicalFileName(!!host.useCaseSensitiveFileNames); - const resolverHost = { - getCanonicalFileName, - getCurrentDirectory: context.tracker.moduleResolverHost.getCurrentDirectory ? () => context.tracker.moduleResolverHost!.getCurrentDirectory!() : () => "", - getCommonSourceDirectory: () => context.tracker.moduleResolverHost!.getCommonSourceDirectory() - }; - const newName = getResolvedExternalModuleName(resolverHost, targetFile); - return createLiteral(newName); - } - } - } - else { - if (context.tracker && context.tracker.trackExternalModuleSymbolOfImportTypeNode) { - const moduleSym = resolveExternalModuleNameWorker(lit, lit, /*moduleNotFoundError*/ undefined); - if (moduleSym) { - context.tracker.trackExternalModuleSymbolOfImportTypeNode(moduleSym); - } - } - } - return lit; - } - } - - function serializeSignatures(kind: SignatureKind, input: Type, baseType: Type | undefined, outputKind: SyntaxKind) { - const signatures = getSignaturesOfType(input, kind); - if (kind === SignatureKind.Construct) { - if (!baseType && every(signatures, s => length(s.parameters) === 0)) { - return []; // No base type, every constructor is empty - elide the extraneous `constructor()` - } - if (baseType) { - // If there is a base type, if every signature in the class is identical to a signature in the baseType, elide all the declarations - const baseSigs = getSignaturesOfType(baseType, SignatureKind.Construct); - if (!length(baseSigs) && every(signatures, s => length(s.parameters) === 0)) { - return []; // Base had no explicit signatures, if all our signatures are also implicit, return an empty list - } - if (baseSigs.length === signatures.length) { - let failed = false; - for (let i = 0; i < baseSigs.length; i++) { - if (!compareSignaturesIdentical(signatures[i], baseSigs[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true, compareTypesIdentical)) { - failed = true; - break; - } - } - if (!failed) { - return []; // Every signature was identical - elide constructor list as it is inherited - } - } - } - let privateProtected: ModifierFlags = 0; - for (const s of signatures) { - if (s.declaration) { - privateProtected |= getSelectedModifierFlags(s.declaration, ModifierFlags.Private | ModifierFlags.Protected); - } - } - if (privateProtected) { - return [setTextRange(createConstructor( - /*decorators*/ undefined, - createModifiersFromModifierFlags(privateProtected), - /*parameters*/ [], - /*body*/ undefined, - ), signatures[0].declaration)]; - } - } - - const results = []; - for (const sig of signatures) { - // Each overload becomes a separate constructor declaration, in order - const decl = signatureToSignatureDeclarationHelper(sig, outputKind, context); - results.push(setTextRange(decl, sig.declaration)); - } - return results; - } - - function serializeIndexSignatures(input: Type, baseType: Type | undefined) { - const results: IndexSignatureDeclaration[] = []; - for (const type of [IndexKind.String, IndexKind.Number]) { - const info = getIndexInfoOfType(input, type); - if (info) { - if (baseType) { - const baseInfo = getIndexInfoOfType(baseType, type); - if (baseInfo) { - if (isTypeIdenticalTo(info.type, baseInfo.type)) { - continue; // elide identical index signatures - } - } - } - results.push(indexInfoToIndexSignatureDeclarationHelper(info, type, context)); - } - } - return results; - } - - function serializeBaseType(t: Type, staticType: Type, rootName: string) { - const ref = trySerializeAsTypeReference(t); - if (ref) { - return ref; - } - const tempName = getUnusedName(`${rootName}_base`); - const statement = createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([ - createVariableDeclaration(tempName, typeToTypeNodeHelper(staticType, context)) - ], NodeFlags.Const)); - addResult(statement, ModifierFlags.None); - return createExpressionWithTypeArguments(/*typeArgs*/ undefined, createIdentifier(tempName)); - } - - function trySerializeAsTypeReference(t: Type) { - let typeArgs: TypeNode[] | undefined; - let reference: Expression | undefined; - // We don't use `isValueSymbolAccessible` below. since that considers alternative containers (like modules) - // which we can't write out in a syntactically valid way as an expression - if ((t as TypeReference).target && getAccessibleSymbolChain((t as TypeReference).target.symbol, enclosingDeclaration, SymbolFlags.Value, /*useOnlyExternalAliasing*/ false)) { - typeArgs = map(getTypeArguments(t as TypeReference), t => typeToTypeNodeHelper(t, context)); - reference = symbolToExpression((t as TypeReference).target.symbol, context, SymbolFlags.Type); - } - else if (t.symbol && getAccessibleSymbolChain(t.symbol, enclosingDeclaration, SymbolFlags.Value, /*useOnlyExternalAliasing*/ false)) { - reference = symbolToExpression(t.symbol, context, SymbolFlags.Type); - } - if (reference) { - return createExpressionWithTypeArguments(typeArgs, reference); - } - } - - function getUnusedName(input: string, symbol?: Symbol): string { - if (symbol) { - if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) { - return context.remappedSymbolNames!.get("" + getSymbolId(symbol))!; - } - } - if (symbol) { - input = getNameCandidateWorker(symbol, input); - } - let i = 0; - const original = input; - while (context.usedSymbolNames!.has(input)) { - i++; - input = `${original}_${i}`; - } - context.usedSymbolNames!.set(input, true); - if (symbol) { - context.remappedSymbolNames!.set("" + getSymbolId(symbol), input); - } - return input; - } - - function getNameCandidateWorker(symbol: Symbol, localName: string) { - if (localName === InternalSymbolName.Default || localName === InternalSymbolName.Class || localName === InternalSymbolName.Function) { - const flags = context.flags; - context.flags |= NodeBuilderFlags.InInitialEntityName; - const nameCandidate = getNameOfSymbolAsWritten(symbol, context); - context.flags = flags; - localName = nameCandidate.length > 0 && isSingleOrDoubleQuote(nameCandidate.charCodeAt(0)) ? stripQuotes(nameCandidate) : nameCandidate; - } - if (localName === InternalSymbolName.Default) { - localName = "_default"; - } - else if (localName === InternalSymbolName.ExportEquals) { - localName = "_exports"; - } - localName = isIdentifierText(localName, languageVersion) && !isStringANonContextualKeyword(localName) ? localName : "_" + localName.replace(/[^a-zA-Z0-9]/g, "_"); - return localName; - } - - function getInternalSymbolName(symbol: Symbol, localName: string) { - if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) { - return context.remappedSymbolNames!.get("" + getSymbolId(symbol))!; - } - localName = getNameCandidateWorker(symbol, localName); - // The result of this is going to be used as the symbol's name - lock it in, so `getUnusedName` will also pick it up - context.remappedSymbolNames!.set("" + getSymbolId(symbol), localName); - return localName; - } - } - } - - function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, writer?: EmitTextWriter): string { - return writer ? typePredicateToStringWorker(writer).getText() : usingSingleLineStringWriter(typePredicateToStringWorker); - - function typePredicateToStringWorker(writer: EmitTextWriter) { - const predicate = createTypePredicateNodeWithModifier( - typePredicate.kind === TypePredicateKind.AssertsThis || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? createToken(SyntaxKind.AssertsKeyword) : undefined, - typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? createIdentifier(typePredicate.parameterName) : createThisTypeNode(), - typePredicate.type && nodeBuilder.typeToTypeNode(typePredicate.type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName)! // TODO: GH#18217 - ); - const printer = createPrinter({ removeComments: true }); - const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer); - return writer; - } - } - - function formatUnionTypes(types: readonly Type[]): Type[] { - const result: Type[] = []; - let flags: TypeFlags = 0; - for (let i = 0; i < types.length; i++) { - const t = types[i]; - flags |= t.flags; - if (!(t.flags & TypeFlags.Nullable)) { - if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) { - const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : getBaseTypeOfEnumLiteralType(t); - if (baseType.flags & TypeFlags.Union) { - const count = (baseType).types.length; - if (i + count <= types.length && getRegularTypeOfLiteralType(types[i + count - 1]) === getRegularTypeOfLiteralType((baseType).types[count - 1])) { - result.push(baseType); - i += count - 1; - continue; - } - } - } - result.push(t); - } - } - if (flags & TypeFlags.Null) result.push(nullType); - if (flags & TypeFlags.Undefined) result.push(undefinedType); - return result || types; - } - - function visibilityToString(flags: ModifierFlags): string | undefined { - if (flags === ModifierFlags.Private) { - return "private"; - } - if (flags === ModifierFlags.Protected) { - return "protected"; - } - return "public"; - } - - function getTypeAliasForTypeLiteral(type: Type): Symbol | undefined { - if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) { - const node = findAncestor(type.symbol.declarations[0].parent, n => n.kind !== SyntaxKind.ParenthesizedType)!; - if (node.kind === SyntaxKind.TypeAliasDeclaration) { - return getSymbolOfNode(node); - } - } - return undefined; - } - - function isTopLevelInExternalModuleAugmentation(node: Node): boolean { - return node && node.parent && - node.parent.kind === SyntaxKind.ModuleBlock && - isExternalModuleAugmentation(node.parent.parent); - } - - interface NodeBuilderContext { - enclosingDeclaration: Node | undefined; - flags: NodeBuilderFlags; - tracker: SymbolTracker; - - // State - encounteredError: boolean; - visitedTypes: Map | undefined; - symbolDepth: Map | undefined; - inferTypeParameters: TypeParameter[] | undefined; - approximateLength: number; - truncating?: boolean; - typeParameterSymbolList?: Map; - typeParameterNames?: Map; - typeParameterNamesByText?: Map; - usedSymbolNames?: Map; - remappedSymbolNames?: Map; - } - - function isDefaultBindingContext(location: Node) { - return location.kind === SyntaxKind.SourceFile || isAmbientModule(location); - } - - function getNameOfSymbolFromNameType(symbol: Symbol, context?: NodeBuilderContext) { - const nameType = symbol.nameType; - if (nameType) { - if (nameType.flags & TypeFlags.StringOrNumberLiteral) { - const name = "" + (nameType).value; - if (!isIdentifierText(name, compilerOptions.target) && !isNumericLiteralName(name)) { - return `"${escapeString(name, CharacterCodes.doubleQuote)}"`; - } - if (isNumericLiteralName(name) && startsWith(name, "-")) { - return `[${name}]`; - } - return name; - } - if (nameType.flags & TypeFlags.UniqueESSymbol) { - return `[${getNameOfSymbolAsWritten((nameType).symbol, context)}]`; - } - } - } - - /** - * Gets a human-readable name for a symbol. - * Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. - * - * Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. - * It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. - */ - function getNameOfSymbolAsWritten(symbol: Symbol, context?: NodeBuilderContext): string { - if (context && symbol.escapedName === InternalSymbolName.Default && !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope) && - // If it's not the first part of an entity name, it must print as `default` - (!(context.flags & NodeBuilderFlags.InInitialEntityName) || - // if the symbol is synthesized, it will only be referenced externally it must print as `default` - !symbol.declarations || - // if not in the same binding context (source file, module declaration), it must print as `default` - (context.enclosingDeclaration && findAncestor(symbol.declarations[0], isDefaultBindingContext) !== findAncestor(context.enclosingDeclaration, isDefaultBindingContext)))) { - return "default"; - } - if (symbol.declarations && symbol.declarations.length) { - let declaration = firstDefined(symbol.declarations, d => getNameOfDeclaration(d) ? d : undefined); // Try using a declaration with a name, first - const name = declaration && getNameOfDeclaration(declaration); - if (declaration && name) { - if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) { - return symbolName(symbol); - } - if (isComputedPropertyName(name) && !(getCheckFlags(symbol) & CheckFlags.Late) && symbol.nameType && symbol.nameType.flags & TypeFlags.StringOrNumberLiteral) { - // Computed property name isn't late bound, but has a well-known name type - use name type to generate a symbol name - const result = getNameOfSymbolFromNameType(symbol, context); - if (result !== undefined) { - return result; - } - } - return declarationNameToString(name); - } - if (!declaration) { - declaration = symbol.declarations[0]; // Declaration may be nameless, but we'll try anyway - } - if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { - return declarationNameToString((declaration.parent).name); - } - switch (declaration.kind) { - case SyntaxKind.ClassExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - if (context && !context.encounteredError && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) { - context.encounteredError = true; - } - return declaration.kind === SyntaxKind.ClassExpression ? "(Anonymous class)" : "(Anonymous function)"; - } - } - const name = getNameOfSymbolFromNameType(symbol, context); - return name !== undefined ? name : symbolName(symbol); - } - - function isDeclarationVisible(node: Node): boolean { - if (node) { - const links = getNodeLinks(node); - if (links.isVisible === undefined) { - links.isVisible = !!determineIfDeclarationIsVisible(); - } - return links.isVisible; - } - - return false; - - function determineIfDeclarationIsVisible() { - switch (node.kind) { - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocEnumTag: - // Top-level jsdoc type aliases are considered exported - // First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file - return !!(node.parent && node.parent.parent && node.parent.parent.parent && isSourceFile(node.parent.parent.parent)); - case SyntaxKind.BindingElement: - return isDeclarationVisible(node.parent.parent); - case SyntaxKind.VariableDeclaration: - if (isBindingPattern((node as VariableDeclaration).name) && - !((node as VariableDeclaration).name as BindingPattern).elements.length) { - // If the binding pattern is empty, this variable declaration is not visible - return false; - } - // falls through - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - // external module augmentation is always visible - if (isExternalModuleAugmentation(node)) { - return true; - } - const parent = getDeclarationContainer(node); - // If the node is not exported or it is not ambient module element (except import declaration) - if (!(getCombinedModifierFlags(node as Declaration) & ModifierFlags.Export) && - !(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && parent.flags & NodeFlags.Ambient)) { - return isGlobalSourceFile(parent); - } - // Exported members/ambient module elements (exception import declaration) are visible if parent is visible - return isDeclarationVisible(parent); - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - if (hasModifier(node, ModifierFlags.Private | ModifierFlags.Protected)) { - // Private/protected properties/methods are not visible - return false; - } - // Public properties/methods are visible if its parents are visible, so: - // falls through - - case SyntaxKind.Constructor: - case SyntaxKind.ConstructSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.Parameter: - case SyntaxKind.ModuleBlock: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.TypeReference: - case SyntaxKind.ArrayType: - case SyntaxKind.TupleType: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - case SyntaxKind.ParenthesizedType: - return isDeclarationVisible(node.parent); - - // Default binding, import specifier and namespace import is visible - // only on demand so by default it is not visible - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportSpecifier: - return false; - - // Type parameters are always visible - case SyntaxKind.TypeParameter: - - // Source file and namespace export are always visible - // falls through - case SyntaxKind.SourceFile: - case SyntaxKind.NamespaceExportDeclaration: - return true; - - // Export assignments do not create name bindings outside the module - case SyntaxKind.ExportAssignment: - return false; - - default: - return false; - } - } - } - - function collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined { - let exportSymbol: Symbol | undefined; - if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) { - exportSymbol = resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, node, /*isUse*/ false); - } - else if (node.parent.kind === SyntaxKind.ExportSpecifier) { - exportSymbol = getTargetOfExportSpecifier(node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); - } - let result: Node[] | undefined; - let visited: Map | undefined; - if (exportSymbol) { - visited = createMap(); - visited.set("" + getSymbolId(exportSymbol), true); - buildVisibleNodeList(exportSymbol.declarations); - } - return result; - - function buildVisibleNodeList(declarations: Declaration[]) { - forEach(declarations, declaration => { - const resultNode = getAnyImportSyntax(declaration) || declaration; - if (setVisibility) { - getNodeLinks(declaration).isVisible = true; - } - else { - result = result || []; - pushIfUnique(result, resultNode); - } - - if (isInternalModuleImportEqualsDeclaration(declaration)) { - // Add the referenced top container visible - const internalModuleReference = declaration.moduleReference; - const firstIdentifier = getFirstIdentifier(internalModuleReference); - const importSymbol = resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, - undefined, undefined, /*isUse*/ false); - const id = importSymbol && "" + getSymbolId(importSymbol); - if (importSymbol && !visited!.has(id!)) { - visited!.set(id!, true); - buildVisibleNodeList(importSymbol.declarations); - } - } - }); - } - } - - /** - * Push an entry on the type resolution stack. If an entry with the given target and the given property name - * is already on the stack, and no entries in between already have a type, then a circularity has occurred. - * In this case, the result values of the existing entry and all entries pushed after it are changed to false, - * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. - * In order to see if the same query has already been done before, the target object and the propertyName both - * must match the one passed in. - * - * @param target The symbol, type, or signature whose type is being queried - * @param propertyName The property name that should be used to query the target for its type - */ - function pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { - const resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName); - if (resolutionCycleStartIndex >= 0) { - // A cycle was found - const { length } = resolutionTargets; - for (let i = resolutionCycleStartIndex; i < length; i++) { - resolutionResults[i] = false; - } - return false; - } - resolutionTargets.push(target); - resolutionResults.push(/*items*/ true); - resolutionPropertyNames.push(propertyName); - return true; - } - - function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number { - for (let i = resolutionTargets.length - 1; i >= 0; i--) { - if (hasType(resolutionTargets[i], resolutionPropertyNames[i])) { - return -1; - } - if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) { - return i; - } - } - return -1; - } - - function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { - switch (propertyName) { - case TypeSystemPropertyName.Type: - return !!getSymbolLinks(target).type; - case TypeSystemPropertyName.EnumTagType: - return !!(getNodeLinks(target as JSDocEnumTag).resolvedEnumType); - case TypeSystemPropertyName.DeclaredType: - return !!getSymbolLinks(target).declaredType; - case TypeSystemPropertyName.ResolvedBaseConstructorType: - return !!(target).resolvedBaseConstructorType; - case TypeSystemPropertyName.ResolvedReturnType: - return !!(target).resolvedReturnType; - case TypeSystemPropertyName.ImmediateBaseConstraint: - return !!(target).immediateBaseConstraint; - case TypeSystemPropertyName.JSDocTypeReference: - return !!getSymbolLinks(target as Symbol).resolvedJSDocType; - case TypeSystemPropertyName.ResolvedTypeArguments: - return !!(target as TypeReference).resolvedTypeArguments; - } - return Debug.assertNever(propertyName); - } - - /** - * Pop an entry from the type resolution stack and return its associated result value. The result value will - * be true if no circularities were detected, or false if a circularity was found. - */ - function popTypeResolution(): boolean { - resolutionTargets.pop(); - resolutionPropertyNames.pop(); - return resolutionResults.pop()!; - } - - function getDeclarationContainer(node: Node): Node { - return findAncestor(getRootDeclaration(node), node => { - switch (node.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.VariableDeclarationList: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.NamedImports: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportClause: - return false; - default: - return true; - } - })!.parent; - } - - function getTypeOfPrototypeProperty(prototype: Symbol): Type { - // TypeScript 1.0 spec (April 2014): 8.4 - // Every class automatically contains a static property member named 'prototype', - // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. - // It is an error to explicitly declare a static property member with the name 'prototype'. - const classType = getDeclaredTypeOfSymbol(getParentOfSymbol(prototype)!); - return classType.typeParameters ? createTypeReference(classType, map(classType.typeParameters, _ => anyType)) : classType; - } - - // Return the type of the given property in the given type, or undefined if no such property exists - function getTypeOfPropertyOfType(type: Type, name: __String): Type | undefined { - const prop = getPropertyOfType(type, name); - return prop ? getTypeOfSymbol(prop) : undefined; - } - - function getTypeOfPropertyOrIndexSignature(type: Type, name: __String): Type { - return getTypeOfPropertyOfType(type, name) || isNumericLiteralName(name) && getIndexTypeOfType(type, IndexKind.Number) || getIndexTypeOfType(type, IndexKind.String) || unknownType; - } - - function isTypeAny(type: Type | undefined) { - return type && (type.flags & TypeFlags.Any) !== 0; - } - - // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been - // assigned by contextual typing. - function getTypeForBindingElementParent(node: BindingElementGrandparent) { - const symbol = getSymbolOfNode(node); - return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false); - } - - function isComputedNonLiteralName(name: PropertyName): boolean { - return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteralLike(name.expression); - } - - function getRestType(source: Type, properties: PropertyName[], symbol: Symbol | undefined): Type { - source = filterType(source, t => !(t.flags & TypeFlags.Nullable)); - if (source.flags & TypeFlags.Never) { - return emptyObjectType; - } - if (source.flags & TypeFlags.Union) { - return mapType(source, t => getRestType(t, properties, symbol)); - } - const omitKeyType = getUnionType(map(properties, getLiteralTypeFromPropertyName)); - if (isGenericObjectType(source) || isGenericIndexType(omitKeyType)) { - if (omitKeyType.flags & TypeFlags.Never) { - return source; - } - - const omitTypeAlias = getGlobalOmitSymbol(); - if (!omitTypeAlias) { - return errorType; - } - return getTypeAliasInstantiation(omitTypeAlias, [source, omitKeyType]); - } - const members = createSymbolTable(); - for (const prop of getPropertiesOfType(source)) { - if (!isTypeAssignableTo(getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique), omitKeyType) - && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected)) - && isSpreadableProperty(prop)) { - members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false)); - } - } - const stringIndexInfo = getIndexInfoOfType(source, IndexKind.String); - const numberIndexInfo = getIndexInfoOfType(source, IndexKind.Number); - const result = createAnonymousType(symbol, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); - result.objectFlags |= ObjectFlags.ObjectRestType; - return result; - } - - // Determine the control flow type associated with a destructuring declaration or assignment. The following - // forms of destructuring are possible: - // let { x } = obj; // BindingElement - // let [ x ] = obj; // BindingElement - // { x } = obj; // ShorthandPropertyAssignment - // { x: v } = obj; // PropertyAssignment - // [ x ] = obj; // Expression - // We construct a synthetic element access expression corresponding to 'obj.x' such that the control - // flow analyzer doesn't have to handle all the different syntactic forms. - function getFlowTypeOfDestructuring(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression, declaredType: Type) { - const reference = getSyntheticElementAccess(node); - return reference ? getFlowTypeOfReference(reference, declaredType) : declaredType; - } - - function getSyntheticElementAccess(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression): ElementAccessExpression | undefined { - const parentAccess = getParentElementAccess(node); - if (parentAccess && parentAccess.flowNode) { - const propName = getDestructuringPropertyName(node); - if (propName) { - const result = createNode(SyntaxKind.ElementAccessExpression, node.pos, node.end); - result.parent = node; - result.expression = parentAccess; - const literal = createNode(SyntaxKind.StringLiteral, node.pos, node.end); - literal.parent = result; - literal.text = propName; - result.argumentExpression = literal; - result.flowNode = parentAccess.flowNode; - return result; - } - } - } - - function getParentElementAccess(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression) { - const ancestor = node.parent.parent; - switch (ancestor.kind) { - case SyntaxKind.BindingElement: - case SyntaxKind.PropertyAssignment: - return getSyntheticElementAccess(ancestor); - case SyntaxKind.ArrayLiteralExpression: - return getSyntheticElementAccess(node.parent); - case SyntaxKind.VariableDeclaration: - return (ancestor).initializer; - case SyntaxKind.BinaryExpression: - return (ancestor).right; - } - } - - function getDestructuringPropertyName(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression) { - const parent = node.parent; - if (node.kind === SyntaxKind.BindingElement && parent.kind === SyntaxKind.ObjectBindingPattern) { - return getLiteralPropertyNameText((node).propertyName || (node).name); - } - if (node.kind === SyntaxKind.PropertyAssignment || node.kind === SyntaxKind.ShorthandPropertyAssignment) { - return getLiteralPropertyNameText((node).name); - } - return "" + (>(parent).elements).indexOf(node); - } - - function getLiteralPropertyNameText(name: PropertyName) { - const type = getLiteralTypeFromPropertyName(name); - return type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral) ? "" + (type).value : undefined; - } - - /** Return the inferred type for a binding element */ - function getTypeForBindingElement(declaration: BindingElement): Type | undefined { - const pattern = declaration.parent; - let parentType = getTypeForBindingElementParent(pattern.parent); - // If no type or an any type was inferred for parent, infer that for the binding element - if (!parentType || isTypeAny(parentType)) { - return parentType; - } - // Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation - if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isParameterDeclaration(declaration)) { - parentType = getNonNullableType(parentType); - } - - let type: Type | undefined; - if (pattern.kind === SyntaxKind.ObjectBindingPattern) { - if (declaration.dotDotDotToken) { - if (parentType.flags & TypeFlags.Unknown || !isValidSpreadType(parentType)) { - error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types); - return errorType; - } - const literalMembers: PropertyName[] = []; - for (const element of pattern.elements) { - if (!element.dotDotDotToken) { - literalMembers.push(element.propertyName || element.name as Identifier); - } - } - type = getRestType(parentType, literalMembers, declaration.symbol); - } - else { - // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) - const name = declaration.propertyName || declaration.name; - const indexType = getLiteralTypeFromPropertyName(name); - const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, name), declaration.name); - type = getFlowTypeOfDestructuring(declaration, declaredType); - } - } - else { - // This elementType will be used if the specific property corresponding to this index is not - // present (aka the tuple element property). This call also checks that the parentType is in - // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, parentType, undefinedType, pattern); - const index = pattern.elements.indexOf(declaration); - if (declaration.dotDotDotToken) { - // If the parent is a tuple type, the rest element has a tuple type of the - // remaining tuple element types. Otherwise, the rest element has an array type with same - // element type as the parent type. - type = everyType(parentType, isTupleType) ? - mapType(parentType, t => sliceTupleType(t, index)) : - createArrayType(elementType); - } - else if (isArrayLikeType(parentType)) { - const indexType = getLiteralType(index); - const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0; - const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags) || errorType, declaration.name); - type = getFlowTypeOfDestructuring(declaration, declaredType); - } - else { - type = elementType; - } - } - if (!declaration.initializer) { - return type; - } - if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined) ? - getTypeWithFacts(type, TypeFacts.NEUndefined) : - type; - } - return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), checkDeclarationInitializer(declaration)], UnionReduction.Subtype); - } - - function getTypeForDeclarationFromJSDocComment(declaration: Node) { - const jsdocType = getJSDocType(declaration); - if (jsdocType) { - return getTypeFromTypeNode(jsdocType); - } - return undefined; - } - - function isNullOrUndefined(node: Expression) { - const expr = skipParentheses(node); - return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(expr) === undefinedSymbol; - } - - function isEmptyArrayLiteral(node: Expression) { - const expr = skipParentheses(node); - return expr.kind === SyntaxKind.ArrayLiteralExpression && (expr).elements.length === 0; - } - - function addOptionality(type: Type, optional = true): Type { - return strictNullChecks && optional ? getOptionalType(type) : type; - } - - function isParameterOfContextuallyTypedFunction(node: Declaration) { - return node.kind === SyntaxKind.Parameter && - (node.parent.kind === SyntaxKind.FunctionExpression || node.parent.kind === SyntaxKind.ArrowFunction) && - !!getContextualType(node.parent); - } - - // Return the inferred type for a variable, parameter, or property declaration - function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, includeOptionality: boolean): Type | undefined { - // A variable declared in a for..in statement is of type string, or of type keyof T when the - // right hand expression is of a type parameter type. - if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) { - const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression))); - return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType; - } - - if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) { - // checkRightHandSideOfForOf will return undefined if the for-of expression type was - // missing properties/signatures required to get its iteratedType (like - // [Symbol.iterator] or next). This may be because we accessed properties from anyType, - // or it may have led to an error inside getElementTypeOfIterable. - const forOfStatement = declaration.parent.parent; - return checkRightHandSideOfForOf(forOfStatement.expression, forOfStatement.awaitModifier) || anyType; - } - - if (isBindingPattern(declaration.parent)) { - return getTypeForBindingElement(declaration); - } - - const isOptional = includeOptionality && ( - isParameter(declaration) && isJSDocOptionalParameter(declaration) - || !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken); - - // Use type from type annotation if one is present - const declaredType = tryGetTypeFromEffectiveTypeNode(declaration); - if (declaredType) { - return addOptionality(declaredType, isOptional); - } - - if ((noImplicitAny || isInJSFile(declaration)) && - declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) && - !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !(declaration.flags & NodeFlags.Ambient)) { - // If --noImplicitAny is on or the declaration is in a Javascript file, - // use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no - // initializer or a 'null' or 'undefined' initializer. - if (!(getCombinedNodeFlags(declaration) & NodeFlags.Const) && (!declaration.initializer || isNullOrUndefined(declaration.initializer))) { - return autoType; - } - // Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array - // literal initializer. - if (declaration.initializer && isEmptyArrayLiteral(declaration.initializer)) { - return autoArrayType; - } - } - - if (declaration.kind === SyntaxKind.Parameter) { - const func = declaration.parent; - // For a parameter of a set accessor, use the type of the get accessor if one is present - if (func.kind === SyntaxKind.SetAccessor && !hasNonBindableDynamicName(func)) { - const getter = getDeclarationOfKind(getSymbolOfNode(declaration.parent), SyntaxKind.GetAccessor); - if (getter) { - const getterSignature = getSignatureFromDeclaration(getter); - const thisParameter = getAccessorThisParameter(func as AccessorDeclaration); - if (thisParameter && declaration === thisParameter) { - // Use the type from the *getter* - Debug.assert(!thisParameter.type); - return getTypeOfSymbol(getterSignature.thisParameter!); - } - return getReturnTypeOfSignature(getterSignature); - } - } - if (isInJSFile(declaration)) { - const typeTag = getJSDocType(func); - if (typeTag && isFunctionTypeNode(typeTag)) { - return getTypeAtPosition(getSignatureFromDeclaration(typeTag), func.parameters.indexOf(declaration)); - } - } - // Use contextual parameter type if one is available - const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration, /*forCache*/ true); - if (type) { - return addOptionality(type, isOptional); - } - } - else if (isInJSFile(declaration)) { - const containerObjectType = getJSContainerObjectType(declaration, getSymbolOfNode(declaration), getDeclaredExpandoInitializer(declaration)); - if (containerObjectType) { - return containerObjectType; - } - } - - // Use the type of the initializer expression if one is present and the declaration is - // not a parameter of a contextually typed function - if (declaration.initializer && !isParameterOfContextuallyTypedFunction(declaration)) { - const type = checkDeclarationInitializer(declaration); - return addOptionality(type, isOptional); - } - - if (isJsxAttribute(declaration)) { - // if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true. - // I.e is sugar for - return trueType; - } - - // If the declaration specifies a binding pattern and is not a parameter of a contextually - // typed function, use the type implied by the binding pattern - if (isBindingPattern(declaration.name) && !isParameterOfContextuallyTypedFunction(declaration)) { - return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true); - } - - // No type specified and nothing can be inferred - return undefined; - } - - function getWidenedTypeForAssignmentDeclaration(symbol: Symbol, resolvedSymbol?: Symbol) { - // function/class/{} initializers are themselves containers, so they won't merge in the same way as other initializers - const container = getAssignedExpandoInitializer(symbol.valueDeclaration); - if (container) { - const tag = getJSDocTypeTag(container); - if (tag && tag.typeExpression) { - return getTypeFromTypeNode(tag.typeExpression); - } - const containerObjectType = getJSContainerObjectType(symbol.valueDeclaration, symbol, container); - return containerObjectType || getWidenedLiteralType(checkExpressionCached(container)); - } - let definedInConstructor = false; - let definedInMethod = false; - let jsdocType: Type | undefined; - let types: Type[] | undefined; - for (const declaration of symbol.declarations) { - const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : - isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : - undefined; - if (!expression) { - continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere - } - - const kind = isAccessExpression(expression) - ? getAssignmentDeclarationPropertyAccessKind(expression) - : getAssignmentDeclarationKind(expression); - if (kind === AssignmentDeclarationKind.ThisProperty) { - if (isDeclarationInConstructor(expression)) { - definedInConstructor = true; - } - else { - definedInMethod = true; - } - } - if (!isCallExpression(expression)) { - jsdocType = getAnnotatedTypeForAssignmentDeclaration(jsdocType, expression, symbol, declaration); - } - if (!jsdocType) { - (types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); - } - } - let type = jsdocType; - if (!type) { - if (!length(types)) { - return errorType; // No types from any declarations :( - } - let constructorTypes = definedInConstructor ? getConstructorDefinedThisAssignmentTypes(types!, symbol.declarations) : undefined; - // use only the constructor types unless they were only assigned null | undefined (including widening variants) - if (definedInMethod) { - const propType = getTypeOfAssignmentDeclarationPropertyOfBaseType(symbol); - if (propType) { - (constructorTypes || (constructorTypes = [])).push(propType); - definedInConstructor = true; - } - } - const sourceTypes = some(constructorTypes, t => !!(t.flags & ~TypeFlags.Nullable)) ? constructorTypes : types; // TODO: GH#18217 - type = getUnionType(sourceTypes!, UnionReduction.Subtype); - } - const widened = getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor)); - if (filterType(widened, t => !!(t.flags & ~TypeFlags.Nullable)) === neverType) { - reportImplicitAny(symbol.valueDeclaration, anyType); - return anyType; - } - return widened; - } - - function getJSContainerObjectType(decl: Node, symbol: Symbol, init: Expression | undefined): Type | undefined { - if (!isInJSFile(decl) || !init || !isObjectLiteralExpression(init) || init.properties.length) { - return undefined; - } - const exports = createSymbolTable(); - while (isBinaryExpression(decl) || isPropertyAccessExpression(decl)) { - const s = getSymbolOfNode(decl); - if (s && hasEntries(s.exports)) { - mergeSymbolTable(exports, s.exports); - } - decl = isBinaryExpression(decl) ? decl.parent : decl.parent.parent; - } - const s = getSymbolOfNode(decl); - if (s && hasEntries(s.exports)) { - mergeSymbolTable(exports, s.exports); - } - const type = createAnonymousType(symbol, exports, emptyArray, emptyArray, undefined, undefined); - type.objectFlags |= ObjectFlags.JSLiteral; - return type; - } - - function getAnnotatedTypeForAssignmentDeclaration(declaredType: Type | undefined, expression: Expression, symbol: Symbol, declaration: Declaration) { - const typeNode = getEffectiveTypeAnnotationNode(expression.parent); - if (typeNode) { - const type = getWidenedType(getTypeFromTypeNode(typeNode)); - if (!declaredType) { - return type; - } - else if (declaredType !== errorType && type !== errorType && !isTypeIdenticalTo(declaredType, type)) { - errorNextVariableOrPropertyDeclarationMustHaveSameType(/*firstDeclaration*/ undefined, declaredType, declaration, type); - } - } - if (symbol.parent) { - const typeNode = getEffectiveTypeAnnotationNode(symbol.parent.valueDeclaration); - if (typeNode) { - return getTypeOfPropertyOfType(getTypeFromTypeNode(typeNode), symbol.escapedName); - } - } - - return declaredType; - } - - /** If we don't have an explicit JSDoc type, get the type from the initializer. */ - function getInitializerTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: BinaryExpression | CallExpression, kind: AssignmentDeclarationKind) { - if (isCallExpression(expression)) { - if (resolvedSymbol) { - return getTypeOfSymbol(resolvedSymbol); // This shouldn't happen except under some hopefully forbidden merges of export assignments and object define assignments - } - const objectLitType = checkExpressionCached((expression as BindableObjectDefinePropertyCall).arguments[2]); - const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String); - if (valueType) { - return valueType; - } - const getFunc = getTypeOfPropertyOfType(objectLitType, "get" as __String); - if (getFunc) { - const getSig = getSingleCallSignature(getFunc); - if (getSig) { - return getReturnTypeOfSignature(getSig); - } - } - const setFunc = getTypeOfPropertyOfType(objectLitType, "set" as __String); - if (setFunc) { - const setSig = getSingleCallSignature(setFunc); - if (setSig) { - return getTypeOfFirstParameterOfSignature(setSig); - } - } - return anyType; - } - const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : getWidenedLiteralType(checkExpressionCached(expression.right)); - if (type.flags & TypeFlags.Object && - kind === AssignmentDeclarationKind.ModuleExports && - symbol.escapedName === InternalSymbolName.ExportEquals) { - const exportedType = resolveStructuredTypeMembers(type as ObjectType); - const members = createSymbolTable(); - copyEntries(exportedType.members, members); - if (resolvedSymbol && !resolvedSymbol.exports) { - resolvedSymbol.exports = createSymbolTable(); - } - (resolvedSymbol || symbol).exports!.forEach((s, name) => { - if (members.has(name)) { - const exportedMember = exportedType.members.get(name)!; - const union = createSymbol(s.flags | exportedMember.flags, name); - union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]); - members.set(name, union); - } - else { - members.set(name, s); - } - }); - const result = createAnonymousType( - exportedType.symbol, - members, - exportedType.callSignatures, - exportedType.constructSignatures, - exportedType.stringIndexInfo, - exportedType.numberIndexInfo); - result.objectFlags |= (getObjectFlags(type) & ObjectFlags.JSLiteral); // Propagate JSLiteral flag - return result; - } - if (isEmptyArrayLiteralType(type)) { - reportImplicitAny(expression, anyArrayType); - return anyArrayType; - } - return type; - } - - function isDeclarationInConstructor(expression: Expression) { - const thisContainer = getThisContainer(expression, /*includeArrowFunctions*/ false); - // Properties defined in a constructor (or base constructor, or javascript constructor function) don't get undefined added. - // Function expressions that are assigned to the prototype count as methods. - return thisContainer.kind === SyntaxKind.Constructor || - thisContainer.kind === SyntaxKind.FunctionDeclaration || - (thisContainer.kind === SyntaxKind.FunctionExpression && !isPrototypePropertyAssignment(thisContainer.parent)); - } - - function getConstructorDefinedThisAssignmentTypes(types: Type[], declarations: Declaration[]): Type[] | undefined { - Debug.assert(types.length === declarations.length); - return types.filter((_, i) => { - const declaration = declarations[i]; - const expression = isBinaryExpression(declaration) ? declaration : - isBinaryExpression(declaration.parent) ? declaration.parent : undefined; - return expression && isDeclarationInConstructor(expression); - }); - } - - /** check for definition in base class if any declaration is in a class */ - function getTypeOfAssignmentDeclarationPropertyOfBaseType(property: Symbol) { - const parentDeclaration = forEach(property.declarations, d => { - const parent = getThisContainer(d, /*includeArrowFunctions*/ false).parent; - return isClassLike(parent) && parent; - }); - if (parentDeclaration) { - const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(parentDeclaration)) as InterfaceType; - const baseClassType = classType && getBaseTypes(classType)[0]; - if (baseClassType) { - return getTypeOfPropertyOfType(baseClassType, property.escapedName); - } - } - } - - // Return the type implied by a binding pattern element. This is the type of the initializer of the element if - // one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding - // pattern. Otherwise, it is the type any. - function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type { - if (element.initializer) { - return addOptionality(checkDeclarationInitializer(element)); - } - if (isBindingPattern(element.name)) { - return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors); - } - if (reportErrors && !declarationBelongsToPrivateAmbientMember(element)) { - reportImplicitAny(element, anyType); - } - // When we're including the pattern in the type (an indication we're obtaining a contextual type), we - // use the non-inferrable any type. Inference will never directly infer this type, but it is possible - // to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases, - // widening of the binding pattern type substitutes a regular any for the non-inferrable any. - return includePatternInType ? nonInferrableAnyType : anyType; - } - - // Return the type implied by an object binding pattern - function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { - const members = createSymbolTable(); - let stringIndexInfo: IndexInfo | undefined; - let objectFlags = ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral; - forEach(pattern.elements, e => { - const name = e.propertyName || e.name; - if (e.dotDotDotToken) { - stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false); - return; - } - - const exprType = getLiteralTypeFromPropertyName(name); - if (!isTypeUsableAsPropertyName(exprType)) { - // do not include computed properties in the implied type - objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; - return; - } - const text = getPropertyNameFromType(exprType); - const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0); - const symbol = createSymbol(flags, text); - symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); - symbol.bindingElement = e; - members.set(symbol.escapedName, symbol); - }); - const result = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, undefined); - result.objectFlags |= objectFlags; - if (includePatternInType) { - result.pattern = pattern; - result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; - } - return result; - } - - // Return the type implied by an array binding pattern - function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { - const elements = pattern.elements; - const lastElement = lastOrUndefined(elements); - const hasRestElement = !!(lastElement && lastElement.kind === SyntaxKind.BindingElement && lastElement.dotDotDotToken); - if (elements.length === 0 || elements.length === 1 && hasRestElement) { - return languageVersion >= ScriptTarget.ES2015 ? createIterableType(anyType) : anyArrayType; - } - const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors)); - const minLength = findLastIndex(elements, e => !isOmittedExpression(e) && !hasDefaultValue(e), elements.length - (hasRestElement ? 2 : 1)) + 1; - let result = createTupleType(elementTypes, minLength, hasRestElement); - if (includePatternInType) { - result = cloneTypeReference(result); - result.pattern = pattern; - result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; - } - return result; - } - - // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself - // and without regard to its context (i.e. without regard any type annotation or initializer associated with the - // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] - // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is - // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring - // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of - // the parameter. - function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType = false, reportErrors = false): Type { - return pattern.kind === SyntaxKind.ObjectBindingPattern - ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors) - : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors); - } - - // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type - // specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it - // is a bit more involved. For example: - // - // var [x, s = ""] = [1, "one"]; - // - // Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the - // binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the - // tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string. - function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, reportErrors?: boolean): Type { - return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true), declaration, reportErrors); - } - - function widenTypeForVariableLikeDeclaration(type: Type | undefined, declaration: any, reportErrors?: boolean) { - if (type) { - if (reportErrors) { - reportErrorsFromWidening(declaration, type); - } - - // always widen a 'unique symbol' type if the type was created for a different declaration. - if (type.flags & TypeFlags.UniqueESSymbol && (isBindingElement(declaration) || !declaration.type) && type.symbol !== getSymbolOfNode(declaration)) { - type = esSymbolType; - } - - return getWidenedType(type); - } - - // Rest parameters default to type any[], other parameters default to type any - type = isParameter(declaration) && declaration.dotDotDotToken ? anyArrayType : anyType; - - // Report implicit any errors unless this is a private property within an ambient declaration - if (reportErrors) { - if (!declarationBelongsToPrivateAmbientMember(declaration)) { - reportImplicitAny(declaration, type); - } - } - return type; - } - - function declarationBelongsToPrivateAmbientMember(declaration: VariableLikeDeclaration) { - const root = getRootDeclaration(declaration); - const memberDeclaration = root.kind === SyntaxKind.Parameter ? root.parent : root; - return isPrivateWithinAmbient(memberDeclaration); - } - - function tryGetTypeFromEffectiveTypeNode(declaration: Declaration) { - const typeNode = getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - } - - function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.type) { - const type = getTypeOfVariableOrParameterOrPropertyWorker(symbol); - // For a contextually typed parameter it is possible that a type has already - // been assigned (in assignTypeToParameterAndFixTypeParameters), and we want - // to preserve this type. - if (!links.type) { - links.type = type; - } - } - return links.type; - } - - function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol) { - // Handle prototype property - if (symbol.flags & SymbolFlags.Prototype) { - return getTypeOfPrototypeProperty(symbol); - } - // CommonsJS require and module both have type any. - if (symbol === requireSymbol) { - return anyType; - } - if (symbol.flags & SymbolFlags.ModuleExports) { - const fileSymbol = getSymbolOfNode(getSourceFileOfNode(symbol.valueDeclaration)); - const members = createSymbolTable(); - members.set("exports" as __String, fileSymbol); - return createAnonymousType(symbol, members, emptyArray, emptyArray, undefined, undefined); - } - // Handle catch clause variables - const declaration = symbol.valueDeclaration; - if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { - return anyType; - } - // Handle export default expressions - if (isSourceFile(declaration) && isJsonSourceFile(declaration)) { - if (!declaration.statements.length) { - return emptyObjectType; - } - return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression))); - } - - // Handle variable, parameter or property - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` - if (symbol.flags & SymbolFlags.ValueModule && !(symbol.flags & SymbolFlags.Assignment)) { - return getTypeOfFuncClassEnumModule(symbol); - } - return reportCircularityError(symbol); - } - let type: Type | undefined; - if (declaration.kind === SyntaxKind.ExportAssignment) { - type = widenTypeForVariableLikeDeclaration(checkExpressionCached((declaration).expression), declaration); - } - else if ( - isBinaryExpression(declaration) || - (isInJSFile(declaration) && - (isCallExpression(declaration) || (isPropertyAccessExpression(declaration) || isBindableStaticElementAccessExpression(declaration)) && isBinaryExpression(declaration.parent)))) { - type = getWidenedTypeForAssignmentDeclaration(symbol); - } - else if (isJSDocPropertyLikeTag(declaration) - || isPropertyAccessExpression(declaration) - || isElementAccessExpression(declaration) - || isIdentifier(declaration) - || isStringLiteralLike(declaration) - || isNumericLiteral(declaration) - || isClassDeclaration(declaration) - || isFunctionDeclaration(declaration) - || (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration)) - || isMethodSignature(declaration) - || isSourceFile(declaration)) { - // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` - if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { - return getTypeOfFuncClassEnumModule(symbol); - } - type = isBinaryExpression(declaration.parent) ? - getWidenedTypeForAssignmentDeclaration(symbol) : - tryGetTypeFromEffectiveTypeNode(declaration) || anyType; - } - else if (isPropertyAssignment(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) || checkPropertyAssignment(declaration); - } - else if (isJsxAttribute(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) || checkJsxAttribute(declaration); - } - else if (isShorthandPropertyAssignment(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionForMutableLocation(declaration.name, CheckMode.Normal); - } - else if (isObjectLiteralMethod(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal); - } - else if (isParameter(declaration) - || isPropertyDeclaration(declaration) - || isPropertySignature(declaration) - || isVariableDeclaration(declaration) - || isBindingElement(declaration)) { - type = getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true); - } - // getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive. - // Re-dispatch based on valueDeclaration.kind instead. - else if (isEnumDeclaration(declaration)) { - type = getTypeOfFuncClassEnumModule(symbol); - } - else if (isEnumMember(declaration)) { - type = getTypeOfEnumMember(symbol); - } - else if (isAccessor(declaration)) { - type = resolveTypeOfAccessors(symbol); - } - else { - return Debug.fail("Unhandled declaration kind! " + Debug.formatSyntaxKind(declaration.kind) + " for " + Debug.formatSymbol(symbol)); - } - - if (!popTypeResolution()) { - // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` - if (symbol.flags & SymbolFlags.ValueModule && !(symbol.flags & SymbolFlags.Assignment)) { - return getTypeOfFuncClassEnumModule(symbol); - } - return reportCircularityError(symbol); - } - return type; - } - - function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | undefined): TypeNode | undefined { - if (accessor) { - if (accessor.kind === SyntaxKind.GetAccessor) { - const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor); - return getterTypeAnnotation; - } - else { - const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor); - return setterTypeAnnotation; - } - } - return undefined; - } - - function getAnnotatedAccessorType(accessor: AccessorDeclaration | undefined): Type | undefined { - const node = getAnnotatedAccessorTypeNode(accessor); - return node && getTypeFromTypeNode(node); - } - - function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined { - const parameter = getAccessorThisParameter(accessor); - return parameter && parameter.symbol; - } - - function getThisTypeOfDeclaration(declaration: SignatureDeclaration): Type | undefined { - return getThisTypeOfSignature(getSignatureFromDeclaration(declaration)); - } - - function getTypeOfAccessors(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - return links.type || (links.type = getTypeOfAccessorsWorker(symbol)); - } - - function getTypeOfAccessorsWorker(symbol: Symbol): Type { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return errorType; - } - - let type = resolveTypeOfAccessors(symbol); - - if (!popTypeResolution()) { - type = anyType; - if (noImplicitAny) { - const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); - error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol)); - } - } - return type; - } - - function resolveTypeOfAccessors(symbol: Symbol) { - const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); - const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); - - if (getter && isInJSFile(getter)) { - const jsDocType = getTypeForDeclarationFromJSDocComment(getter); - if (jsDocType) { - return jsDocType; - } - } - // First try to see if the user specified a return type on the get-accessor. - const getterReturnType = getAnnotatedAccessorType(getter); - if (getterReturnType) { - return getterReturnType; - } - else { - // If the user didn't specify a return type, try to use the set-accessor's parameter type. - const setterParameterType = getAnnotatedAccessorType(setter); - if (setterParameterType) { - return setterParameterType; - } - else { - // If there are no specified types, try to infer it from the body of the get accessor if it exists. - if (getter && getter.body) { - return getReturnTypeFromBody(getter); - } - // Otherwise, fall back to 'any'. - else { - if (setter) { - if (!isPrivateWithinAmbient(setter)) { - errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol)); - } - } - else { - Debug.assert(!!getter, "there must exist a getter as we are current checking either setter or getter in this function"); - if (!isPrivateWithinAmbient(getter!)) { - errorOrSuggestion(noImplicitAny, getter!, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol)); - } - } - return anyType; - } - } - } - } - - function getBaseTypeVariableOfClass(symbol: Symbol) { - const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol)); - return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : - baseConstructorType.flags & TypeFlags.Intersection ? find((baseConstructorType as IntersectionType).types, t => !!(t.flags & TypeFlags.TypeVariable)) : - undefined; - } - - function getTypeOfFuncClassEnumModule(symbol: Symbol): Type { - let links = getSymbolLinks(symbol); - const originalLinks = links; - if (!links.type) { - const jsDeclaration = getDeclarationOfExpando(symbol.valueDeclaration); - if (jsDeclaration) { - const merged = mergeJSSymbols(symbol, getSymbolOfNode(jsDeclaration)); - if (merged) { - // note:we overwrite links because we just cloned the symbol - symbol = links = merged; - } - } - originalLinks.type = links.type = getTypeOfFuncClassEnumModuleWorker(symbol); - } - return links.type; - } - - function getTypeOfFuncClassEnumModuleWorker(symbol: Symbol): Type { - const declaration = symbol.valueDeclaration; - if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) { - return anyType; - } - else if (declaration.kind === SyntaxKind.BinaryExpression || - isAccessExpression(declaration) && - declaration.parent.kind === SyntaxKind.BinaryExpression) { - return getWidenedTypeForAssignmentDeclaration(symbol); - } - else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) { - const resolvedModule = resolveExternalModuleSymbol(symbol); - if (resolvedModule !== symbol) { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return errorType; - } - const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!); - const type = getWidenedTypeForAssignmentDeclaration(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule); - if (!popTypeResolution()) { - return reportCircularityError(symbol); - } - return type; - } - } - const type = createObjectType(ObjectFlags.Anonymous, symbol); - if (symbol.flags & SymbolFlags.Class) { - const baseTypeVariable = getBaseTypeVariableOfClass(symbol); - return baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type; - } - else { - return strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type) : type; - } - } - - function getTypeOfEnumMember(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - return links.type || (links.type = getDeclaredTypeOfEnumMember(symbol)); - } - - function getTypeOfAlias(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.type) { - const targetSymbol = resolveAlias(symbol); - - // It only makes sense to get the type of a value symbol. If the result of resolving - // the alias is not a value, then it has no type. To get the type associated with a - // type symbol, call getDeclaredTypeOfSymbol. - // This check is important because without it, a call to getTypeOfSymbol could end - // up recursively calling getTypeOfAlias, causing a stack overflow. - links.type = targetSymbol.flags & SymbolFlags.Value - ? getTypeOfSymbol(targetSymbol) - : errorType; - } - return links.type; - } - - function getTypeOfInstantiatedSymbol(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.type) { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return links.type = errorType; - } - let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper); - if (!popTypeResolution()) { - type = reportCircularityError(symbol); - } - links.type = type; - } - return links.type; - } - - function reportCircularityError(symbol: Symbol) { - const declaration = symbol.valueDeclaration; - // Check if variable has type annotation that circularly references the variable itself - if (getEffectiveTypeAnnotationNode(declaration)) { - error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, - symbolToString(symbol)); - return errorType; - } - // Check if variable has initializer that circularly references the variable itself - if (noImplicitAny && (declaration.kind !== SyntaxKind.Parameter || (declaration).initializer)) { - error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, - symbolToString(symbol)); - } - // Circularities could also result from parameters in function expressions that end up - // having themselves as contextual types following type argument inference. In those cases - // we have already reported an implicit any error so we don't report anything here. - return anyType; - } - - function getTypeOfSymbolWithDeferredType(symbol: Symbol) { - const links = getSymbolLinks(symbol); - if (!links.type) { - Debug.assertDefined(links.deferralParent); - Debug.assertDefined(links.deferralConstituents); - links.type = links.deferralParent!.flags & TypeFlags.Union ? getUnionType(links.deferralConstituents!) : getIntersectionType(links.deferralConstituents!); - } - return links.type; - } - - function getTypeOfSymbol(symbol: Symbol): Type { - if (getCheckFlags(symbol) & CheckFlags.DeferredType) { - return getTypeOfSymbolWithDeferredType(symbol); - } - if (getCheckFlags(symbol) & CheckFlags.Instantiated) { - return getTypeOfInstantiatedSymbol(symbol); - } - if (getCheckFlags(symbol) & CheckFlags.ReverseMapped) { - return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol); - } - if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { - return getTypeOfVariableOrParameterOrProperty(symbol); - } - if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { - return getTypeOfFuncClassEnumModule(symbol); - } - if (symbol.flags & SymbolFlags.EnumMember) { - return getTypeOfEnumMember(symbol); - } - if (symbol.flags & SymbolFlags.Accessor) { - return getTypeOfAccessors(symbol); - } - if (symbol.flags & SymbolFlags.Alias) { - return getTypeOfAlias(symbol); - } - return errorType; - } - - function isReferenceToType(type: Type, target: Type) { - return type !== undefined - && target !== undefined - && (getObjectFlags(type) & ObjectFlags.Reference) !== 0 - && (type).target === target; - } - - function getTargetType(type: Type): Type { - return getObjectFlags(type) & ObjectFlags.Reference ? (type).target : type; - } - - // TODO: GH#18217 If `checkBase` is undefined, we should not call this because this will always return false. - function hasBaseType(type: Type, checkBase: Type | undefined) { - return check(type); - function check(type: Type): boolean { - if (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) { - const target = getTargetType(type); - return target === checkBase || some(getBaseTypes(target), check); - } - else if (type.flags & TypeFlags.Intersection) { - return some((type).types, check); - } - return false; - } - } - - // Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set. - // The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set - // in-place and returns the same array. - function appendTypeParameters(typeParameters: TypeParameter[] | undefined, declarations: readonly TypeParameterDeclaration[]): TypeParameter[] | undefined { - for (const declaration of declarations) { - typeParameters = appendIfUnique(typeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration))); - } - return typeParameters; - } - - // Return the outer type parameters of a node or undefined if the node has no outer type parameters. - function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] | undefined { - while (true) { - node = node.parent; // TODO: GH#18217 Use SourceFile kind check instead - if (node && isBinaryExpression(node)) { - // prototype assignments get the outer type parameters of their constructor function - const assignmentKind = getAssignmentDeclarationKind(node); - if (assignmentKind === AssignmentDeclarationKind.Prototype || assignmentKind === AssignmentDeclarationKind.PrototypeProperty) { - const symbol = getSymbolOfNode(node.left); - if (symbol && symbol.parent && !findAncestor(symbol.parent.valueDeclaration, d => node === d)) { - node = symbol.parent.valueDeclaration; - } - } - } - if (!node) { - return undefined; - } - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.MethodSignature: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.JSDocTemplateTag: - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocEnumTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.MappedType: - case SyntaxKind.ConditionalType: - const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes); - if (node.kind === SyntaxKind.MappedType) { - return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter))); - } - else if (node.kind === SyntaxKind.ConditionalType) { - return concatenate(outerTypeParameters, getInferTypeParameters(node)); - } - const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(node)); - const thisType = includeThisTypes && - (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration || isJSConstructor(node)) && - getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node as ClassLikeDeclaration | InterfaceDeclaration)).thisType; - return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters; - } - } - } - - // The outer type parameters are those defined by enclosing generic classes, methods, or functions. - function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] | undefined { - const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration)!; - Debug.assert(!!declaration, "Class was missing valueDeclaration -OR- non-class had no interface declarations"); - return getOuterTypeParameters(declaration); - } - - // The local type parameters are the combined set of type parameters from all declarations of the class, - // interface, or type alias. - function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] | undefined { - let result: TypeParameter[] | undefined; - for (const node of symbol.declarations) { - if (node.kind === SyntaxKind.InterfaceDeclaration || - node.kind === SyntaxKind.ClassDeclaration || - node.kind === SyntaxKind.ClassExpression || - isJSConstructor(node) || - isTypeAlias(node)) { - const declaration = node; - result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration)); - } - } - return result; - } - - // The full set of type parameters for a generic class or interface type consists of its outer type parameters plus - // its locally declared type parameters. - function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] | undefined { - return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)); - } - - // A type is a mixin constructor if it has a single construct signature taking no type parameters and a single - // rest parameter of type any[]. - function isMixinConstructorType(type: Type) { - const signatures = getSignaturesOfType(type, SignatureKind.Construct); - if (signatures.length === 1) { - const s = signatures[0]; - return !s.typeParameters && s.parameters.length === 1 && signatureHasRestParameter(s) && getElementTypeOfArrayType(getTypeOfParameter(s.parameters[0])) === anyType; - } - return false; - } - - function isConstructorType(type: Type): boolean { - if (getSignaturesOfType(type, SignatureKind.Construct).length > 0) { - return true; - } - if (type.flags & TypeFlags.TypeVariable) { - const constraint = getBaseConstraintOfType(type); - return !!constraint && isMixinConstructorType(constraint); - } - return false; - } - - function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments | undefined { - return getEffectiveBaseTypeNode(type.symbol.valueDeclaration as ClassLikeDeclaration); - } - - function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: readonly TypeNode[] | undefined, location: Node): readonly Signature[] { - const typeArgCount = length(typeArgumentNodes); - const isJavascript = isInJSFile(location); - return filter(getSignaturesOfType(type, SignatureKind.Construct), - sig => (isJavascript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters)); - } - - function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: readonly TypeNode[] | undefined, location: Node): readonly Signature[] { - const signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location); - const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode); - return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments, isInJSFile(location)) : sig); - } - - /** - * The base constructor of a class can resolve to - * * undefinedType if the class has no extends clause, - * * unknownType if an error occurred during resolution of the extends expression, - * * nullType if the extends expression is the null value, - * * anyType if the extends expression has type any, or - * * an object type with at least one construct signature. - */ - function getBaseConstructorTypeOfClass(type: InterfaceType): Type { - if (!type.resolvedBaseConstructorType) { - const decl = type.symbol.valueDeclaration; - const extended = getEffectiveBaseTypeNode(decl); - const baseTypeNode = getBaseTypeNodeOfClass(type); - if (!baseTypeNode) { - return type.resolvedBaseConstructorType = undefinedType; - } - if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) { - return errorType; - } - const baseConstructorType = checkExpression(baseTypeNode.expression); - if (extended && baseTypeNode !== extended) { - Debug.assert(!extended.typeArguments); // Because this is in a JS file, and baseTypeNode is in an @extends tag - checkExpression(extended.expression); - } - if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) { - // Resolving the members of a class requires us to resolve the base class of that class. - // We force resolution here such that we catch circularities now. - resolveStructuredTypeMembers(baseConstructorType); - } - if (!popTypeResolution()) { - error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); - return type.resolvedBaseConstructorType = errorType; - } - if (!(baseConstructorType.flags & TypeFlags.Any) && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) { - const err = error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType)); - if (baseConstructorType.flags & TypeFlags.TypeParameter) { - const constraint = getConstraintFromTypeParameter(baseConstructorType); - let ctorReturn: Type = unknownType; - if (constraint) { - const ctorSig = getSignaturesOfType(constraint, SignatureKind.Construct); - if (ctorSig[0]) { - ctorReturn = getReturnTypeOfSignature(ctorSig[0]); - } - } - addRelatedInfo(err, createDiagnosticForNode(baseConstructorType.symbol.declarations[0], Diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, symbolToString(baseConstructorType.symbol), typeToString(ctorReturn))); - } - return type.resolvedBaseConstructorType = errorType; - } - type.resolvedBaseConstructorType = baseConstructorType; - } - return type.resolvedBaseConstructorType; - } - - function getBaseTypes(type: InterfaceType): BaseType[] { - if (!type.resolvedBaseTypes) { - if (type.objectFlags & ObjectFlags.Tuple) { - type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray), (type).readonly)]; - } - else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - if (type.symbol.flags & SymbolFlags.Class) { - resolveBaseTypesOfClass(type); - } - if (type.symbol.flags & SymbolFlags.Interface) { - resolveBaseTypesOfInterface(type); - } - } - else { - Debug.fail("type must be class or interface"); - } - } - return type.resolvedBaseTypes; - } - - function resolveBaseTypesOfClass(type: InterfaceType) { - type.resolvedBaseTypes = resolvingEmptyArray; - const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); - if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) { - return type.resolvedBaseTypes = emptyArray; - } - const baseTypeNode = getBaseTypeNodeOfClass(type)!; - let baseType: Type; - const originalBaseType = baseConstructorType.symbol ? getDeclaredTypeOfSymbol(baseConstructorType.symbol) : undefined; - if (baseConstructorType.symbol && baseConstructorType.symbol.flags & SymbolFlags.Class && - areAllOuterTypeParametersApplied(originalBaseType!)) { - // When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the - // class and all return the instance type of the class. There is no need for further checks and we can apply the - // type arguments in the same manner as a type reference to get the same error reporting experience. - baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol); - } - else if (baseConstructorType.flags & TypeFlags.Any) { - baseType = baseConstructorType; - } - else { - // The class derives from a "class-like" constructor function, check that we have at least one construct signature - // with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere - // we check that all instantiated signatures return the same type. - const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode); - if (!constructors.length) { - error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments); - return type.resolvedBaseTypes = emptyArray; - } - baseType = getReturnTypeOfSignature(constructors[0]); - } - - if (baseType === errorType) { - return type.resolvedBaseTypes = emptyArray; - } - if (!isValidBaseType(baseType)) { - error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_an_object_type_or_intersection_of_object_types_with_statically_known_members, typeToString(baseType)); - return type.resolvedBaseTypes = emptyArray; - } - if (type === baseType || hasBaseType(baseType, type)) { - error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, - typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); - return type.resolvedBaseTypes = emptyArray; - } - if (type.resolvedBaseTypes === resolvingEmptyArray) { - // Circular reference, likely through instantiation of default parameters - // (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset - // as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a - // partial instantiation of the members without the base types fully resolved - type.members = undefined; - } - return type.resolvedBaseTypes = [baseType]; - } - - function areAllOuterTypeParametersApplied(type: Type): boolean { // TODO: GH#18217 Shouldn't this take an InterfaceType? - // An unapplied type parameter has its symbol still the same as the matching argument symbol. - // Since parameters are applied outer-to-inner, only the last outer parameter needs to be checked. - const outerTypeParameters = (type).outerTypeParameters; - if (outerTypeParameters) { - const last = outerTypeParameters.length - 1; - const typeArguments = getTypeArguments(type); - return outerTypeParameters[last].symbol !== typeArguments[last].symbol; - } - return true; - } - - // A valid base type is `any`, an object type or intersection of object types. - function isValidBaseType(type: Type): type is BaseType { - if (type.flags & TypeFlags.TypeParameter) { - const constraint = getBaseConstraintOfType(type); - if (constraint) { - return isValidBaseType(constraint); - } - } - // TODO: Given that we allow type parmeters here now, is this `!isGenericMappedType(type)` check really needed? - // There's no reason a `T` should be allowed while a `Readonly` should not. - return !!(type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any)) && !isGenericMappedType(type) || - !!(type.flags & TypeFlags.Intersection) && every((type).types, isValidBaseType); - } - - function resolveBaseTypesOfInterface(type: InterfaceType): void { - type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; - for (const declaration of type.symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { - for (const node of getInterfaceBaseTypeNodes(declaration)!) { - const baseType = getTypeFromTypeNode(node); - if (baseType !== errorType) { - if (isValidBaseType(baseType)) { - if (type !== baseType && !hasBaseType(baseType, type)) { - if (type.resolvedBaseTypes === emptyArray) { - type.resolvedBaseTypes = [baseType]; - } - else { - type.resolvedBaseTypes.push(baseType); - } - } - else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); - } - } - else { - error(node, Diagnostics.An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members); - } - } - } - } - } - } - - /** - * Returns true if the interface given by the symbol is free of "this" references. - * - * Specifically, the result is true if the interface itself contains no references - * to "this" in its body, if all base types are interfaces, - * and if none of the base interfaces have a "this" type. - */ - function isThislessInterface(symbol: Symbol): boolean { - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration) { - if (declaration.flags & NodeFlags.ContainsThis) { - return false; - } - const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); - if (baseTypeNodes) { - for (const node of baseTypeNodes) { - if (isEntityNameExpression(node.expression)) { - const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); - if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { - return false; - } - } - } - } - } - } - return true; - } - - function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType { - let links = getSymbolLinks(symbol); - const originalLinks = links; - if (!links.declaredType) { - const kind = symbol.flags & SymbolFlags.Class ? ObjectFlags.Class : ObjectFlags.Interface; - const merged = mergeJSSymbols(symbol, getAssignedClassSymbol(symbol.valueDeclaration)); - if (merged) { - // note:we overwrite links because we just cloned the symbol - symbol = links = merged; - } - - const type = originalLinks.declaredType = links.declaredType = createObjectType(kind, symbol); - const outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol); - const localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - // A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type - // because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular, - // property types inferred from initializers and method return types inferred from return statements are very hard - // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of - // "this" references. - if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isThislessInterface(symbol)) { - type.objectFlags |= ObjectFlags.Reference; - type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); - type.outerTypeParameters = outerTypeParameters; - type.localTypeParameters = localTypeParameters; - (type).instantiations = createMap(); - (type).instantiations.set(getTypeListId(type.typeParameters), type); - (type).target = type; - (type).resolvedTypeArguments = type.typeParameters; - type.thisType = createTypeParameter(symbol); - type.thisType.isThisType = true; - type.thisType.constraint = type; - } - } - return links.declaredType; - } - - function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.declaredType) { - // Note that we use the links object as the target here because the symbol object is used as the unique - // identity for resolution of the 'type' property in SymbolLinks. - if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) { - return errorType; - } - - let type: Type; - let declaration; - if (isTypeOnlyAlias(symbol)) { - // Symbol is synthetic type alias for type-only import or export. - // See `createSyntheticTypeAlias`. - type = getDeclaredTypeOfSymbol(symbol.immediateTarget); - declaration = symbol.valueDeclaration; - } - else { - declaration = Debug.assertDefined(find(symbol.declarations, isTypeAlias), "Type alias symbol with no valid declaration found"); - const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type; - // If typeNode is missing, we will error in checkJSDocTypedefTag. - type = typeNode ? getTypeFromTypeNode(typeNode) : errorType; - } - - if (popTypeResolution()) { - const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - if (typeParameters) { - // Initialize the instantiation cache for generic type aliases. The declared type corresponds to - // an instantiation of the type alias with the type parameters supplied as type arguments. - links.typeParameters = typeParameters; - links.instantiations = createMap(); - links.instantiations.set(getTypeListId(typeParameters), type); - } - } - else { - type = errorType; - error(isNamedDeclaration(declaration) ? declaration.name : declaration || declaration, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); - } - links.declaredType = type; - } - return links.declaredType; - } - - function isStringConcatExpression(expr: Node): boolean { - if (isStringLiteralLike(expr)) { - return true; - } - else if (expr.kind === SyntaxKind.BinaryExpression) { - return isStringConcatExpression((expr).left) && isStringConcatExpression((expr).right); - } - return false; - } - - function isLiteralEnumMember(member: EnumMember) { - const expr = member.initializer; - if (!expr) { - return !(member.flags & NodeFlags.Ambient); - } - switch (expr.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return true; - case SyntaxKind.PrefixUnaryExpression: - return (expr).operator === SyntaxKind.MinusToken && - (expr).operand.kind === SyntaxKind.NumericLiteral; - case SyntaxKind.Identifier: - return nodeIsMissing(expr) || !!getSymbolOfNode(member.parent).exports!.get((expr).escapedText); - case SyntaxKind.BinaryExpression: - return isStringConcatExpression(expr); - default: - return false; - } - } - - function getEnumKind(symbol: Symbol): EnumKind { - const links = getSymbolLinks(symbol); - if (links.enumKind !== undefined) { - return links.enumKind; - } - let hasNonLiteralMember = false; - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { - for (const member of (declaration).members) { - if (member.initializer && isStringLiteralLike(member.initializer)) { - return links.enumKind = EnumKind.Literal; - } - if (!isLiteralEnumMember(member)) { - hasNonLiteralMember = true; - } - } - } - } - return links.enumKind = hasNonLiteralMember ? EnumKind.Numeric : EnumKind.Literal; - } - - function getBaseTypeOfEnumLiteralType(type: Type) { - return type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union) ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)!) : type; - } - - function getDeclaredTypeOfEnum(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (links.declaredType) { - return links.declaredType; - } - if (getEnumKind(symbol) === EnumKind.Literal) { - enumCount++; - const memberTypeList: Type[] = []; - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { - for (const member of (declaration).members) { - const value = getEnumMemberValue(member); - const memberType = getFreshTypeOfLiteralType(getLiteralType(value !== undefined ? value : 0, enumCount, getSymbolOfNode(member))); - getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType; - memberTypeList.push(getRegularTypeOfLiteralType(memberType)); - } - } - } - if (memberTypeList.length) { - const enumType = getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined); - if (enumType.flags & TypeFlags.Union) { - enumType.flags |= TypeFlags.EnumLiteral; - enumType.symbol = symbol; - } - return links.declaredType = enumType; - } - } - const enumType = createType(TypeFlags.Enum); - enumType.symbol = symbol; - return links.declaredType = enumType; - } - - function getDeclaredTypeOfEnumMember(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.declaredType) { - const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)!); - if (!links.declaredType) { - links.declaredType = enumType; - } - } - return links.declaredType; - } - - function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter { - const links = getSymbolLinks(symbol); - return links.declaredType || (links.declaredType = createTypeParameter(symbol)); - } - - function getDeclaredTypeOfAlias(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - return links.declaredType || (links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol))); - } - - function getDeclaredTypeOfSymbol(symbol: Symbol): Type { - return tryGetDeclaredTypeOfSymbol(symbol) || errorType; - } - - function tryGetDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined { - if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - return getDeclaredTypeOfClassOrInterface(symbol); - } - if (symbol.flags & SymbolFlags.TypeAlias) { - return getDeclaredTypeOfTypeAlias(symbol); - } - if (symbol.flags & SymbolFlags.TypeParameter) { - return getDeclaredTypeOfTypeParameter(symbol); - } - if (symbol.flags & SymbolFlags.Enum) { - return getDeclaredTypeOfEnum(symbol); - } - if (symbol.flags & SymbolFlags.EnumMember) { - return getDeclaredTypeOfEnumMember(symbol); - } - if (symbol.flags & SymbolFlags.Alias) { - return getDeclaredTypeOfAlias(symbol); - } - return undefined; - } - - /** - * A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string - * literal type, an array with an element type that is free of this references, or a type reference that is - * free of this references. - */ - function isThislessType(node: TypeNode): boolean { - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.UnknownKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.BigIntKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.ObjectKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.LiteralType: - return true; - case SyntaxKind.ArrayType: - return isThislessType((node).elementType); - case SyntaxKind.TypeReference: - return !(node as TypeReferenceNode).typeArguments || (node as TypeReferenceNode).typeArguments!.every(isThislessType); - } - return false; - } - - /** A type parameter is thisless if its constraint is thisless, or if it has no constraint. */ - function isThislessTypeParameter(node: TypeParameterDeclaration) { - const constraint = getEffectiveConstraintOfTypeParameter(node); - return !constraint || isThislessType(constraint); - } - - /** - * A variable-like declaration is free of this references if it has a type annotation - * that is thisless, or if it has no type annotation and no initializer (and is thus of type any). - */ - function isThislessVariableLikeDeclaration(node: VariableLikeDeclaration): boolean { - const typeNode = getEffectiveTypeAnnotationNode(node); - return typeNode ? isThislessType(typeNode) : !hasInitializer(node); - } - - /** - * A function-like declaration is considered free of `this` references if it has a return type - * annotation that is free of this references and if each parameter is thisless and if - * each type parameter (if present) is thisless. - */ - function isThislessFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { - const returnType = getEffectiveReturnTypeNode(node); - const typeParameters = getEffectiveTypeParameterDeclarations(node); - return (node.kind === SyntaxKind.Constructor || (!!returnType && isThislessType(returnType))) && - node.parameters.every(isThislessVariableLikeDeclaration) && - typeParameters.every(isThislessTypeParameter); - } - - /** - * Returns true if the class or interface member given by the symbol is free of "this" references. The - * function may return false for symbols that are actually free of "this" references because it is not - * feasible to perform a complete analysis in all cases. In particular, property members with types - * inferred from their initializers and function members with inferred return types are conservatively - * assumed not to be free of "this" references. - */ - function isThisless(symbol: Symbol): boolean { - if (symbol.declarations && symbol.declarations.length === 1) { - const declaration = symbol.declarations[0]; - if (declaration) { - switch (declaration.kind) { - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - return isThislessVariableLikeDeclaration(declaration); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return isThislessFunctionLikeDeclaration(declaration); - } - } - } - return false; - } - - // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, - // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. - function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { - const result = createSymbolTable(); - for (const symbol of symbols) { - result.set(symbol.escapedName, mappingThisOnly && isThisless(symbol) ? symbol : instantiateSymbol(symbol, mapper)); - } - return result; - } - - function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { - for (const s of baseSymbols) { - if (!symbols.has(s.escapedName) && !isStaticPrivateIdentifierProperty(s)) { - symbols.set(s.escapedName, s); - } - } - } - - function isStaticPrivateIdentifierProperty(s: Symbol): boolean { - return !!s.valueDeclaration && isPrivateIdentifierPropertyDeclaration(s.valueDeclaration) && hasModifier(s.valueDeclaration, ModifierFlags.Static); - } - - function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers { - if (!(type).declaredProperties) { - const symbol = type.symbol; - const members = getMembersOfSymbol(symbol); - (type).declaredProperties = getNamedMembers(members); - // Start with signatures at empty array in case of recursive types - (type).declaredCallSignatures = emptyArray; - (type).declaredConstructSignatures = emptyArray; - - (type).declaredCallSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); - (type).declaredConstructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New)); - (type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); - (type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); - } - return type; - } - - /** - * Indicates whether a type can be used as a property name. - */ - function isTypeUsableAsPropertyName(type: Type): type is StringLiteralType | NumberLiteralType | UniqueESSymbolType { - return !!(type.flags & TypeFlags.StringOrNumberLiteralOrUnique); - } - - /** - * Indicates whether a declaration name is definitely late-bindable. - * A declaration name is only late-bindable if: - * - It is a `ComputedPropertyName`. - * - Its expression is an `Identifier` or either a `PropertyAccessExpression` an - * `ElementAccessExpression` consisting only of these same three types of nodes. - * - The type of its expression is a string or numeric literal type, or is a `unique symbol` type. - */ - function isLateBindableName(node: DeclarationName): node is LateBoundName { - if (!isComputedPropertyName(node) && !isElementAccessExpression(node)) { - return false; - } - const expr = isComputedPropertyName(node) ? node.expression : node.argumentExpression; - return isEntityNameExpression(expr) - && isTypeUsableAsPropertyName(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached(expr)); - } - - function isLateBoundName(name: __String): boolean { - return (name as string).charCodeAt(0) === CharacterCodes._ && - (name as string).charCodeAt(1) === CharacterCodes._ && - (name as string).charCodeAt(2) === CharacterCodes.at; - } - - /** - * Indicates whether a declaration has a late-bindable dynamic name. - */ - function hasLateBindableName(node: Declaration): node is LateBoundDeclaration | LateBoundBinaryExpressionDeclaration { - const name = getNameOfDeclaration(node); - return !!name && isLateBindableName(name); - } - - /** - * Indicates whether a declaration has a dynamic name that cannot be late-bound. - */ - function hasNonBindableDynamicName(node: Declaration) { - return hasDynamicName(node) && !hasLateBindableName(node); - } - - /** - * Indicates whether a declaration name is a dynamic name that cannot be late-bound. - */ - function isNonBindableDynamicName(node: DeclarationName) { - return isDynamicName(node) && !isLateBindableName(node); - } - - /** - * Gets the symbolic name for a member from its type. - */ - function getPropertyNameFromType(type: StringLiteralType | NumberLiteralType | UniqueESSymbolType): __String { - if (type.flags & TypeFlags.UniqueESSymbol) { - return (type).escapedName; - } - if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { - return escapeLeadingUnderscores("" + (type).value); - } - return Debug.fail(); - } - - /** - * Adds a declaration to a late-bound dynamic member. This performs the same function for - * late-bound members that `addDeclarationToSymbol` in binder.ts performs for early-bound - * members. - */ - function addDeclarationToLateBoundSymbol(symbol: Symbol, member: LateBoundDeclaration | BinaryExpression, symbolFlags: SymbolFlags) { - Debug.assert(!!(getCheckFlags(symbol) & CheckFlags.Late), "Expected a late-bound symbol."); - symbol.flags |= symbolFlags; - getSymbolLinks(member.symbol).lateSymbol = symbol; - if (!symbol.declarations) { - symbol.declarations = [member]; - } - else { - symbol.declarations.push(member); - } - if (symbolFlags & SymbolFlags.Value) { - if (!symbol.valueDeclaration || symbol.valueDeclaration.kind !== member.kind) { - symbol.valueDeclaration = member; - } - } - } - - /** - * Performs late-binding of a dynamic member. This performs the same function for - * late-bound members that `declareSymbol` in binder.ts performs for early-bound - * members. - * - * If a symbol is a dynamic name from a computed property, we perform an additional "late" - * binding phase to attempt to resolve the name for the symbol from the type of the computed - * property's expression. If the type of the expression is a string-literal, numeric-literal, - * or unique symbol type, we can use that type as the name of the symbol. - * - * For example, given: - * - * const x = Symbol(); - * - * interface I { - * [x]: number; - * } - * - * The binder gives the property `[x]: number` a special symbol with the name "__computed". - * In the late-binding phase we can type-check the expression `x` and see that it has a - * unique symbol type which we can then use as the name of the member. This allows users - * to define custom symbols that can be used in the members of an object type. - * - * @param parent The containing symbol for the member. - * @param earlySymbols The early-bound symbols of the parent. - * @param lateSymbols The late-bound symbols of the parent. - * @param decl The member to bind. - */ - function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: SymbolTable, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration) { - Debug.assert(!!decl.symbol, "The member is expected to have a symbol."); - const links = getNodeLinks(decl); - if (!links.resolvedSymbol) { - // In the event we attempt to resolve the late-bound name of this member recursively, - // fall back to the early-bound name of this member. - links.resolvedSymbol = decl.symbol; - const declName = isBinaryExpression(decl) ? decl.left : decl.name; - const type = isElementAccessExpression(declName) ? checkExpressionCached(declName.argumentExpression) : checkComputedPropertyName(declName); - if (isTypeUsableAsPropertyName(type)) { - const memberName = getPropertyNameFromType(type); - const symbolFlags = decl.symbol.flags; - - // Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations. - let lateSymbol = lateSymbols.get(memberName); - if (!lateSymbol) lateSymbols.set(memberName, lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late)); - - // Report an error if a late-bound member has the same name as an early-bound member, - // or if we have another early-bound symbol declaration with the same name and - // conflicting flags. - const earlySymbol = earlySymbols && earlySymbols.get(memberName); - if (lateSymbol.flags & getExcludedSymbolFlags(symbolFlags) || earlySymbol) { - // If we have an existing early-bound member, combine its declarations so that we can - // report an error at each declaration. - const declarations = earlySymbol ? concatenate(earlySymbol.declarations, lateSymbol.declarations) : lateSymbol.declarations; - const name = !(type.flags & TypeFlags.UniqueESSymbol) && unescapeLeadingUnderscores(memberName) || declarationNameToString(declName); - forEach(declarations, declaration => error(getNameOfDeclaration(declaration) || declaration, Diagnostics.Property_0_was_also_declared_here, name)); - error(declName || decl, Diagnostics.Duplicate_property_0, name); - lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late); - } - lateSymbol.nameType = type; - addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags); - if (lateSymbol.parent) { - Debug.assert(lateSymbol.parent === parent, "Existing symbol parent should match new one"); - } - else { - lateSymbol.parent = parent; - } - return links.resolvedSymbol = lateSymbol; - } - } - return links.resolvedSymbol; - } - - function getResolvedMembersOrExportsOfSymbol(symbol: Symbol, resolutionKind: MembersOrExportsResolutionKind): UnderscoreEscapedMap { - const links = getSymbolLinks(symbol); - if (!links[resolutionKind]) { - const isStatic = resolutionKind === MembersOrExportsResolutionKind.resolvedExports; - const earlySymbols = !isStatic ? symbol.members : - symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol) : - symbol.exports; - - // In the event we recursively resolve the members/exports of the symbol, we - // set the initial value of resolvedMembers/resolvedExports to the early-bound - // members/exports of the symbol. - links[resolutionKind] = earlySymbols || emptySymbols; - - // fill in any as-yet-unresolved late-bound members. - const lateSymbols = createSymbolTable(); - for (const decl of symbol.declarations) { - const members = getMembersOfDeclaration(decl); - if (members) { - for (const member of members) { - if (isStatic === hasStaticModifier(member) && hasLateBindableName(member)) { - lateBindMember(symbol, earlySymbols, lateSymbols, member); - } - } - } - } - const assignments = symbol.assignmentDeclarationMembers; - if (assignments) { - const decls = arrayFrom(assignments.values()); - for (const member of decls) { - const assignmentKind = getAssignmentDeclarationKind(member as BinaryExpression | CallExpression); - const isInstanceMember = assignmentKind === AssignmentDeclarationKind.PrototypeProperty - || assignmentKind === AssignmentDeclarationKind.ThisProperty - || assignmentKind === AssignmentDeclarationKind.ObjectDefinePrototypeProperty - || assignmentKind === AssignmentDeclarationKind.Prototype; // A straight `Prototype` assignment probably can never have a computed name - if (isStatic === !isInstanceMember && hasLateBindableName(member)) { - lateBindMember(symbol, earlySymbols, lateSymbols, member); - } - } - } - - links[resolutionKind] = combineSymbolTables(earlySymbols, lateSymbols) || emptySymbols; - } - - return links[resolutionKind]!; - } - - /** - * Gets a SymbolTable containing both the early- and late-bound members of a symbol. - * - * For a description of late-binding, see `lateBindMember`. - */ - function getMembersOfSymbol(symbol: Symbol) { - return symbol.flags & SymbolFlags.LateBindingContainer - ? getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKind.resolvedMembers) - : symbol.members || emptySymbols; - } - - /** - * If a symbol is the dynamic name of the member of an object type, get the late-bound - * symbol of the member. - * - * For a description of late-binding, see `lateBindMember`. - */ - function getLateBoundSymbol(symbol: Symbol): Symbol { - if (symbol.flags & SymbolFlags.ClassMember && symbol.escapedName === InternalSymbolName.Computed) { - const links = getSymbolLinks(symbol); - if (!links.lateSymbol && some(symbol.declarations, hasLateBindableName)) { - // force late binding of members/exports. This will set the late-bound symbol - const parent = getMergedSymbol(symbol.parent)!; - if (some(symbol.declarations, hasStaticModifier)) { - getExportsOfSymbol(parent); - } - else { - getMembersOfSymbol(parent); - } - } - return links.lateSymbol || (links.lateSymbol = symbol); - } - return symbol; - } - - function getTypeWithThisArgument(type: Type, thisArgument?: Type, needApparentType?: boolean): Type { - if (getObjectFlags(type) & ObjectFlags.Reference) { - const target = (type).target; - const typeArguments = getTypeArguments(type); - if (length(target.typeParameters) === length(typeArguments)) { - const ref = createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType!])); - return needApparentType ? getApparentType(ref) : ref; - } - } - else if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map((type).types, t => getTypeWithThisArgument(t, thisArgument, needApparentType))); - } - return needApparentType ? getApparentType(type) : type; - } - - function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: readonly TypeParameter[], typeArguments: readonly Type[]) { - let mapper: TypeMapper; - let members: SymbolTable; - let callSignatures: readonly Signature[]; - let constructSignatures: readonly Signature[] | undefined; - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - if (rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) { - mapper = identityMapper; - members = source.symbol ? getMembersOfSymbol(source.symbol) : createSymbolTable(source.declaredProperties); - callSignatures = source.declaredCallSignatures; - constructSignatures = source.declaredConstructSignatures; - stringIndexInfo = source.declaredStringIndexInfo; - numberIndexInfo = source.declaredNumberIndexInfo; - } - else { - mapper = createTypeMapper(typeParameters, typeArguments); - members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1); - callSignatures = instantiateSignatures(source.declaredCallSignatures, mapper); - constructSignatures = instantiateSignatures(source.declaredConstructSignatures, mapper); - stringIndexInfo = instantiateIndexInfo(source.declaredStringIndexInfo, mapper); - numberIndexInfo = instantiateIndexInfo(source.declaredNumberIndexInfo, mapper); - } - const baseTypes = getBaseTypes(source); - if (baseTypes.length) { - if (source.symbol && members === getMembersOfSymbol(source.symbol)) { - members = createSymbolTable(source.declaredProperties); - } - setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); - const thisArgument = lastOrUndefined(typeArguments); - for (const baseType of baseTypes) { - const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; - addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType)); - callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); - constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); - if (!stringIndexInfo) { - stringIndexInfo = instantiatedBaseType === anyType ? - createIndexInfo(anyType, /*isReadonly*/ false) : - getIndexInfoOfType(instantiatedBaseType, IndexKind.String); - } - numberIndexInfo = numberIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.Number); - } - } - setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); - } - - function resolveClassOrInterfaceMembers(type: InterfaceType): void { - resolveObjectTypeMembers(type, resolveDeclaredMembers(type), emptyArray, emptyArray); - } - - function resolveTypeReferenceMembers(type: TypeReference): void { - const source = resolveDeclaredMembers(type.target); - const typeParameters = concatenate(source.typeParameters!, [source.thisType!]); - const typeArguments = getTypeArguments(type); - const paddedTypeArguments = typeArguments.length === typeParameters.length ? typeArguments : concatenate(typeArguments, [type]); - resolveObjectTypeMembers(type, source, typeParameters, paddedTypeArguments); - } - - function createSignature( - declaration: SignatureDeclaration | JSDocSignature | undefined, - typeParameters: readonly TypeParameter[] | undefined, - thisParameter: Symbol | undefined, - parameters: readonly Symbol[], - resolvedReturnType: Type | undefined, - resolvedTypePredicate: TypePredicate | undefined, - minArgumentCount: number, - flags: SignatureFlags - ): Signature { - const sig = new Signature(checker, flags); - sig.declaration = declaration; - sig.typeParameters = typeParameters; - sig.parameters = parameters; - sig.thisParameter = thisParameter; - sig.resolvedReturnType = resolvedReturnType; - sig.resolvedTypePredicate = resolvedTypePredicate; - sig.minArgumentCount = minArgumentCount; - sig.target = undefined; - sig.mapper = undefined; - sig.unionSignatures = undefined; - return sig; - } - - function cloneSignature(sig: Signature): Signature { - const result = createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, /*resolvedReturnType*/ undefined, - /*resolvedTypePredicate*/ undefined, sig.minArgumentCount, sig.flags & SignatureFlags.PropagatingFlags); - result.target = sig.target; - result.mapper = sig.mapper; - result.unionSignatures = sig.unionSignatures; - return result; - } - - function createUnionSignature(signature: Signature, unionSignatures: Signature[]) { - const result = cloneSignature(signature); - result.unionSignatures = unionSignatures; - result.target = undefined; - result.mapper = undefined; - return result; - } - - function getOptionalCallSignature(signature: Signature, callChainFlags: SignatureFlags): Signature { - if ((signature.flags & SignatureFlags.CallChainFlags) === callChainFlags) { - return signature; - } - if (!signature.optionalCallSignatureCache) { - signature.optionalCallSignatureCache = {}; - } - const key = callChainFlags === SignatureFlags.IsInnerCallChain ? "inner" : "outer"; - return signature.optionalCallSignatureCache[key] - || (signature.optionalCallSignatureCache[key] = createOptionalCallSignature(signature, callChainFlags)); - } - - function createOptionalCallSignature(signature: Signature, callChainFlags: SignatureFlags) { - Debug.assert(callChainFlags === SignatureFlags.IsInnerCallChain || callChainFlags === SignatureFlags.IsOuterCallChain, - "An optional call signature can either be for an inner call chain or an outer call chain, but not both."); - const result = cloneSignature(signature); - result.flags |= callChainFlags; - return result; - } - - function getExpandedParameters(sig: Signature): readonly Symbol[] { - if (signatureHasRestParameter(sig)) { - const restIndex = sig.parameters.length - 1; - const restParameter = sig.parameters[restIndex]; - const restType = getTypeOfSymbol(restParameter); - if (isTupleType(restType)) { - const elementTypes = getTypeArguments(restType); - const minLength = restType.target.minLength; - const tupleRestIndex = restType.target.hasRestElement ? elementTypes.length - 1 : -1; - const restParams = map(elementTypes, (t, i) => { - const name = getParameterNameAtPosition(sig, restIndex + i); - const checkFlags = i === tupleRestIndex ? CheckFlags.RestParameter : - i >= minLength ? CheckFlags.OptionalParameter : 0; - const symbol = createSymbol(SymbolFlags.FunctionScopedVariable, name, checkFlags); - symbol.type = i === tupleRestIndex ? createArrayType(t) : t; - return symbol; - }); - return concatenate(sig.parameters.slice(0, restIndex), restParams); - } - } - return sig.parameters; - } - - function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { - const baseConstructorType = getBaseConstructorTypeOfClass(classType); - const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); - if (baseSignatures.length === 0) { - return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None)]; - } - const baseTypeNode = getBaseTypeNodeOfClass(classType)!; - const isJavaScript = isInJSFile(baseTypeNode); - const typeArguments = typeArgumentsFromTypeReferenceNode(baseTypeNode); - const typeArgCount = length(typeArguments); - const result: Signature[] = []; - for (const baseSig of baseSignatures) { - const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters); - const typeParamCount = length(baseSig.typeParameters); - if (isJavaScript || typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount) { - const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig); - sig.typeParameters = classType.localTypeParameters; - sig.resolvedReturnType = classType; - result.push(sig); - } - } - return result; - } - - function findMatchingSignature(signatureList: readonly Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature | undefined { - for (const s of signatureList) { - if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, partialMatch ? compareTypesSubtypeOf : compareTypesIdentical)) { - return s; - } - } - } - - function findMatchingSignatures(signatureLists: readonly (readonly Signature[])[], signature: Signature, listIndex: number): Signature[] | undefined { - if (signature.typeParameters) { - // We require an exact match for generic signatures, so we only return signatures from the first - // signature list and only if they have exact matches in the other signature lists. - if (listIndex > 0) { - return undefined; - } - for (let i = 1; i < signatureLists.length; i++) { - if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false)) { - return undefined; - } - } - return [signature]; - } - let result: Signature[] | undefined; - for (let i = 0; i < signatureLists.length; i++) { - // Allow matching non-generic signatures to have excess parameters and different return types. - // Prefer matching this types if possible. - const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true); - if (!match) { - return undefined; - } - result = appendIfUnique(result, match); - } - return result; - } - - // The signatures of a union type are those signatures that are present in each of the constituent types. - // Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional - // parameters and may differ in return types. When signatures differ in return types, the resulting return - // type is the union of the constituent return types. - function getUnionSignatures(signatureLists: readonly (readonly Signature[])[]): Signature[] { - let result: Signature[] | undefined; - let indexWithLengthOverOne: number | undefined; - for (let i = 0; i < signatureLists.length; i++) { - if (signatureLists[i].length === 0) return emptyArray; - if (signatureLists[i].length > 1) { - indexWithLengthOverOne = indexWithLengthOverOne === undefined ? i : -1; // -1 is a signal there are multiple overload sets - } - for (const signature of signatureLists[i]) { - // Only process signatures with parameter lists that aren't already in the result list - if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true)) { - const unionSignatures = findMatchingSignatures(signatureLists, signature, i); - if (unionSignatures) { - let s = signature; - // Union the result types when more than one signature matches - if (unionSignatures.length > 1) { - let thisParameter = signature.thisParameter; - const firstThisParameterOfUnionSignatures = forEach(unionSignatures, sig => sig.thisParameter); - if (firstThisParameterOfUnionSignatures) { - const thisType = getIntersectionType(mapDefined(unionSignatures, sig => sig.thisParameter && getTypeOfSymbol(sig.thisParameter))); - thisParameter = createSymbolWithType(firstThisParameterOfUnionSignatures, thisType); - } - s = createUnionSignature(signature, unionSignatures); - s.thisParameter = thisParameter; - } - (result || (result = [])).push(s); - } - } - } - } - if (!length(result) && indexWithLengthOverOne !== -1) { - // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single - // signature that handles all over them. We only do this when there are overloads in only one constituent. - // (Overloads are conditional in nature and having overloads in multiple constituents would necessitate making a power set of - // signatures from the type, whose ordering would be non-obvious) - const masterList = signatureLists[indexWithLengthOverOne !== undefined ? indexWithLengthOverOne : 0]; - let results: Signature[] | undefined = masterList.slice(); - for (const signatures of signatureLists) { - if (signatures !== masterList) { - const signature = signatures[0]; - Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); - results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); - if (!results) { - break; - } - } - } - result = results; - } - return result || emptyArray; - } - - function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined { - if (!left || !right) { - return left || right; - } - // A signature `this` type might be a read or a write position... It's very possible that it should be invariant - // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be - // permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures. - const thisType = getIntersectionType([getTypeOfSymbol(left), getTypeOfSymbol(right)]); - return createSymbolWithType(left, thisType); - } - - function combineUnionParameters(left: Signature, right: Signature) { - const leftCount = getParameterCount(left); - const rightCount = getParameterCount(right); - const longest = leftCount >= rightCount ? left : right; - const shorter = longest === left ? right : left; - const longestCount = longest === left ? leftCount : rightCount; - const eitherHasEffectiveRest = (hasEffectiveRestParameter(left) || hasEffectiveRestParameter(right)); - const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); - const params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); - for (let i = 0; i < longestCount; i++) { - const longestParamType = tryGetTypeAtPosition(longest, i)!; - const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; - const unionParamType = getIntersectionType([longestParamType, shorterParamType]); - const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); - const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter); - const leftName = i >= leftCount ? undefined : getParameterNameAtPosition(left, i); - const rightName = i >= rightCount ? undefined : getParameterNameAtPosition(right, i); - - const paramName = leftName === rightName ? leftName : - !leftName ? rightName : - !rightName ? leftName : - undefined; - const paramSymbol = createSymbol( - SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), - paramName || `arg${i}` as __String - ); - paramSymbol.type = isRestParam ? createArrayType(unionParamType) : unionParamType; - params[i] = paramSymbol; - } - if (needsExtraRestElement) { - const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); - restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount)); - params[longestCount] = restParamSymbol; - } - return params; - } - - function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature { - const declaration = left.declaration; - const params = combineUnionParameters(left, right); - const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter); - const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); - const result = createSignature( - declaration, - left.typeParameters || right.typeParameters, - thisParam, - params, - /*resolvedReturnType*/ undefined, - /*resolvedTypePredicate*/ undefined, - minArgCount, - (left.flags | right.flags) & SignatureFlags.PropagatingFlags - ); - result.unionSignatures = concatenate(left.unionSignatures || [left], [right]); - return result; - } - - function getUnionIndexInfo(types: readonly Type[], kind: IndexKind): IndexInfo | undefined { - const indexTypes: Type[] = []; - let isAnyReadonly = false; - for (const type of types) { - const indexInfo = getIndexInfoOfType(type, kind); - if (!indexInfo) { - return undefined; - } - indexTypes.push(indexInfo.type); - isAnyReadonly = isAnyReadonly || indexInfo.isReadonly; - } - return createIndexInfo(getUnionType(indexTypes, UnionReduction.Subtype), isAnyReadonly); - } - - function resolveUnionTypeMembers(type: UnionType) { - // The members and properties collections are empty for union types. To get all properties of a union - // type use getPropertiesOfType (only the language service uses this). - const callSignatures = getUnionSignatures(map(type.types, t => t === globalFunctionType ? [unknownSignature] : getSignaturesOfType(t, SignatureKind.Call))); - const constructSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Construct))); - const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String); - const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number); - setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); - } - - function intersectTypes(type1: Type, type2: Type): Type; - function intersectTypes(type1: Type | undefined, type2: Type | undefined): Type | undefined; - function intersectTypes(type1: Type | undefined, type2: Type | undefined): Type | undefined { - return !type1 ? type2 : !type2 ? type1 : getIntersectionType([type1, type2]); - } - - function intersectIndexInfos(info1: IndexInfo | undefined, info2: IndexInfo | undefined): IndexInfo | undefined { - return !info1 ? info2 : !info2 ? info1 : createIndexInfo( - getIntersectionType([info1.type, info2.type]), info1.isReadonly && info2.isReadonly); - } - - function unionSpreadIndexInfos(info1: IndexInfo | undefined, info2: IndexInfo | undefined): IndexInfo | undefined { - return info1 && info2 && createIndexInfo( - getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly); - } - - function findMixins(types: readonly Type[]): readonly boolean[] { - const constructorTypeCount = countWhere(types, (t) => getSignaturesOfType(t, SignatureKind.Construct).length > 0); - const mixinFlags = map(types, isMixinConstructorType); - if (constructorTypeCount > 0 && constructorTypeCount === countWhere(mixinFlags, (b) => b)) { - const firstMixinIndex = mixinFlags.indexOf(/*searchElement*/ true); - mixinFlags[firstMixinIndex] = false; - } - return mixinFlags; - } - - function includeMixinType(type: Type, types: readonly Type[], mixinFlags: readonly boolean[], index: number): Type { - const mixedTypes: Type[] = []; - for (let i = 0; i < types.length; i++) { - if (i === index) { - mixedTypes.push(type); - } - else if (mixinFlags[i]) { - mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0])); - } - } - return getIntersectionType(mixedTypes); - } - - function resolveIntersectionTypeMembers(type: IntersectionType) { - // The members and properties collections are empty for intersection types. To get all properties of an - // intersection type use getPropertiesOfType (only the language service uses this). - let callSignatures: Signature[] | undefined; - let constructSignatures: Signature[] | undefined; - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - const types = type.types; - const mixinFlags = findMixins(types); - const mixinCount = countWhere(mixinFlags, (b) => b); - for (let i = 0; i < types.length; i++) { - const t = type.types[i]; - // When an intersection type contains mixin constructor types, the construct signatures from - // those types are discarded and their return types are mixed into the return types of all - // other construct signatures in the intersection type. For example, the intersection type - // '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature - // 'new(s: string) => A & B'. - if (!mixinFlags[i]) { - let signatures = getSignaturesOfType(t, SignatureKind.Construct); - if (signatures.length && mixinCount > 0) { - signatures = map(signatures, s => { - const clone = cloneSignature(s); - clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, mixinFlags, i); - return clone; - }); - } - constructSignatures = appendSignatures(constructSignatures, signatures); - } - callSignatures = appendSignatures(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); - stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); - numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); - } - setStructuredTypeMembers(type, emptySymbols, callSignatures || emptyArray, constructSignatures || emptyArray, stringIndexInfo, numberIndexInfo); - } - - function appendSignatures(signatures: Signature[] | undefined, newSignatures: readonly Signature[]) { - for (const sig of newSignatures) { - if (!signatures || every(signatures, s => !compareSignaturesIdentical(s, sig, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, compareTypesIdentical))) { - signatures = append(signatures, sig); - } - } - return signatures; - } - - /** - * Converts an AnonymousType to a ResolvedType. - */ - function resolveAnonymousTypeMembers(type: AnonymousType) { - const symbol = getMergedSymbol(type.symbol); - if (type.target) { - setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper!, /*mappingThisOnly*/ false); - const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper!); - const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper!); - const stringIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.String), type.mapper!); - const numberIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.Number), type.mapper!); - setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); - } - else if (symbol.flags & SymbolFlags.TypeLiteral) { - setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const members = getMembersOfSymbol(symbol); - const callSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); - const constructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New)); - const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); - const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); - setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); - } - else { - // Combinations of function, class, enum and module - let members = emptySymbols; - let stringIndexInfo: IndexInfo | undefined; - if (symbol.exports) { - members = getExportsOfSymbol(symbol); - if (symbol === globalThisSymbol) { - const varsOnly = createMap() as SymbolTable; - members.forEach(p => { - if (!(p.flags & SymbolFlags.BlockScoped)) { - varsOnly.set(p.escapedName, p); - } - }); - members = varsOnly; - } - } - setStructuredTypeMembers(type, members, emptyArray, emptyArray, undefined, undefined); - if (symbol.flags & SymbolFlags.Class) { - const classType = getDeclaredTypeOfClassOrInterface(symbol); - const baseConstructorType = getBaseConstructorTypeOfClass(classType); - if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) { - members = createSymbolTable(getNamedMembers(members)); - addInheritedMembers(members, getPropertiesOfType(baseConstructorType)); - } - else if (baseConstructorType === anyType) { - stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false); - } - } - const numberIndexInfo = symbol.flags & SymbolFlags.Enum && (getDeclaredTypeOfSymbol(symbol).flags & TypeFlags.Enum || - some(type.properties, prop => !!(getTypeOfSymbol(prop).flags & TypeFlags.NumberLike))) ? enumNumberIndexInfo : undefined; - setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); - // We resolve the members before computing the signatures because a signature may use - // typeof with a qualified name expression that circularly references the type we are - // in the process of resolving (see issue #6072). The temporarily empty signature list - // will never be observed because a qualified name can't reference signatures. - if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) { - type.callSignatures = getSignaturesOfSymbol(symbol); - } - // And likewise for construct signatures for classes - if (symbol.flags & SymbolFlags.Class) { - const classType = getDeclaredTypeOfClassOrInterface(symbol); - let constructSignatures = symbol.members ? getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Constructor)) : emptyArray; - if (symbol.flags & SymbolFlags.Function) { - constructSignatures = addRange(constructSignatures.slice(), mapDefined( - type.callSignatures, - sig => isJSConstructor(sig.declaration) ? - createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, classType, /*resolvedTypePredicate*/ undefined, sig.minArgumentCount, sig.flags & SignatureFlags.PropagatingFlags) : - undefined)); - } - if (!constructSignatures.length) { - constructSignatures = getDefaultConstructSignatures(classType); - } - type.constructSignatures = constructSignatures; - } - } - } - - function resolveReverseMappedTypeMembers(type: ReverseMappedType) { - const indexInfo = getIndexInfoOfType(type.source, IndexKind.String); - const modifiers = getMappedTypeModifiers(type.mappedType); - const readonlyMask = modifiers & MappedTypeModifiers.IncludeReadonly ? false : true; - const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional; - const stringIndexInfo = indexInfo && createIndexInfo(inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType), readonlyMask && indexInfo.isReadonly); - const members = createSymbolTable(); - for (const prop of getPropertiesOfType(type.source)) { - const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); - const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; - inferredProp.declarations = prop.declarations; - inferredProp.nameType = prop.nameType; - inferredProp.propertyType = getTypeOfSymbol(prop); - inferredProp.mappedType = type.mappedType; - inferredProp.constraintType = type.constraintType; - members.set(prop.escapedName, inferredProp); - } - setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); - } - - // Return the lower bound of the key type in a mapped type. Intuitively, the lower - // bound includes those keys that are known to always be present, for example because - // because of constraints on type parameters (e.g. 'keyof T' for a constrained T). - function getLowerBoundOfKeyType(type: Type): Type { - if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) { - return type; - } - if (type.flags & TypeFlags.Index) { - return getIndexType(getApparentType((type).type)); - } - if (type.flags & TypeFlags.Conditional) { - if ((type).root.isDistributive) { - const checkType = (type).checkType; - const constraint = getLowerBoundOfKeyType(checkType); - if (constraint !== checkType) { - const mapper = makeUnaryTypeMapper((type).root.checkType, constraint); - return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, (type).mapper)); - } - } - return type; - } - if (type.flags & TypeFlags.Union) { - return getUnionType(sameMap((type).types, getLowerBoundOfKeyType)); - } - if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(sameMap((type).types, getLowerBoundOfKeyType)); - } - return neverType; - } - - /** Resolve the members of a mapped type { [P in K]: T } */ - function resolveMappedTypeMembers(type: MappedType) { - const members: SymbolTable = createSymbolTable(); - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - // Resolve upfront such that recursive references see an empty object type. - setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); - // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, - // and T as the template type. - const typeParameter = getTypeParameterFromMappedType(type); - const constraintType = getConstraintTypeFromMappedType(type); - const templateType = getTemplateTypeFromMappedType(type.target || type); - const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' - const templateModifiers = getMappedTypeModifiers(type); - const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique; - if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - // We have a { [P in keyof T]: X } - for (const prop of getPropertiesOfType(modifiersType)) { - addMemberForKeyType(getLiteralTypeFromProperty(prop, include)); - } - if (modifiersType.flags & TypeFlags.Any || getIndexInfoOfType(modifiersType, IndexKind.String)) { - addMemberForKeyType(stringType); - } - if (!keyofStringsOnly && getIndexInfoOfType(modifiersType, IndexKind.Number)) { - addMemberForKeyType(numberType); - } - } - else { - forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType); - } - setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); - - function addMemberForKeyType(t: Type) { - // Create a mapper from T to the current iteration type constituent. Then, if the - // mapped type is itself an instantiated type, combine the iteration mapper with the - // instantiation mapper. - const templateMapper = combineTypeMappers(type.mapper, createTypeMapper([typeParameter], [t])); - const propType = instantiateType(templateType, templateMapper); - // If the current iteration type constituent is a string literal type, create a property. - // Otherwise, for type string create a string index signature. - if (isTypeUsableAsPropertyName(t)) { - const propName = getPropertyNameFromType(t); - const modifiersProp = getPropertyOfType(modifiersType, propName); - const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional || - !(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional); - const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly || - !(templateModifiers & MappedTypeModifiers.ExcludeReadonly) && modifiersProp && isReadonlySymbol(modifiersProp)); - const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, isReadonly ? CheckFlags.Readonly : 0); - // When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the - // type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks - // mode, if the underlying property is optional we remove 'undefined' from the type. - prop.type = strictNullChecks && isOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) : - strictNullChecks && !isOptional && modifiersProp && modifiersProp.flags & SymbolFlags.Optional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) : - propType; - if (modifiersProp) { - prop.syntheticOrigin = modifiersProp; - prop.declarations = modifiersProp.declarations; - } - prop.nameType = t; - members.set(propName, prop); - } - else if (t.flags & (TypeFlags.Any | TypeFlags.String)) { - stringIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly)); - } - else if (t.flags & (TypeFlags.Number | TypeFlags.Enum)) { - numberIndexInfo = createIndexInfo(numberIndexInfo ? getUnionType([numberIndexInfo.type, propType]) : propType, - !!(templateModifiers & MappedTypeModifiers.IncludeReadonly)); - } - } - } - - function getTypeParameterFromMappedType(type: MappedType) { - return type.typeParameter || - (type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(type.declaration.typeParameter))); - } - - function getConstraintTypeFromMappedType(type: MappedType) { - return type.constraintType || - (type.constraintType = getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)) || errorType); - } - - function getTemplateTypeFromMappedType(type: MappedType) { - return type.templateType || - (type.templateType = type.declaration.type ? - instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), type.mapper || identityMapper) : - errorType); - } - - function getConstraintDeclarationForMappedType(type: MappedType) { - return getEffectiveConstraintOfTypeParameter(type.declaration.typeParameter); - } - - function isMappedTypeWithKeyofConstraintDeclaration(type: MappedType) { - const constraintDeclaration = getConstraintDeclarationForMappedType(type)!; // TODO: GH#18217 - return constraintDeclaration.kind === SyntaxKind.TypeOperator && - (constraintDeclaration).operator === SyntaxKind.KeyOfKeyword; - } - - function getModifiersTypeFromMappedType(type: MappedType) { - if (!type.modifiersType) { - if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check - // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves - // 'keyof T' to a literal union type and we can't recover T from that type. - type.modifiersType = instantiateType(getTypeFromTypeNode((getConstraintDeclarationForMappedType(type)).type), type.mapper || identityMapper); - } - else { - // Otherwise, get the declared constraint type, and if the constraint type is a type parameter, - // get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T', - // the modifiers type is T. Otherwise, the modifiers type is unknown. - const declaredType = getTypeFromMappedTypeNode(type.declaration); - const constraint = getConstraintTypeFromMappedType(declaredType); - const extendedConstraint = constraint && constraint.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(constraint) : constraint; - type.modifiersType = extendedConstraint && extendedConstraint.flags & TypeFlags.Index ? instantiateType((extendedConstraint).type, type.mapper || identityMapper) : unknownType; - } - } - return type.modifiersType; - } - - function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers { - const declaration = type.declaration; - return (declaration.readonlyToken ? declaration.readonlyToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeReadonly : MappedTypeModifiers.IncludeReadonly : 0) | - (declaration.questionToken ? declaration.questionToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeOptional : MappedTypeModifiers.IncludeOptional : 0); - } - - function getMappedTypeOptionality(type: MappedType): number { - const modifiers = getMappedTypeModifiers(type); - return modifiers & MappedTypeModifiers.ExcludeOptional ? -1 : modifiers & MappedTypeModifiers.IncludeOptional ? 1 : 0; - } - - function getCombinedMappedTypeOptionality(type: MappedType): number { - const optionality = getMappedTypeOptionality(type); - const modifiersType = getModifiersTypeFromMappedType(type); - return optionality || (isGenericMappedType(modifiersType) ? getMappedTypeOptionality(modifiersType) : 0); - } - - function isPartialMappedType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.Mapped && getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional); - } - - function isGenericMappedType(type: Type): type is MappedType { - return !!(getObjectFlags(type) & ObjectFlags.Mapped) && isGenericIndexType(getConstraintTypeFromMappedType(type)); - } - - function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { - if (!(type).members) { - if (type.flags & TypeFlags.Object) { - if ((type).objectFlags & ObjectFlags.Reference) { - resolveTypeReferenceMembers(type); - } - else if ((type).objectFlags & ObjectFlags.ClassOrInterface) { - resolveClassOrInterfaceMembers(type); - } - else if ((type).objectFlags & ObjectFlags.ReverseMapped) { - resolveReverseMappedTypeMembers(type as ReverseMappedType); - } - else if ((type).objectFlags & ObjectFlags.Anonymous) { - resolveAnonymousTypeMembers(type); - } - else if ((type).objectFlags & ObjectFlags.Mapped) { - resolveMappedTypeMembers(type); - } - } - else if (type.flags & TypeFlags.Union) { - resolveUnionTypeMembers(type); - } - else if (type.flags & TypeFlags.Intersection) { - resolveIntersectionTypeMembers(type); - } - } - return type; - } - - /** Return properties of an object type or an empty array for other types */ - function getPropertiesOfObjectType(type: Type): Symbol[] { - if (type.flags & TypeFlags.Object) { - return resolveStructuredTypeMembers(type).properties; - } - return emptyArray; - } - - /** If the given type is an object type and that type has a property by the given name, - * return the symbol for that property. Otherwise return undefined. - */ - function getPropertyOfObjectType(type: Type, name: __String): Symbol | undefined { - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members.get(name); - if (symbol && symbolIsValue(symbol)) { - return symbol; - } - } - } - - function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { - if (!type.resolvedProperties) { - const members = createSymbolTable(); - for (const current of type.types) { - for (const prop of getPropertiesOfType(current)) { - if (!members.has(prop.escapedName)) { - const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); - if (combinedProp) { - members.set(prop.escapedName, combinedProp); - } - } - } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type - if (type.flags & TypeFlags.Union) { - break; - } - } - type.resolvedProperties = getNamedMembers(members); - } - return type.resolvedProperties; - } - - function getPropertiesOfType(type: Type): Symbol[] { - type = getApparentType(type); - return type.flags & TypeFlags.UnionOrIntersection ? - getPropertiesOfUnionOrIntersectionType(type) : - getPropertiesOfObjectType(type); - } - - function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean { - const list = obj.properties as NodeArray; - return list.some(property => { - const nameType = property.name && getLiteralTypeFromPropertyName(property.name); - const name = nameType && isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined; - const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name); - return !!expected && isLiteralType(expected) && !isTypeAssignableTo(getTypeOfNode(property), expected); - }); - } - - function getAllPossiblePropertiesOfTypes(types: readonly Type[]): Symbol[] { - const unionType = getUnionType(types); - if (!(unionType.flags & TypeFlags.Union)) { - return getAugmentedPropertiesOfType(unionType); - } - - const props = createSymbolTable(); - for (const memberType of types) { - for (const { escapedName } of getAugmentedPropertiesOfType(memberType)) { - if (!props.has(escapedName)) { - const prop = createUnionOrIntersectionProperty(unionType as UnionType, escapedName); - // May be undefined if the property is private - if (prop) props.set(escapedName, prop); - } - } - } - return arrayFrom(props.values()); - } - - function getConstraintOfType(type: InstantiableType | UnionOrIntersectionType): Type | undefined { - return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(type) : - type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(type) : - type.flags & TypeFlags.Conditional ? getConstraintOfConditionalType(type) : - getBaseConstraintOfType(type); - } - - function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type | undefined { - return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined; - } - - function getConstraintOfIndexedAccess(type: IndexedAccessType) { - return hasNonCircularBaseConstraint(type) ? getConstraintFromIndexedAccess(type) : undefined; - } - - function getSimplifiedTypeOrConstraint(type: Type) { - const simplified = getSimplifiedType(type, /*writing*/ false); - return simplified !== type ? simplified : getConstraintOfType(type); - } - - function getConstraintFromIndexedAccess(type: IndexedAccessType) { - const indexConstraint = getSimplifiedTypeOrConstraint(type.indexType); - if (indexConstraint && indexConstraint !== type.indexType) { - const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint); - if (indexedAccess) { - return indexedAccess; - } - } - const objectConstraint = getSimplifiedTypeOrConstraint(type.objectType); - if (objectConstraint && objectConstraint !== type.objectType) { - return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType); - } - return undefined; - } - - function getDefaultConstraintOfConditionalType(type: ConditionalType) { - if (!type.resolvedDefaultConstraint) { - // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, - // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to - // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, - // in effect treating `any` like `never` rather than `unknown` in this location. - const trueConstraint = getInferredTrueTypeFromConditionalType(type); - const falseConstraint = getFalseTypeFromConditionalType(type); - type.resolvedDefaultConstraint = isTypeAny(trueConstraint) ? falseConstraint : isTypeAny(falseConstraint) ? trueConstraint : getUnionType([trueConstraint, falseConstraint]); - } - return type.resolvedDefaultConstraint; - } - - function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type | undefined { - // Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained - // type parameter. If so, create an instantiation of the conditional type where T is replaced - // with its constraint. We do this because if the constraint is a union type it will be distributed - // over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T' - // removes 'undefined' from T. - // We skip returning a distributive constraint for a restrictive instantiation of a conditional type - // as the constraint for all type params (check type included) have been replace with `unknown`, which - // is going to produce even more false positive/negative results than the distribute constraint already does. - // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter - // a union - once negated types exist and are applied to the conditional false branch, this "constraint" - // likely doesn't need to exist. - if (type.root.isDistributive && type.restrictiveInstantiation !== type) { - const simplified = getSimplifiedType(type.checkType, /*writing*/ false); - const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified; - if (constraint && constraint !== type.checkType) { - const mapper = makeUnaryTypeMapper(type.root.checkType, constraint); - const instantiated = getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper)); - if (!(instantiated.flags & TypeFlags.Never)) { - return instantiated; - } - } - } - return undefined; - } - - function getConstraintFromConditionalType(type: ConditionalType) { - return getConstraintOfDistributiveConditionalType(type) || getDefaultConstraintOfConditionalType(type); - } - - function getConstraintOfConditionalType(type: ConditionalType) { - return hasNonCircularBaseConstraint(type) ? getConstraintFromConditionalType(type) : undefined; - } - - function getEffectiveConstraintOfIntersection(types: readonly Type[], targetIsUnion: boolean) { - let constraints: Type[] | undefined; - let hasDisjointDomainType = false; - for (const t of types) { - if (t.flags & TypeFlags.Instantiable) { - // We keep following constraints as long as we have an instantiable type that is known - // not to be circular or infinite (hence we stop on index access types). - let constraint = getConstraintOfType(t); - while (constraint && constraint.flags & (TypeFlags.TypeParameter | TypeFlags.Index | TypeFlags.Conditional)) { - constraint = getConstraintOfType(constraint); - } - if (constraint) { - constraints = append(constraints, constraint); - if (targetIsUnion) { - constraints = append(constraints, t); - } - } - } - else if (t.flags & TypeFlags.DisjointDomains) { - hasDisjointDomainType = true; - } - } - // If the target is a union type or if we are intersecting with types belonging to one of the - // disjoint domains, we may end up producing a constraint that hasn't been examined before. - if (constraints && (targetIsUnion || hasDisjointDomainType)) { - if (hasDisjointDomainType) { - // We add any types belong to one of the disjoint domains because they might cause the final - // intersection operation to reduce the union constraints. - for (const t of types) { - if (t.flags & TypeFlags.DisjointDomains) { - constraints = append(constraints, t); - } - } - } - return getIntersectionType(constraints); - } - return undefined; - } - - function getBaseConstraintOfType(type: Type): Type | undefined { - if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection)) { - const constraint = getResolvedBaseConstraint(type); - return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined; - } - return type.flags & TypeFlags.Index ? keyofConstraintType : undefined; - } - - /** - * This is similar to `getBaseConstraintOfType` except it returns the input type if there's no base constraint, instead of `undefined` - * It also doesn't map indexes to `string`, as where this is used this would be unneeded (and likely undesirable) - */ - function getBaseConstraintOrType(type: Type) { - return getBaseConstraintOfType(type) || type; - } - - function hasNonCircularBaseConstraint(type: InstantiableType): boolean { - return getResolvedBaseConstraint(type) !== circularConstraintType; - } - - /** - * Return the resolved base constraint of a type variable. The noConstraintType singleton is returned if the - * type variable has no constraint, and the circularConstraintType singleton is returned if the constraint - * circularly references the type variable. - */ - function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type { - let nonTerminating = false; - return type.resolvedBaseConstraint || - (type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type)); - - function getImmediateBaseConstraint(t: Type): Type { - if (!t.immediateBaseConstraint) { - if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) { - return circularConstraintType; - } - if (constraintDepth >= 50) { - // We have reached 50 recursive invocations of getImmediateBaseConstraint and there is a - // very high likelihood we're dealing with an infinite generic type that perpetually generates - // new type identities as we descend into it. We stop the recursion here and mark this type - // and the outer types as having circular constraints. - error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); - nonTerminating = true; - return t.immediateBaseConstraint = noConstraintType; - } - constraintDepth++; - let result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); - constraintDepth--; - if (!popTypeResolution()) { - if (t.flags & TypeFlags.TypeParameter) { - const errorNode = getConstraintDeclaration(t); - if (errorNode) { - const diagnostic = error(errorNode, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(t)); - if (currentNode && !isNodeDescendantOf(errorNode, currentNode) && !isNodeDescendantOf(currentNode, errorNode)) { - addRelatedInfo(diagnostic, createDiagnosticForNode(currentNode, Diagnostics.Circularity_originates_in_type_at_this_location)); - } - } - } - result = circularConstraintType; - } - if (nonTerminating) { - result = circularConstraintType; - } - t.immediateBaseConstraint = result || noConstraintType; - } - return t.immediateBaseConstraint; - } - - function getBaseConstraint(t: Type): Type | undefined { - const c = getImmediateBaseConstraint(t); - return c !== noConstraintType && c !== circularConstraintType ? c : undefined; - } - - function computeBaseConstraint(t: Type): Type | undefined { - if (t.flags & TypeFlags.TypeParameter) { - const constraint = getConstraintFromTypeParameter(t); - return (t as TypeParameter).isThisType || !constraint ? - constraint : - getBaseConstraint(constraint); - } - if (t.flags & TypeFlags.UnionOrIntersection) { - const types = (t).types; - const baseTypes: Type[] = []; - for (const type of types) { - const baseType = getBaseConstraint(type); - if (baseType) { - baseTypes.push(baseType); - } - } - return t.flags & TypeFlags.Union && baseTypes.length === types.length ? getUnionType(baseTypes) : - t.flags & TypeFlags.Intersection && baseTypes.length ? getIntersectionType(baseTypes) : - undefined; - } - if (t.flags & TypeFlags.Index) { - return keyofConstraintType; - } - if (t.flags & TypeFlags.IndexedAccess) { - const baseObjectType = getBaseConstraint((t).objectType); - const baseIndexType = getBaseConstraint((t).indexType); - const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType); - return baseIndexedAccess && getBaseConstraint(baseIndexedAccess); - } - if (t.flags & TypeFlags.Conditional) { - const constraint = getConstraintFromConditionalType(t); - constraintDepth++; // Penalize repeating conditional types (this captures the recursion within getConstraintFromConditionalType and carries it forward) - const result = constraint && getBaseConstraint(constraint); - constraintDepth--; - return result; - } - if (t.flags & TypeFlags.Substitution) { - return getBaseConstraint((t).substitute); - } - return t; - } - } - - function getApparentTypeOfIntersectionType(type: IntersectionType) { - return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type, /*apparentType*/ true)); - } - - function getResolvedTypeParameterDefault(typeParameter: TypeParameter): Type | undefined { - if (!typeParameter.default) { - if (typeParameter.target) { - const targetDefault = getResolvedTypeParameterDefault(typeParameter.target); - typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType; - } - else { - // To block recursion, set the initial value to the resolvingDefaultType. - typeParameter.default = resolvingDefaultType; - const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default); - const defaultType = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType; - if (typeParameter.default === resolvingDefaultType) { - // If we have not been called recursively, set the correct default type. - typeParameter.default = defaultType; - } - } - } - else if (typeParameter.default === resolvingDefaultType) { - // If we are called recursively for this type parameter, mark the default as circular. - typeParameter.default = circularConstraintType; - } - return typeParameter.default; - } - - /** - * Gets the default type for a type parameter. - * - * If the type parameter is the result of an instantiation, this gets the instantiated - * default type of its target. If the type parameter has no default type or the default is - * circular, `undefined` is returned. - */ - function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined { - const defaultType = getResolvedTypeParameterDefault(typeParameter); - return defaultType !== noConstraintType && defaultType !== circularConstraintType ? defaultType : undefined; - } - - function hasNonCircularTypeParameterDefault(typeParameter: TypeParameter) { - return getResolvedTypeParameterDefault(typeParameter) !== circularConstraintType; - } - - /** - * Indicates whether the declaration of a typeParameter has a default type. - */ - function hasTypeParameterDefault(typeParameter: TypeParameter): boolean { - return !!(typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default)); - } - - function getApparentTypeOfMappedType(type: MappedType) { - return type.resolvedApparentType || (type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type)); - } - - function getResolvedApparentTypeOfMappedType(type: MappedType) { - const typeVariable = getHomomorphicTypeVariable(type); - if (typeVariable) { - const constraint = getConstraintOfTypeParameter(typeVariable); - if (constraint && (isArrayType(constraint) || isTupleType(constraint))) { - const mapper = makeUnaryTypeMapper(typeVariable, constraint); - return instantiateType(type, combineTypeMappers(mapper, type.mapper)); - } - } - return type; - } - - /** - * For a type parameter, return the base constraint of the type parameter. For the string, number, - * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the - * type itself. Note that the apparent type of a union type is the union type itself. - */ - function getApparentType(type: Type): Type { - const t = type.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(type) || unknownType : type; - return getObjectFlags(t) & ObjectFlags.Mapped ? getApparentTypeOfMappedType(t) : - t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t) : - t.flags & TypeFlags.StringLike ? globalStringType : - t.flags & TypeFlags.NumberLike ? globalNumberType : - t.flags & TypeFlags.BigIntLike ? getGlobalBigIntType(/*reportErrors*/ languageVersion >= ScriptTarget.ESNext) : - t.flags & TypeFlags.BooleanLike ? globalBooleanType : - t.flags & TypeFlags.ESSymbolLike ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) : - t.flags & TypeFlags.NonPrimitive ? emptyObjectType : - t.flags & TypeFlags.Index ? keyofConstraintType : - t.flags & TypeFlags.Unknown && !strictNullChecks ? emptyObjectType : - t; - } - - function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined { - const propSet = createMap(); - let indexTypes: Type[] | undefined; - const isUnion = containingType.flags & TypeFlags.Union; - const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; - // Flags we want to propagate to the result if they exist in all source symbols - let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional; - let syntheticFlag = CheckFlags.SyntheticMethod; - let checkFlags = 0; - for (const current of containingType.types) { - const type = getApparentType(current); - if (type !== errorType) { - const prop = getPropertyOfType(type, name); - const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; - if (prop && !(modifiers & excludeModifiers)) { - if (isUnion) { - optionalFlag |= (prop.flags & SymbolFlags.Optional); - } - else { - optionalFlag &= prop.flags; - } - const id = "" + getSymbolId(prop); - if (!propSet.has(id)) { - propSet.set(id, prop); - } - checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) | - (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) | - (modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) | - (modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) | - (modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0); - if (!isPrototypeProperty(prop)) { - syntheticFlag = CheckFlags.SyntheticProperty; - } - } - else if (isUnion) { - const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String)); - if (indexInfo) { - checkFlags |= CheckFlags.WritePartial | (indexInfo.isReadonly ? CheckFlags.Readonly : 0); - indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type); - } - else if (isObjectLiteralType(type)) { - checkFlags |= CheckFlags.WritePartial; - indexTypes = append(indexTypes, undefinedType); - } - else { - checkFlags |= CheckFlags.ReadPartial; - } - } - } - } - if (!propSet.size) { - return undefined; - } - const props = arrayFrom(propSet.values()); - if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) { - return props[0]; - } - let declarations: Declaration[] | undefined; - let firstType: Type | undefined; - let nameType: Type | undefined; - const propTypes: Type[] = []; - let firstValueDeclaration: Declaration | undefined; - let hasNonUniformValueDeclaration = false; - for (const prop of props) { - if (!firstValueDeclaration) { - firstValueDeclaration = prop.valueDeclaration; - } - else if (prop.valueDeclaration !== firstValueDeclaration) { - hasNonUniformValueDeclaration = true; - } - declarations = addRange(declarations, prop.declarations); - const type = getTypeOfSymbol(prop); - if (!firstType) { - firstType = type; - nameType = prop.nameType; - } - else if (type !== firstType) { - checkFlags |= CheckFlags.HasNonUniformType; - } - if (isLiteralType(type)) { - checkFlags |= CheckFlags.HasLiteralType; - } - propTypes.push(type); - } - addRange(propTypes, indexTypes); - const result = createSymbol(SymbolFlags.Property | optionalFlag, name, syntheticFlag | checkFlags); - result.containingType = containingType; - if (!hasNonUniformValueDeclaration && firstValueDeclaration) { - result.valueDeclaration = firstValueDeclaration; - - // Inherit information about parent type. - if (firstValueDeclaration.symbol.parent) { - result.parent = firstValueDeclaration.symbol.parent; - } - } - - result.declarations = declarations!; - result.nameType = nameType; - if (propTypes.length > 2) { - // When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed - result.checkFlags |= CheckFlags.DeferredType; - result.deferralParent = containingType; - result.deferralConstituents = propTypes; - } - else { - result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); - } - return result; - } - - // Return the symbol for a given property in a union or intersection type, or undefined if the property - // does not exist in any constituent type. Note that the returned property may only be present in some - // constituents, in which case the isPartial flag is set when the containing type is union type. We need - // these partial properties when identifying discriminant properties, but otherwise they are filtered out - // and do not appear to be present in the union type. - function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String): Symbol | undefined { - const properties = type.propertyCache || (type.propertyCache = createSymbolTable()); - let property = properties.get(name); - if (!property) { - property = createUnionOrIntersectionProperty(type, name); - if (property) { - properties.set(name, property); - } - } - return property; - } - - function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined { - const property = getUnionOrIntersectionProperty(type, name); - // We need to filter out partial properties in union types - return property && !(getCheckFlags(property) & CheckFlags.ReadPartial) ? property : undefined; - } - - /** - * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - * Object and Function as appropriate. - * - * @param type a type to look up property from - * @param name a name of property to look up in a given type - */ - function getPropertyOfType(type: Type, name: __String): Symbol | undefined { - type = getApparentType(type); - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members.get(name); - if (symbol && symbolIsValue(symbol)) { - return symbol; - } - const functionType = resolved === anyFunctionType ? globalFunctionType : - resolved.callSignatures.length ? globalCallableFunctionType : - resolved.constructSignatures.length ? globalNewableFunctionType : - undefined; - if (functionType) { - const symbol = getPropertyOfObjectType(functionType, name); - if (symbol) { - return symbol; - } - } - return getPropertyOfObjectType(globalObjectType, name); - } - if (type.flags & TypeFlags.UnionOrIntersection) { - return getPropertyOfUnionOrIntersectionType(type, name); - } - return undefined; - } - - function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): readonly Signature[] { - if (type.flags & TypeFlags.StructuredType) { - const resolved = resolveStructuredTypeMembers(type); - return kind === SignatureKind.Call ? resolved.callSignatures : resolved.constructSignatures; - } - return emptyArray; - } - - /** - * Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and - * maps primitive types and type parameters are to their apparent types. - */ - function getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[] { - return getSignaturesOfStructuredType(getApparentType(type), kind); - } - - function getIndexInfoOfStructuredType(type: Type, kind: IndexKind): IndexInfo | undefined { - if (type.flags & TypeFlags.StructuredType) { - const resolved = resolveStructuredTypeMembers(type); - return kind === IndexKind.String ? resolved.stringIndexInfo : resolved.numberIndexInfo; - } - } - - function getIndexTypeOfStructuredType(type: Type, kind: IndexKind): Type | undefined { - const info = getIndexInfoOfStructuredType(type, kind); - return info && info.type; - } - - // Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and - // maps primitive types and type parameters are to their apparent types. - function getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined { - return getIndexInfoOfStructuredType(getApparentType(type), kind); - } - - // Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and - // maps primitive types and type parameters are to their apparent types. - function getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined { - return getIndexTypeOfStructuredType(getApparentType(type), kind); - } - - function getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined { - if (isObjectTypeWithInferableIndex(type)) { - const propTypes: Type[] = []; - for (const prop of getPropertiesOfType(type)) { - if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) { - propTypes.push(getTypeOfSymbol(prop)); - } - } - if (kind === IndexKind.String) { - append(propTypes, getIndexTypeOfType(type, IndexKind.Number)); - } - if (propTypes.length) { - return getUnionType(propTypes, UnionReduction.Subtype); - } - } - return undefined; - } - - // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual - // type checking functions). - function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] | undefined { - let result: TypeParameter[] | undefined; - for (const node of getEffectiveTypeParameterDeclarations(declaration)) { - result = appendIfUnique(result, getDeclaredTypeOfTypeParameter(node.symbol)); - } - return result; - } - - function symbolsToArray(symbols: SymbolTable): Symbol[] { - const result: Symbol[] = []; - symbols.forEach((symbol, id) => { - if (!isReservedMemberName(id)) { - result.push(symbol); - } - }); - return result; - } - - function isJSDocOptionalParameter(node: ParameterDeclaration) { - return isInJSFile(node) && ( - // node.type should only be a JSDocOptionalType when node is a parameter of a JSDocFunctionType - node.type && node.type.kind === SyntaxKind.JSDocOptionalType - || getJSDocParameterTags(node).some(({ isBracketed, typeExpression }) => - isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType)); - } - - function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) { - if (isExternalModuleNameRelative(moduleName)) { - return undefined; - } - const symbol = getSymbol(globals, '"' + moduleName + '"' as __String, SymbolFlags.ValueModule); - // merged symbol is module declaration symbol combined with all augmentations - return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol; - } - - function isOptionalParameter(node: ParameterDeclaration | JSDocParameterTag) { - if (hasQuestionToken(node) || isOptionalJSDocParameterTag(node) || isJSDocOptionalParameter(node)) { - return true; - } - - if (node.initializer) { - const signature = getSignatureFromDeclaration(node.parent); - const parameterIndex = node.parent.parameters.indexOf(node); - Debug.assert(parameterIndex >= 0); - return parameterIndex >= getMinArgumentCount(signature); - } - const iife = getImmediatelyInvokedFunctionExpression(node.parent); - if (iife) { - return !node.type && - !node.dotDotDotToken && - node.parent.parameters.indexOf(node) >= iife.arguments.length; - } - - return false; - } - - function isOptionalJSDocParameterTag(node: Node): node is JSDocParameterTag { - if (!isJSDocParameterTag(node)) { - return false; - } - const { isBracketed, typeExpression } = node; - return isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType; - } - - function createTypePredicate(kind: TypePredicateKind, parameterName: string | undefined, parameterIndex: number | undefined, type: Type | undefined): TypePredicate { - return { kind, parameterName, parameterIndex, type } as TypePredicate; - } - - /** - * Gets the minimum number of type arguments needed to satisfy all non-optional type - * parameters. - */ - function getMinTypeArgumentCount(typeParameters: readonly TypeParameter[] | undefined): number { - let minTypeArgumentCount = 0; - if (typeParameters) { - for (let i = 0; i < typeParameters.length; i++) { - if (!hasTypeParameterDefault(typeParameters[i])) { - minTypeArgumentCount = i + 1; - } - } - } - return minTypeArgumentCount; - } - - /** - * Fill in default types for unsupplied type arguments. If `typeArguments` is undefined - * when a default type is supplied, a new array will be created and returned. - * - * @param typeArguments The supplied type arguments. - * @param typeParameters The requested type parameters. - * @param minTypeArgumentCount The minimum number of required type arguments. - */ - function fillMissingTypeArguments(typeArguments: readonly Type[], typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean): Type[]; - function fillMissingTypeArguments(typeArguments: readonly Type[] | undefined, typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean): Type[] | undefined; - function fillMissingTypeArguments(typeArguments: readonly Type[] | undefined, typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean) { - const numTypeParameters = length(typeParameters); - if (!numTypeParameters) { - return []; - } - const numTypeArguments = length(typeArguments); - if (isJavaScriptImplicitAny || (numTypeArguments >= minTypeArgumentCount && numTypeArguments <= numTypeParameters)) { - const result = typeArguments ? typeArguments.slice() : []; - // Map invalid forward references in default types to the error type - for (let i = numTypeArguments; i < numTypeParameters; i++) { - result[i] = errorType; - } - const baseDefaultType = getDefaultTypeArgumentType(isJavaScriptImplicitAny); - for (let i = numTypeArguments; i < numTypeParameters; i++) { - let defaultType = getDefaultFromTypeParameter(typeParameters![i]); - if (isJavaScriptImplicitAny && defaultType && (isTypeIdenticalTo(defaultType, unknownType) || isTypeIdenticalTo(defaultType, emptyObjectType))) { - defaultType = anyType; - } - result[i] = defaultType ? instantiateType(defaultType, createTypeMapper(typeParameters!, result)) : baseDefaultType; - } - result.length = typeParameters!.length; - return result; - } - return typeArguments && typeArguments.slice(); - } - - function getSignatureFromDeclaration(declaration: SignatureDeclaration | JSDocSignature): Signature { - const links = getNodeLinks(declaration); - if (!links.resolvedSignature) { - const parameters: Symbol[] = []; - let flags = SignatureFlags.None; - let minArgumentCount = 0; - let thisParameter: Symbol | undefined; - let hasThisParameter = false; - const iife = getImmediatelyInvokedFunctionExpression(declaration); - const isJSConstructSignature = isJSDocConstructSignature(declaration); - const isUntypedSignatureInJSFile = !iife && - isInJSFile(declaration) && - isValueSignatureDeclaration(declaration) && - !hasJSDocParameterTags(declaration) && - !getJSDocType(declaration); - - // If this is a JSDoc construct signature, then skip the first parameter in the - // parameter list. The first parameter represents the return type of the construct - // signature. - for (let i = isJSConstructSignature ? 1 : 0; i < declaration.parameters.length; i++) { - const param = declaration.parameters[i]; - - let paramSymbol = param.symbol; - const type = isJSDocParameterTag(param) ? (param.typeExpression && param.typeExpression.type) : param.type; - // Include parameter symbol instead of property symbol in the signature - if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) { - const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined, /*isUse*/ false); - paramSymbol = resolvedSymbol!; - } - if (i === 0 && paramSymbol.escapedName === InternalSymbolName.This) { - hasThisParameter = true; - thisParameter = param.symbol; - } - else { - parameters.push(paramSymbol); - } - - if (type && type.kind === SyntaxKind.LiteralType) { - flags |= SignatureFlags.HasLiteralTypes; - } - - // Record a new minimum argument count if this is not an optional parameter - const isOptionalParameter = isOptionalJSDocParameterTag(param) || - param.initializer || param.questionToken || param.dotDotDotToken || - iife && parameters.length > iife.arguments.length && !type || - isUntypedSignatureInJSFile || - isJSDocOptionalParameter(param); - if (!isOptionalParameter) { - minArgumentCount = parameters.length; - } - } - - // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation - if ((declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) && - !hasNonBindableDynamicName(declaration) && - (!hasThisParameter || !thisParameter)) { - const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - const other = getDeclarationOfKind(getSymbolOfNode(declaration), otherKind); - if (other) { - thisParameter = getAnnotatedAccessorThisParameter(other); - } - } - - const classType = declaration.kind === SyntaxKind.Constructor ? - getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) - : undefined; - const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration); - if (hasRestParameter(declaration) || isInJSFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters)) { - flags |= SignatureFlags.HasRestParameter; - } - links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, - /*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined, - minArgumentCount, flags); - } - return links.resolvedSignature; - } - - /** - * A JS function gets a synthetic rest parameter if it references `arguments` AND: - * 1. It has no parameters but at least one `@param` with a type that starts with `...` - * OR - * 2. It has at least one parameter, and the last parameter has a matching `@param` with a type that starts with `...` - */ - function maybeAddJsSyntheticRestParameter(declaration: SignatureDeclaration | JSDocSignature, parameters: Symbol[]): boolean { - if (isJSDocSignature(declaration) || !containsArgumentsReference(declaration)) { - return false; - } - const lastParam = lastOrUndefined(declaration.parameters); - const lastParamTags = lastParam ? getJSDocParameterTags(lastParam) : getJSDocTags(declaration).filter(isJSDocParameterTag); - const lastParamVariadicType = firstDefined(lastParamTags, p => - p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined); - - const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String, CheckFlags.RestParameter); - syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType; - if (lastParamVariadicType) { - // Replace the last parameter with a rest parameter. - parameters.pop(); - } - parameters.push(syntheticArgsSymbol); - return true; - } - - function getSignatureOfTypeTag(node: SignatureDeclaration | JSDocSignature) { - const typeTag = isInJSFile(node) ? getJSDocTypeTag(node) : undefined; - const signature = typeTag && typeTag.typeExpression && getSingleCallSignature(getTypeFromTypeNode(typeTag.typeExpression)); - return signature && getErasedSignature(signature); - } - - function getReturnTypeOfTypeTag(node: SignatureDeclaration | JSDocSignature) { - const signature = getSignatureOfTypeTag(node); - return signature && getReturnTypeOfSignature(signature); - } - - function containsArgumentsReference(declaration: SignatureDeclaration): boolean { - const links = getNodeLinks(declaration); - if (links.containsArgumentsReference === undefined) { - if (links.flags & NodeCheckFlags.CaptureArguments) { - links.containsArgumentsReference = true; - } - else { - links.containsArgumentsReference = traverse((declaration as FunctionLikeDeclaration).body!); - } - } - return links.containsArgumentsReference; - - function traverse(node: Node): boolean { - if (!node) return false; - switch (node.kind) { - case SyntaxKind.Identifier: - return (node).escapedText === "arguments" && isExpressionNode(node); - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return (node).name!.kind === SyntaxKind.ComputedPropertyName - && traverse((node).name!); - - default: - return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && !!forEachChild(node, traverse); - } - } - } - - function getSignaturesOfSymbol(symbol: Symbol | undefined): Signature[] { - if (!symbol) return emptyArray; - const result: Signature[] = []; - for (let i = 0; i < symbol.declarations.length; i++) { - const decl = symbol.declarations[i]; - if (!isFunctionLike(decl)) continue; - // Don't include signature if node is the implementation of an overloaded function. A node is considered - // an implementation node if it has a body and the previous node is of the same kind and immediately - // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). - if (i > 0 && (decl as FunctionLikeDeclaration).body) { - const previous = symbol.declarations[i - 1]; - if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { - continue; - } - } - result.push(getSignatureFromDeclaration(decl)); - } - return result; - } - - function resolveExternalModuleTypeByLiteral(name: StringLiteral) { - const moduleSym = resolveExternalModuleName(name, name); - if (moduleSym) { - const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym); - if (resolvedModuleSymbol) { - return getTypeOfSymbol(resolvedModuleSymbol); - } - } - - return anyType; - } - - function getThisTypeOfSignature(signature: Signature): Type | undefined { - if (signature.thisParameter) { - return getTypeOfSymbol(signature.thisParameter); - } - } - - function getTypePredicateOfSignature(signature: Signature): TypePredicate | undefined { - if (!signature.resolvedTypePredicate) { - if (signature.target) { - const targetTypePredicate = getTypePredicateOfSignature(signature.target); - signature.resolvedTypePredicate = targetTypePredicate ? instantiateTypePredicate(targetTypePredicate, signature.mapper!) : noTypePredicate; - } - else if (signature.unionSignatures) { - signature.resolvedTypePredicate = getUnionTypePredicate(signature.unionSignatures) || noTypePredicate; - } - else { - const type = signature.declaration && getEffectiveReturnTypeNode(signature.declaration); - let jsdocPredicate: TypePredicate | undefined; - if (!type && isInJSFile(signature.declaration)) { - const jsdocSignature = getSignatureOfTypeTag(signature.declaration!); - if (jsdocSignature && signature !== jsdocSignature) { - jsdocPredicate = getTypePredicateOfSignature(jsdocSignature); - } - } - signature.resolvedTypePredicate = type && isTypePredicateNode(type) ? - createTypePredicateFromTypePredicateNode(type, signature) : - jsdocPredicate || noTypePredicate; - } - Debug.assert(!!signature.resolvedTypePredicate); - } - return signature.resolvedTypePredicate === noTypePredicate ? undefined : signature.resolvedTypePredicate; - } - - function createTypePredicateFromTypePredicateNode(node: TypePredicateNode, signature: Signature): TypePredicate { - const parameterName = node.parameterName; - const type = node.type && getTypeFromTypeNode(node.type); - return parameterName.kind === SyntaxKind.ThisType ? - createTypePredicate(node.assertsModifier ? TypePredicateKind.AssertsThis : TypePredicateKind.This, /*parameterName*/ undefined, /*parameterIndex*/ undefined, type) : - createTypePredicate(node.assertsModifier ? TypePredicateKind.AssertsIdentifier : TypePredicateKind.Identifier, parameterName.escapedText as string, - findIndex(signature.parameters, p => p.escapedName === parameterName.escapedText), type); - } - - function getReturnTypeOfSignature(signature: Signature): Type { - if (!signature.resolvedReturnType) { - if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) { - return errorType; - } - let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) : - signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) : - getReturnTypeFromAnnotation(signature.declaration!) || - (nodeIsMissing((signature.declaration).body) ? anyType : getReturnTypeFromBody(signature.declaration)); - if (signature.flags & SignatureFlags.IsInnerCallChain) { - type = addOptionalTypeMarker(type); - } - else if (signature.flags & SignatureFlags.IsOuterCallChain) { - type = getOptionalType(type); - } - if (!popTypeResolution()) { - if (signature.declaration) { - const typeNode = getEffectiveReturnTypeNode(signature.declaration); - if (typeNode) { - error(typeNode, Diagnostics.Return_type_annotation_circularly_references_itself); - } - else if (noImplicitAny) { - const declaration = signature.declaration; - const name = getNameOfDeclaration(declaration); - if (name) { - error(name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(name)); - } - else { - error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions); - } - } - } - type = anyType; - } - signature.resolvedReturnType = type; - } - return signature.resolvedReturnType; - } - - function getReturnTypeFromAnnotation(declaration: SignatureDeclaration | JSDocSignature) { - if (declaration.kind === SyntaxKind.Constructor) { - return getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)); - } - if (isJSDocConstructSignature(declaration)) { - return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217 - } - const typeNode = getEffectiveReturnTypeNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - if (declaration.kind === SyntaxKind.GetAccessor && !hasNonBindableDynamicName(declaration)) { - const jsDocType = isInJSFile(declaration) && getTypeForDeclarationFromJSDocComment(declaration); - if (jsDocType) { - return jsDocType; - } - const setter = getDeclarationOfKind(getSymbolOfNode(declaration), SyntaxKind.SetAccessor); - const setterType = getAnnotatedAccessorType(setter); - if (setterType) { - return setterType; - } - } - return getReturnTypeOfTypeTag(declaration); - } - - function isResolvingReturnTypeOfSignature(signature: Signature) { - return !signature.resolvedReturnType && findResolutionCycleStartIndex(signature, TypeSystemPropertyName.ResolvedReturnType) >= 0; - } - - function getRestTypeOfSignature(signature: Signature): Type { - return tryGetRestTypeOfSignature(signature) || anyType; - } - - function tryGetRestTypeOfSignature(signature: Signature): Type | undefined { - if (signatureHasRestParameter(signature)) { - const sigRestType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); - const restType = isTupleType(sigRestType) ? getRestTypeOfTupleType(sigRestType) : sigRestType; - return restType && getIndexTypeOfType(restType, IndexKind.Number); - } - return undefined; - } - - function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { - const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); - if (inferredTypeParameters) { - const returnSignature = getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature)); - if (returnSignature) { - const newReturnSignature = cloneSignature(returnSignature); - newReturnSignature.typeParameters = inferredTypeParameters; - const newInstantiatedSignature = cloneSignature(instantiatedSignature); - newInstantiatedSignature.resolvedReturnType = getOrCreateTypeFromSignature(newReturnSignature); - return newInstantiatedSignature; - } - } - return instantiatedSignature; - } - - function getSignatureInstantiationWithoutFillingInTypeArguments(signature: Signature, typeArguments: readonly Type[] | undefined): Signature { - const instantiations = signature.instantiations || (signature.instantiations = createMap()); - const id = getTypeListId(typeArguments); - let instantiation = instantiations.get(id); - if (!instantiation) { - instantiations.set(id, instantiation = createSignatureInstantiation(signature, typeArguments)); - } - return instantiation; - } - - function createSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined): Signature { - return instantiateSignature(signature, createSignatureTypeMapper(signature, typeArguments), /*eraseTypeParameters*/ true); - } - - function createSignatureTypeMapper(signature: Signature, typeArguments: readonly Type[] | undefined): TypeMapper { - return createTypeMapper(signature.typeParameters!, typeArguments); - } - - function getErasedSignature(signature: Signature): Signature { - return signature.typeParameters ? - signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) : - signature; - } - - function createErasedSignature(signature: Signature) { - // Create an instantiation of the signature where all type arguments are the any type. - return instantiateSignature(signature, createTypeEraser(signature.typeParameters!), /*eraseTypeParameters*/ true); - } - - function getCanonicalSignature(signature: Signature): Signature { - return signature.typeParameters ? - signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) : - signature; - } - - function createCanonicalSignature(signature: Signature) { - // Create an instantiation of the signature where each unconstrained type parameter is replaced with - // its original. When a generic class or interface is instantiated, each generic method in the class or - // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios - // where different generations of the same type parameter are in scope). This leads to a lot of new type - // identities, and potentially a lot of work comparing those identities, so here we create an instantiation - // that uses the original type identities for all unconstrained type parameters. - return getSignatureInstantiation( - signature, - map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp), - isInJSFile(signature.declaration)); - } - - function getBaseSignature(signature: Signature) { - const typeParameters = signature.typeParameters; - if (typeParameters) { - const typeEraser = createTypeEraser(typeParameters); - const baseConstraints = map(typeParameters, tp => instantiateType(getBaseConstraintOfType(tp), typeEraser) || unknownType); - return instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true); - } - return signature; - } - - function getOrCreateTypeFromSignature(signature: Signature): ObjectType { - // There are two ways to declare a construct signature, one is by declaring a class constructor - // using the constructor keyword, and the other is declaring a bare construct signature in an - // object type literal or interface (using the new keyword). Each way of declaring a constructor - // will result in a different declaration kind. - if (!signature.isolatedSignatureType) { - const kind = signature.declaration ? signature.declaration.kind : SyntaxKind.Unknown; - const isConstructor = kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType; - const type = createObjectType(ObjectFlags.Anonymous); - type.members = emptySymbols; - type.properties = emptyArray; - type.callSignatures = !isConstructor ? [signature] : emptyArray; - type.constructSignatures = isConstructor ? [signature] : emptyArray; - signature.isolatedSignatureType = type; - } - - return signature.isolatedSignatureType; - } - - function getIndexSymbol(symbol: Symbol): Symbol | undefined { - return symbol.members!.get(InternalSymbolName.Index); - } - - function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): IndexSignatureDeclaration | undefined { - const syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword; - const indexSymbol = getIndexSymbol(symbol); - if (indexSymbol) { - for (const decl of indexSymbol.declarations) { - const node = cast(decl, isIndexSignatureDeclaration); - if (node.parameters.length === 1) { - const parameter = node.parameters[0]; - if (parameter.type && parameter.type.kind === syntaxKind) { - return node; - } - } - } - } - - return undefined; - } - - function createIndexInfo(type: Type, isReadonly: boolean, declaration?: IndexSignatureDeclaration): IndexInfo { - return { type, isReadonly, declaration }; - } - - function getIndexInfoOfSymbol(symbol: Symbol, kind: IndexKind): IndexInfo | undefined { - const declaration = getIndexDeclarationOfSymbol(symbol, kind); - if (declaration) { - return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, - hasModifier(declaration, ModifierFlags.Readonly), declaration); - } - return undefined; - } - - function getConstraintDeclaration(type: TypeParameter): TypeNode | undefined { - return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0]; - } - - function getInferredTypeParameterConstraint(typeParameter: TypeParameter) { - let inferences: Type[] | undefined; - if (typeParameter.symbol) { - for (const declaration of typeParameter.symbol.declarations) { - if (declaration.parent.kind === SyntaxKind.InferType) { - // When an 'infer T' declaration is immediately contained in a type reference node - // (such as 'Foo'), T's constraint is inferred from the constraint of the - // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are - // present, we form an intersection of the inferred constraint types. - const grandParent = declaration.parent.parent; - if (grandParent.kind === SyntaxKind.TypeReference) { - const typeReference = grandParent; - const typeParameters = getTypeParametersForTypeReference(typeReference); - if (typeParameters) { - const index = typeReference.typeArguments!.indexOf(declaration.parent); - if (index < typeParameters.length) { - const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]); - if (declaredConstraint) { - // Type parameter constraints can reference other type parameters so - // constraints need to be instantiated. If instantiation produces the - // type parameter itself, we discard that inference. For example, in - // type Foo = [T, U]; - // type Bar = T extends Foo ? Foo : T; - // the instantiated constraint for U is X, so we discard that inference. - const mapper = createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReference, typeParameters)); - const constraint = instantiateType(declaredConstraint, mapper); - if (constraint !== typeParameter) { - inferences = append(inferences, constraint); - } - } - } - } - } - // When an 'infer T' declaration is immediately contained in a rest parameter - // declaration, we infer an 'unknown[]' constraint. - else if (grandParent.kind === SyntaxKind.Parameter && (grandParent).dotDotDotToken) { - inferences = append(inferences, createArrayType(unknownType)); - } - } - } - } - return inferences && getIntersectionType(inferences); - } - - /** This is a worker function. Use getConstraintOfTypeParameter which guards against circular constraints. */ - function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type | undefined { - if (!typeParameter.constraint) { - if (typeParameter.target) { - const targetConstraint = getConstraintOfTypeParameter(typeParameter.target); - typeParameter.constraint = targetConstraint ? instantiateType(targetConstraint, typeParameter.mapper) : noConstraintType; - } - else { - const constraintDeclaration = getConstraintDeclaration(typeParameter); - typeParameter.constraint = constraintDeclaration ? getTypeFromTypeNode(constraintDeclaration) : - getInferredTypeParameterConstraint(typeParameter) || noConstraintType; - } - } - return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint; - } - - function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined { - const tp = getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!; - const host = isJSDocTemplateTag(tp.parent) ? getHostSignatureFromJSDoc(tp.parent) : tp.parent; - return host && getSymbolOfNode(host); - } - - function getTypeListId(types: readonly Type[] | undefined) { - let result = ""; - if (types) { - const length = types.length; - let i = 0; - while (i < length) { - const startId = types[i].id; - let count = 1; - while (i + count < length && types[i + count].id === startId + count) { - count++; - } - if (result.length) { - result += ","; - } - result += startId; - if (count > 1) { - result += ":" + count; - } - i += count; - } - } - return result; - } - - // This function is used to propagate certain flags when creating new object type references and union types. - // It is only necessary to do so if a constituent type might be the undefined type, the null type, the type - // of an object literal or the anyFunctionType. This is because there are operations in the type checker - // that care about the presence of such types at arbitrary depth in a containing type. - function getPropagatingFlagsOfTypes(types: readonly Type[], excludeKinds: TypeFlags): ObjectFlags { - let result: ObjectFlags = 0; - for (const type of types) { - if (!(type.flags & excludeKinds)) { - result |= getObjectFlags(type); - } - } - return result & ObjectFlags.PropagatingFlags; - } - - function createTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined): TypeReference { - const id = getTypeListId(typeArguments); - let type = target.instantiations.get(id); - if (!type) { - type = createObjectType(ObjectFlags.Reference, target.symbol); - target.instantiations.set(id, type); - type.objectFlags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; - type.target = target; - type.resolvedTypeArguments = typeArguments; - } - return type; - } - - function cloneTypeReference(source: TypeReference): TypeReference { - const type = createType(source.flags); - type.symbol = source.symbol; - type.objectFlags = source.objectFlags; - type.target = source.target; - type.resolvedTypeArguments = source.resolvedTypeArguments; - return type; - } - - function createDeferredTypeReference(target: GenericType, node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, mapper?: TypeMapper): DeferredTypeReference { - const aliasSymbol = getAliasSymbolForTypeNode(node); - const aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); - const type = createObjectType(ObjectFlags.Reference, target.symbol); - type.target = target; - type.node = node; - type.mapper = mapper; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = mapper ? instantiateTypes(aliasTypeArguments, mapper) : aliasTypeArguments; - return type; - } - - function getTypeArguments(type: TypeReference): readonly Type[] { - if (!type.resolvedTypeArguments) { - if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedTypeArguments)) { - return type.target.localTypeParameters?.map(() => errorType) || emptyArray; - } - const node = type.node; - const typeArguments = !node ? emptyArray : - node.kind === SyntaxKind.TypeReference ? concatenate(type.target.outerTypeParameters, getEffectiveTypeArguments(node, type.target.localTypeParameters!)) : - node.kind === SyntaxKind.ArrayType ? [getTypeFromTypeNode(node.elementType)] : - map(node.elementTypes, getTypeFromTypeNode); - if (popTypeResolution()) { - type.resolvedTypeArguments = type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; - } - else { - type.resolvedTypeArguments = type.target.localTypeParameters?.map(() => errorType) || emptyArray; - error( - type.node || currentNode, - type.target.symbol - ? Diagnostics.Type_arguments_for_0_circularly_reference_themselves - : Diagnostics.Tuple_type_arguments_circularly_reference_themselves - , - type.target.symbol && symbolToString(type.target.symbol) - ); - } - } - return type.resolvedTypeArguments; - } - - function getTypeReferenceArity(type: TypeReference): number { - return length(type.target.typeParameters); - } - - - /** - * Get type from type-reference that reference to class or interface - */ - function getTypeFromClassOrInterfaceReference(node: NodeWithTypeArguments, symbol: Symbol): Type { - const type = getDeclaredTypeOfSymbol(getMergedSymbol(symbol)); - const typeParameters = type.localTypeParameters; - if (typeParameters) { - const numTypeArguments = length(node.typeArguments); - const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); - const isJs = isInJSFile(node); - const isJsImplicitAny = !noImplicitAny && isJs; - if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { - const missingAugmentsTag = isJs && isExpressionWithTypeArguments(node) && !isJSDocAugmentsTag(node.parent); - const diag = minTypeArgumentCount === typeParameters.length ? - missingAugmentsTag ? - Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag : - Diagnostics.Generic_type_0_requires_1_type_argument_s : - missingAugmentsTag ? - Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag : - Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; - - const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); - error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); - if (!isJs) { - // TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments) - return errorType; - } - } - if (node.kind === SyntaxKind.TypeReference && isAliasedType(node)) { - return createDeferredTypeReference(type, node, /*mapper*/ undefined); - } - // In a type reference, the outer type parameters of the referenced class or interface are automatically - // supplied as type arguments and the type reference only specifies arguments for the local type parameters - // of the class or interface. - const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgumentsFromTypeReferenceNode(node), typeParameters, minTypeArgumentCount, isJs)); - return createTypeReference(type, typeArguments); - } - return checkNoTypeArguments(node, symbol) ? type : errorType; - } - - function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined): Type { - const type = getDeclaredTypeOfSymbol(symbol); - const links = getSymbolLinks(symbol); - const typeParameters = links.typeParameters!; - const id = getTypeListId(typeArguments); - let instantiation = links.instantiations!.get(id); - if (!instantiation) { - links.instantiations!.set(id, instantiation = instantiateType(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters), isInJSFile(symbol.valueDeclaration))))); - } - return instantiation; - } - - /** - * Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include - * references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the - * declared type. Instantiations are cached using the type identities of the type arguments as the key. - */ - function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol): Type { - const type = getDeclaredTypeOfSymbol(symbol); - const typeParameters = getSymbolLinks(symbol).typeParameters; - if (typeParameters) { - const numTypeArguments = length(node.typeArguments); - const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); - if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) { - error(node, - minTypeArgumentCount === typeParameters.length ? - Diagnostics.Generic_type_0_requires_1_type_argument_s : - Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments, - symbolToString(symbol), - minTypeArgumentCount, - typeParameters.length); - return errorType; - } - return getTypeAliasInstantiation(symbol, typeArgumentsFromTypeReferenceNode(node)); - } - return checkNoTypeArguments(node, symbol) ? type : errorType; - } - - function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined { - switch (node.kind) { - case SyntaxKind.TypeReference: - return node.typeName; - case SyntaxKind.ExpressionWithTypeArguments: - // We only support expressions that are simple qualified names. For other - // expressions this produces undefined. - const expr = node.expression; - if (isEntityNameExpression(expr)) { - return expr; - } - // fall through; - } - - return undefined; - } - - function resolveTypeReferenceName(typeReferenceName: EntityNameExpression | EntityName | undefined, meaning: SymbolFlags, ignoreErrors?: boolean) { - if (!typeReferenceName) { - return unknownSymbol; - } - - return resolveEntityName(typeReferenceName, meaning, ignoreErrors) || unknownSymbol; - } - - function getTypeReferenceType(node: NodeWithTypeArguments, symbol: Symbol): Type { - if (symbol === unknownSymbol) { - return errorType; - } - symbol = getExpandoSymbol(symbol) || symbol; - if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - return getTypeFromClassOrInterfaceReference(node, symbol); - } - if (isTypeOnlyAlias(symbol)) { - return getTypeReferenceType(node, symbol.immediateTarget); - } - if (symbol.flags & SymbolFlags.TypeAlias) { - return getTypeFromTypeAliasReference(node, symbol); - } - // Get type from reference to named type that cannot be generic (enum or type parameter) - const res = tryGetDeclaredTypeOfSymbol(symbol); - if (res) { - return checkNoTypeArguments(node, symbol) ? - res.flags & TypeFlags.TypeParameter ? getConstrainedTypeVariable(res, node) : getRegularTypeOfLiteralType(res) : - errorType; - } - if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) { - const jsdocType = getTypeFromJSDocValueReference(node, symbol); - if (jsdocType) { - return jsdocType; - } - else { - // Resolve the type reference as a Type for the purpose of reporting errors. - resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type); - return getTypeOfSymbol(symbol); - } - } - return errorType; - } - - /** - * A JSdoc TypeReference may be to a value, but resolve it as a type anyway. - * Note: If the value is imported from commonjs, it should really be an alias, - * but this function's special-case code fakes alias resolution as well. - */ - function getTypeFromJSDocValueReference(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined { - const valueType = getTypeOfSymbol(symbol); - let typeType = valueType; - if (symbol.valueDeclaration) { - const decl = getRootDeclaration(symbol.valueDeclaration); - let isRequireAlias = false; - if (isVariableDeclaration(decl) && decl.initializer) { - let expr = decl.initializer; - // skip past entity names, eg `require("x").a.b.c` - while (isPropertyAccessExpression(expr)) { - expr = expr.expression; - } - isRequireAlias = isCallExpression(expr) && isRequireCall(expr, /*requireStringLiteralLikeArgument*/ true) && !!valueType.symbol; - } - const isImportTypeWithQualifier = node.kind === SyntaxKind.ImportType && (node as ImportTypeNode).qualifier; - // valueType might not have a symbol, eg, {import('./b').STRING_LITERAL} - if (valueType.symbol && (isRequireAlias || isImportTypeWithQualifier)) { - typeType = getTypeReferenceType(node, valueType.symbol); - } - } - return getSymbolLinks(symbol).resolvedJSDocType = typeType; - } - - function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) { - if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === typeVariable) { - return typeVariable; - } - const id = `${getTypeId(typeVariable)}>${getTypeId(substitute)}`; - const cached = substitutionTypes.get(id); - if (cached) { - return cached; - } - const result = createType(TypeFlags.Substitution); - result.typeVariable = typeVariable; - result.substitute = substitute; - substitutionTypes.set(id, result); - return result; - } - - function isUnaryTupleTypeNode(node: TypeNode) { - return node.kind === SyntaxKind.TupleType && (node).elementTypes.length === 1; - } - - function getImpliedConstraint(typeVariable: TypeVariable, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined { - return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(typeVariable, (checkNode).elementTypes[0], (extendsNode).elementTypes[0]) : - getActualTypeVariable(getTypeFromTypeNode(checkNode)) === typeVariable ? getTypeFromTypeNode(extendsNode) : - undefined; - } - - function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) { - let constraints: Type[] | undefined; - while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDocComment) { - const parent = node.parent; - if (parent.kind === SyntaxKind.ConditionalType && node === (parent).trueType) { - const constraint = getImpliedConstraint(typeVariable, (parent).checkType, (parent).extendsType); - if (constraint) { - constraints = append(constraints, constraint); - } - } - node = parent; - } - return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable; - } - - function isJSDocTypeReference(node: Node): node is TypeReferenceNode { - return !!(node.flags & NodeFlags.JSDoc) && (node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ImportType); - } - - function checkNoTypeArguments(node: NodeWithTypeArguments, symbol?: Symbol) { - if (node.typeArguments) { - error(node, Diagnostics.Type_0_is_not_generic, symbol ? symbolToString(symbol) : (node).typeName ? declarationNameToString((node).typeName) : anon); - return false; - } - return true; - } - - function getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type | undefined { - if (isIdentifier(node.typeName)) { - const typeArgs = node.typeArguments; - switch (node.typeName.escapedText) { - case "String": - checkNoTypeArguments(node); - return stringType; - case "Number": - checkNoTypeArguments(node); - return numberType; - case "Boolean": - checkNoTypeArguments(node); - return booleanType; - case "Void": - checkNoTypeArguments(node); - return voidType; - case "Undefined": - checkNoTypeArguments(node); - return undefinedType; - case "Null": - checkNoTypeArguments(node); - return nullType; - case "Function": - case "function": - checkNoTypeArguments(node); - return globalFunctionType; - case "array": - return (!typeArgs || !typeArgs.length) && !noImplicitAny ? anyArrayType : undefined; - case "promise": - return (!typeArgs || !typeArgs.length) && !noImplicitAny ? createPromiseType(anyType) : undefined; - case "Object": - if (typeArgs && typeArgs.length === 2) { - if (isJSDocIndexSignature(node)) { - const indexed = getTypeFromTypeNode(typeArgs[0]); - const target = getTypeFromTypeNode(typeArgs[1]); - const index = createIndexInfo(target, /*isReadonly*/ false); - return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType ? index : undefined, indexed === numberType ? index : undefined); - } - return anyType; - } - checkNoTypeArguments(node); - return !noImplicitAny ? anyType : undefined; - } - } - } - - function getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) { - const type = getTypeFromTypeNode(node.type); - return strictNullChecks ? getNullableType(type, TypeFlags.Null) : type; - } - - function getTypeFromTypeReference(node: TypeReferenceType): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - let symbol: Symbol | undefined; - let type: Type | undefined; - const meaning = SymbolFlags.Type; - if (isJSDocTypeReference(node)) { - type = getIntendedTypeFromJSDocTypeReference(node); - if (!type) { - symbol = resolveTypeReferenceName(getTypeReferenceName(node), meaning, /*ignoreErrors*/ true); - if (symbol === unknownSymbol) { - symbol = resolveTypeReferenceName(getTypeReferenceName(node), meaning | SymbolFlags.Value); - } - else { - resolveTypeReferenceName(getTypeReferenceName(node), meaning); // Resolve again to mark errors, if any - } - type = getTypeReferenceType(node, symbol); - } - } - if (!type) { - symbol = resolveTypeReferenceName(getTypeReferenceName(node), meaning); - type = getTypeReferenceType(node, symbol); - } - // Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the - // type reference in checkTypeReferenceNode. - links.resolvedSymbol = symbol; - links.resolvedType = type; - } - return links.resolvedType; - } - - function typeArgumentsFromTypeReferenceNode(node: NodeWithTypeArguments): Type[] | undefined { - return map(node.typeArguments, getTypeFromTypeNode); - } - - function getTypeFromTypeQueryNode(node: TypeQueryNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - // TypeScript 1.0 spec (April 2014): 3.6.3 - // The expression is processed as an identifier expression (section 4.3) - // or property access expression(section 4.10), - // the widened type(section 3.9) of which becomes the result. - links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(checkExpression(node.exprName))); - } - return links.resolvedType; - } - - function getTypeOfGlobalSymbol(symbol: Symbol | undefined, arity: number): ObjectType { - - function getTypeDeclaration(symbol: Symbol): Declaration | undefined { - const declarations = symbol.declarations; - for (const declaration of declarations) { - switch (declaration.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - return declaration; - } - } - } - - if (!symbol) { - return arity ? emptyGenericType : emptyObjectType; - } - const type = getDeclaredTypeOfSymbol(symbol); - if (!(type.flags & TypeFlags.Object)) { - error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbolName(symbol)); - return arity ? emptyGenericType : emptyObjectType; - } - if (length((type).typeParameters) !== arity) { - error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity); - return arity ? emptyGenericType : emptyObjectType; - } - return type; - } - - function getGlobalValueSymbol(name: __String, reportErrors: boolean): Symbol | undefined { - return getGlobalSymbol(name, SymbolFlags.Value, reportErrors ? Diagnostics.Cannot_find_global_value_0 : undefined); - } - - function getGlobalTypeSymbol(name: __String, reportErrors: boolean): Symbol | undefined { - return getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined); - } - - function getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage | undefined): Symbol | undefined { - // Don't track references for global symbols anyway, so value if `isReference` is arbitrary - return resolveName(undefined, name, meaning, diagnostic, name, /*isUse*/ false); - } - - function getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType; - function getGlobalType(name: __String, arity: number, reportErrors: boolean): GenericType; - function getGlobalType(name: __String, arity: number, reportErrors: boolean): ObjectType | undefined { - const symbol = getGlobalTypeSymbol(name, reportErrors); - return symbol || reportErrors ? getTypeOfGlobalSymbol(symbol, arity) : undefined; - } - - function getGlobalTypedPropertyDescriptorType() { - return deferredGlobalTypedPropertyDescriptorType || (deferredGlobalTypedPropertyDescriptorType = getGlobalType("TypedPropertyDescriptor" as __String, /*arity*/ 1, /*reportErrors*/ true)) || emptyGenericType; - } - - function getGlobalTemplateStringsArrayType() { - return deferredGlobalTemplateStringsArrayType || (deferredGlobalTemplateStringsArrayType = getGlobalType("TemplateStringsArray" as __String, /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType; - } - - function getGlobalImportMetaType() { - return deferredGlobalImportMetaType || (deferredGlobalImportMetaType = getGlobalType("ImportMeta" as __String, /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType; - } - - function getGlobalESSymbolConstructorSymbol(reportErrors: boolean) { - return deferredGlobalESSymbolConstructorSymbol || (deferredGlobalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol" as __String, reportErrors)); - } - - function getGlobalESSymbolType(reportErrors: boolean) { - return deferredGlobalESSymbolType || (deferredGlobalESSymbolType = getGlobalType("Symbol" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; - } - - function getGlobalPromiseType(reportErrors: boolean) { - return deferredGlobalPromiseType || (deferredGlobalPromiseType = getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalPromiseLikeType(reportErrors: boolean) { - return deferredGlobalPromiseLikeType || (deferredGlobalPromiseLikeType = getGlobalType("PromiseLike" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalPromiseConstructorSymbol(reportErrors: boolean): Symbol | undefined { - return deferredGlobalPromiseConstructorSymbol || (deferredGlobalPromiseConstructorSymbol = getGlobalValueSymbol("Promise" as __String, reportErrors)); - } - - function getGlobalPromiseConstructorLikeType(reportErrors: boolean) { - return deferredGlobalPromiseConstructorLikeType || (deferredGlobalPromiseConstructorLikeType = getGlobalType("PromiseConstructorLike" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; - } - - function getGlobalAsyncIterableType(reportErrors: boolean) { - return deferredGlobalAsyncIterableType || (deferredGlobalAsyncIterableType = getGlobalType("AsyncIterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalAsyncIteratorType(reportErrors: boolean) { - return deferredGlobalAsyncIteratorType || (deferredGlobalAsyncIteratorType = getGlobalType("AsyncIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; - } - - function getGlobalAsyncIterableIteratorType(reportErrors: boolean) { - return deferredGlobalAsyncIterableIteratorType || (deferredGlobalAsyncIterableIteratorType = getGlobalType("AsyncIterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalAsyncGeneratorType(reportErrors: boolean) { - return deferredGlobalAsyncGeneratorType || (deferredGlobalAsyncGeneratorType = getGlobalType("AsyncGenerator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; - } - - function getGlobalIterableType(reportErrors: boolean) { - return deferredGlobalIterableType || (deferredGlobalIterableType = getGlobalType("Iterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalIteratorType(reportErrors: boolean) { - return deferredGlobalIteratorType || (deferredGlobalIteratorType = getGlobalType("Iterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; - } - - function getGlobalIterableIteratorType(reportErrors: boolean) { - return deferredGlobalIterableIteratorType || (deferredGlobalIterableIteratorType = getGlobalType("IterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalGeneratorType(reportErrors: boolean) { - return deferredGlobalGeneratorType || (deferredGlobalGeneratorType = getGlobalType("Generator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; - } - - function getGlobalIteratorYieldResultType(reportErrors: boolean) { - return deferredGlobalIteratorYieldResultType || (deferredGlobalIteratorYieldResultType = getGlobalType("IteratorYieldResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalIteratorReturnResultType(reportErrors: boolean) { - return deferredGlobalIteratorReturnResultType || (deferredGlobalIteratorReturnResultType = getGlobalType("IteratorReturnResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; - } - - function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined { - const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined); - return symbol && getTypeOfGlobalSymbol(symbol, arity); - } - - function getGlobalExtractSymbol(): Symbol { - return deferredGlobalExtractSymbol || (deferredGlobalExtractSymbol = getGlobalSymbol("Extract" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217 - } - - function getGlobalOmitSymbol(): Symbol { - return deferredGlobalOmitSymbol || (deferredGlobalOmitSymbol = getGlobalSymbol("Omit" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217 - } - - function getGlobalBigIntType(reportErrors: boolean) { - return deferredGlobalBigIntType || (deferredGlobalBigIntType = getGlobalType("BigInt" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; - } - - /** - * Instantiates a global type that is generic with some element type, and returns that instantiation. - */ - function createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: readonly Type[]): ObjectType { - return genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, typeArguments) : emptyObjectType; - } - - function createTypedPropertyDescriptorType(propertyType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalTypedPropertyDescriptorType(), [propertyType]); - } - - function createIterableType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalIterableType(/*reportErrors*/ true), [iteratedType]); - } - - function createArrayType(elementType: Type, readonly?: boolean): ObjectType { - return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]); - } - - function getArrayOrTupleTargetType(node: ArrayTypeNode | TupleTypeNode): GenericType { - const readonly = isReadonlyTypeOperator(node.parent); - if (node.kind === SyntaxKind.ArrayType || node.elementTypes.length === 1 && node.elementTypes[0].kind === SyntaxKind.RestType) { - return readonly ? globalReadonlyArrayType : globalArrayType; - } - const lastElement = lastOrUndefined(node.elementTypes); - const restElement = lastElement && lastElement.kind === SyntaxKind.RestType ? lastElement : undefined; - const minLength = findLastIndex(node.elementTypes, n => n.kind !== SyntaxKind.OptionalType && n !== restElement) + 1; - return getTupleTypeOfArity(node.elementTypes.length, minLength, !!restElement, readonly, /*associatedNames*/ undefined); - } - - // Return true when the given node is transitively contained in type constructs that eagerly - // resolve their constituent types. We include SyntaxKind.TypeReference because type arguments - // of type aliases are eagerly resolved. - function isAliasedType(node: Node): boolean { - const parent = node.parent; - switch (parent.kind) { - case SyntaxKind.ParenthesizedType: - case SyntaxKind.TypeReference: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - case SyntaxKind.IndexedAccessType: - case SyntaxKind.ConditionalType: - case SyntaxKind.TypeOperator: - return isAliasedType(parent); - case SyntaxKind.TypeAliasDeclaration: - return true; - } - return false; - } - - function getTypeFromArrayOrTupleTypeNode(node: ArrayTypeNode | TupleTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const target = getArrayOrTupleTargetType(node); - if (target === emptyGenericType) { - links.resolvedType = emptyObjectType; - } - else if (isAliasedType(node)) { - links.resolvedType = node.kind === SyntaxKind.TupleType && node.elementTypes.length === 0 ? target : - createDeferredTypeReference(target, node, /*mapper*/ undefined); - } - else { - const elementTypes = node.kind === SyntaxKind.ArrayType ? [getTypeFromTypeNode(node.elementType)] : map(node.elementTypes, getTypeFromTypeNode); - links.resolvedType = createTypeReference(target, elementTypes); - } - } - return links.resolvedType; - } - - function isReadonlyTypeOperator(node: Node) { - return isTypeOperatorNode(node) && node.operator === SyntaxKind.ReadonlyKeyword; - } - - // We represent tuple types as type references to synthesized generic interface types created by - // this function. The types are of the form: - // - // interface Tuple extends Array { 0: T0, 1: T1, 2: T2, ... } - // - // Note that the generic type created by this function has no symbol associated with it. The same - // is true for each of the synthesized type parameters. - function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType { - let typeParameters: TypeParameter[] | undefined; - const properties: Symbol[] = []; - const maxLength = hasRestElement ? arity - 1 : arity; - if (arity) { - typeParameters = new Array(arity); - for (let i = 0; i < arity; i++) { - const typeParameter = typeParameters[i] = createTypeParameter(); - if (i < maxLength) { - const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), - "" + i as __String, readonly ? CheckFlags.Readonly : 0); - property.type = typeParameter; - properties.push(property); - } - } - } - const literalTypes = []; - for (let i = minLength; i <= maxLength; i++) literalTypes.push(getLiteralType(i)); - const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String); - lengthSymbol.type = hasRestElement ? numberType : getUnionType(literalTypes); - properties.push(lengthSymbol); - const type = createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference); - type.typeParameters = typeParameters; - type.outerTypeParameters = undefined; - type.localTypeParameters = typeParameters; - type.instantiations = createMap(); - type.instantiations.set(getTypeListId(type.typeParameters), type); - type.target = type; - type.resolvedTypeArguments = type.typeParameters; - type.thisType = createTypeParameter(); - type.thisType.isThisType = true; - type.thisType.constraint = type; - type.declaredProperties = properties; - type.declaredCallSignatures = emptyArray; - type.declaredConstructSignatures = emptyArray; - type.declaredStringIndexInfo = undefined; - type.declaredNumberIndexInfo = undefined; - type.minLength = minLength; - type.hasRestElement = hasRestElement; - type.readonly = readonly; - type.associatedNames = associatedNames; - return type; - } - - function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType { - const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : ""); - let type = tupleTypes.get(key); - if (!type) { - tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames)); - } - return type; - } - - function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) { - const arity = elementTypes.length; - if (arity === 1 && hasRestElement) { - return createArrayType(elementTypes[0], readonly); - } - const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames); - return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType; - } - - function sliceTupleType(type: TupleTypeReference, index: number) { - const tuple = type.target; - if (tuple.hasRestElement) { - // don't slice off rest element - index = Math.min(index, getTypeReferenceArity(type) - 1); - } - return createTupleType( - getTypeArguments(type).slice(index), - Math.max(0, tuple.minLength - index), - tuple.hasRestElement, - tuple.readonly, - tuple.associatedNames && tuple.associatedNames.slice(index), - ); - } - - function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type { - const type = getTypeFromTypeNode(node.type); - return strictNullChecks ? getOptionalType(type) : type; - } - - function getTypeId(type: Type) { - return type.id; - } - - function containsType(types: readonly Type[], type: Type): boolean { - return binarySearch(types, type, getTypeId, compareValues) >= 0; - } - - function insertType(types: Type[], type: Type): boolean { - const index = binarySearch(types, type, getTypeId, compareValues); - if (index < 0) { - types.splice(~index, 0, type); - return true; - } - return false; - } - - function addTypeToUnion(typeSet: Type[], includes: TypeFlags, type: Type) { - const flags = type.flags; - if (flags & TypeFlags.Union) { - return addTypesToUnion(typeSet, includes, (type).types); - } - // We ignore 'never' types in unions - if (!(flags & TypeFlags.Never)) { - includes |= flags & TypeFlags.IncludesMask; - if (flags & TypeFlags.StructuredOrInstantiable) includes |= TypeFlags.IncludesStructuredOrInstantiable; - if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; - if (!strictNullChecks && flags & TypeFlags.Nullable) { - if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType; - } - else { - const len = typeSet.length; - const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearch(typeSet, type, getTypeId, compareValues); - if (index < 0) { - typeSet.splice(~index, 0, type); - } - } - } - return includes; - } - - // Add the given types to the given type set. Order is preserved, duplicates are removed, - // and nested types of the given kind are flattened into the set. - function addTypesToUnion(typeSet: Type[], includes: TypeFlags, types: readonly Type[]): TypeFlags { - for (const type of types) { - includes = addTypeToUnion(typeSet, includes, type); - } - return includes; - } - - function isSetOfLiteralsFromSameEnum(types: readonly Type[]): boolean { - const first = types[0]; - if (first.flags & TypeFlags.EnumLiteral) { - const firstEnum = getParentOfSymbol(first.symbol); - for (let i = 1; i < types.length; i++) { - const other = types[i]; - if (!(other.flags & TypeFlags.EnumLiteral) || (firstEnum !== getParentOfSymbol(other.symbol))) { - return false; - } - } - return true; - } - - return false; - } - - function removeSubtypes(types: Type[], primitivesOnly: boolean): boolean { - const len = types.length; - if (len === 0 || isSetOfLiteralsFromSameEnum(types)) { - return true; - } - let i = len; - let count = 0; - while (i > 0) { - i--; - const source = types[i]; - for (const target of types) { - if (source !== target) { - if (count === 100000) { - // After 100000 subtype checks we estimate the remaining amount of work by assuming the - // same ratio of checks per element. If the estimated number of remaining type checks is - // greater than an upper limit we deem the union type too complex to represent. The - // upper limit is 25M for unions of primitives only, and 1M otherwise. This for example - // caps union types at 5000 unique literal types and 1000 unique object types. - const estimatedCount = (count / (len - i)) * len; - if (estimatedCount > (primitivesOnly ? 25000000 : 1000000)) { - error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); - return false; - } - } - count++; - if (isTypeRelatedTo(source, target, strictSubtypeRelation) && ( - !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) || - !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) || - isTypeDerivedFrom(source, target))) { - orderedRemoveItemAt(types, i); - break; - } - } - } - } - return true; - } - - function removeRedundantLiteralTypes(types: Type[], includes: TypeFlags) { - let i = types.length; - while (i > 0) { - i--; - const t = types[i]; - const remove = - t.flags & TypeFlags.StringLiteral && includes & TypeFlags.String || - t.flags & TypeFlags.NumberLiteral && includes & TypeFlags.Number || - t.flags & TypeFlags.BigIntLiteral && includes & TypeFlags.BigInt || - t.flags & TypeFlags.UniqueESSymbol && includes & TypeFlags.ESSymbol || - isFreshLiteralType(t) && containsType(types, (t).regularType); - if (remove) { - orderedRemoveItemAt(types, i); - } - } - } - - // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction - // flag is specified we also reduce the constituent type set to only include types that aren't subtypes - // of other types. Subtype reduction is expensive for large union types and is possible only when union - // types are known not to circularly reference themselves (as is the case with union types created by - // expression constructs such as array literals and the || and ?: operators). Named types can - // circularly reference themselves and therefore cannot be subtype reduced during their declaration. - // For example, "type Item = string | (() => Item" is a named type that circularly references itself. - function getUnionType(types: readonly Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - if (types.length === 0) { - return neverType; - } - if (types.length === 1) { - return types[0]; - } - const typeSet: Type[] = []; - const includes = addTypesToUnion(typeSet, 0, types); - if (unionReduction !== UnionReduction.None) { - if (includes & TypeFlags.AnyOrUnknown) { - return includes & TypeFlags.Any ? includes & TypeFlags.IncludesWildcard ? wildcardType : anyType : unknownType; - } - switch (unionReduction) { - case UnionReduction.Literal: - if (includes & (TypeFlags.Literal | TypeFlags.UniqueESSymbol)) { - removeRedundantLiteralTypes(typeSet, includes); - } - break; - case UnionReduction.Subtype: - if (!removeSubtypes(typeSet, !(includes & TypeFlags.IncludesStructuredOrInstantiable))) { - return errorType; - } - break; - } - if (typeSet.length === 0) { - return includes & TypeFlags.Null ? includes & TypeFlags.IncludesNonWideningType ? nullType : nullWideningType : - includes & TypeFlags.Undefined ? includes & TypeFlags.IncludesNonWideningType ? undefinedType : undefinedWideningType : - neverType; - } - } - return getUnionTypeFromSortedList(typeSet, includes & TypeFlags.NotPrimitiveUnion ? 0 : ObjectFlags.PrimitiveUnion, aliasSymbol, aliasTypeArguments); - } - - function getUnionTypePredicate(signatures: readonly Signature[]): TypePredicate | undefined { - let first: TypePredicate | undefined; - const types: Type[] = []; - for (const sig of signatures) { - const pred = getTypePredicateOfSignature(sig); - if (!pred || pred.kind === TypePredicateKind.AssertsThis || pred.kind === TypePredicateKind.AssertsIdentifier) { - continue; - } - - if (first) { - if (!typePredicateKindsMatch(first, pred)) { - // No common type predicate. - return undefined; - } - } - else { - first = pred; - } - types.push(pred.type); - } - if (!first) { - // No union signatures had a type predicate. - return undefined; - } - const unionType = getUnionType(types); - return createTypePredicate(first.kind, first.parameterName, first.parameterIndex, unionType); - } - - function typePredicateKindsMatch(a: TypePredicate, b: TypePredicate): boolean { - return a.kind === b.kind && a.parameterIndex === b.parameterIndex; - } - - // This function assumes the constituent type list is sorted and deduplicated. - function getUnionTypeFromSortedList(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - if (types.length === 0) { - return neverType; - } - if (types.length === 1) { - return types[0]; - } - const id = getTypeListId(types); - let type = unionTypes.get(id); - if (!type) { - type = createType(TypeFlags.Union); - unionTypes.set(id, type); - type.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type.types = types; - /* - Note: This is the alias symbol (or lack thereof) that we see when we first encounter this union type. - For aliases of identical unions, eg `type T = A | B; type U = A | B`, the symbol of the first alias encountered is the aliasSymbol. - (In the language service, the order may depend on the order in which a user takes actions, such as hovering over symbols.) - It's important that we create equivalent union types only once, so that's an unfortunate side effect. - */ - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; - } - return type; - } - - function getTypeFromUnionTypeNode(node: UnionTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const aliasSymbol = getAliasSymbolForTypeNode(node); - links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), UnionReduction.Literal, - aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol)); - } - return links.resolvedType; - } - - function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { - const flags = type.flags; - if (flags & TypeFlags.Intersection) { - return addTypesToIntersection(typeSet, includes, (type).types); - } - if (isEmptyAnonymousObjectType(type)) { - if (!(includes & TypeFlags.IncludesEmptyObject)) { - includes |= TypeFlags.IncludesEmptyObject; - typeSet.set(type.id.toString(), type); - } - } - else { - if (flags & TypeFlags.AnyOrUnknown) { - if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; - } - else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) { - if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) { - // We have seen two distinct unit types which means we should reduce to an - // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen. - includes |= TypeFlags.NonPrimitive; - } - typeSet.set(type.id.toString(), type); - } - includes |= flags & TypeFlags.IncludesMask; - } - return includes; - } - - // Add the given types to the given type set. Order is preserved, freshness is removed from literal - // types, duplicates are removed, and nested types of the given kind are flattened into the set. - function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: readonly Type[]) { - for (const type of types) { - includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type)); - } - return includes; - } - - function removeRedundantPrimitiveTypes(types: Type[], includes: TypeFlags) { - let i = types.length; - while (i > 0) { - i--; - const t = types[i]; - const remove = - t.flags & TypeFlags.String && includes & TypeFlags.StringLiteral || - t.flags & TypeFlags.Number && includes & TypeFlags.NumberLiteral || - t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || - t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol; - if (remove) { - orderedRemoveItemAt(types, i); - } - } - } - - // Check that the given type has a match in every union. A given type is matched by - // an identical type, and a literal type is additionally matched by its corresponding - // primitive type. - function eachUnionContains(unionTypes: UnionType[], type: Type) { - for (const u of unionTypes) { - if (!containsType(u.types, type)) { - const primitive = type.flags & TypeFlags.StringLiteral ? stringType : - type.flags & TypeFlags.NumberLiteral ? numberType : - type.flags & TypeFlags.BigIntLiteral ? bigintType : - type.flags & TypeFlags.UniqueESSymbol ? esSymbolType : - undefined; - if (!primitive || !containsType(u.types, primitive)) { - return false; - } - } - } - return true; - } - - function extractIrreducible(types: Type[], flag: TypeFlags) { - if (every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)))) { - for (let i = 0; i < types.length; i++) { - types[i] = filterType(types[i], t => !(t.flags & flag)); - } - return true; - } - return false; - } - - // If the given list of types contains more than one union of primitive types, replace the - // first with a union containing an intersection of those primitive types, then remove the - // other unions and return true. Otherwise, do nothing and return false. - function intersectUnionsOfPrimitiveTypes(types: Type[]) { - let unionTypes: UnionType[] | undefined; - const index = findIndex(types, t => !!(getObjectFlags(t) & ObjectFlags.PrimitiveUnion)); - if (index < 0) { - return false; - } - let i = index + 1; - // Remove all but the first union of primitive types and collect them in - // the unionTypes array. - while (i < types.length) { - const t = types[i]; - if (getObjectFlags(t) & ObjectFlags.PrimitiveUnion) { - (unionTypes || (unionTypes = [types[index]])).push(t); - orderedRemoveItemAt(types, i); - } - else { - i++; - } - } - // Return false if there was only one union of primitive types - if (!unionTypes) { - return false; - } - // We have more than one union of primitive types, now intersect them. For each - // type in each union we check if the type is matched in every union and if so - // we include it in the result. - const checked: Type[] = []; - const result: Type[] = []; - for (const u of unionTypes) { - for (const t of u.types) { - if (insertType(checked, t)) { - if (eachUnionContains(unionTypes, t)) { - insertType(result, t); - } - } - } - } - // Finally replace the first union with the result - types[index] = getUnionTypeFromSortedList(result, ObjectFlags.PrimitiveUnion); - return true; - } - - function createIntersectionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) { - const result = createType(TypeFlags.Intersection); - result.objectFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - result.types = types; - result.aliasSymbol = aliasSymbol; // See comment in `getUnionTypeFromSortedList`. - result.aliasTypeArguments = aliasTypeArguments; - return result; - } - - // We normalize combinations of intersection and union types based on the distributive property of the '&' - // operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection - // types with union type constituents into equivalent union types with intersection type constituents and - // effectively ensure that union types are always at the top level in type representations. - // - // We do not perform structural deduplication on intersection types. Intersection types are created only by the & - // type operator and we can't reduce those because we want to support recursive intersection types. For example, - // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. - // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution - // for intersections of types with signatures can be deterministic. - function getIntersectionType(types: readonly Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - const typeMembershipMap: Map = createMap(); - const includes = addTypesToIntersection(typeMembershipMap, 0, types); - const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); - // An intersection type is considered empty if it contains - // the type never, or - // more than one unit type or, - // an object type and a nullable type (null or undefined), or - // a string-like type and a type known to be non-string-like, or - // a number-like type and a type known to be non-number-like, or - // a symbol-like type and a type known to be non-symbol-like, or - // a void-like type and a type known to be non-void-like, or - // a non-primitive type and a type known to be primitive. - if (includes & TypeFlags.Never || - strictNullChecks && includes & TypeFlags.Nullable && includes & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.IncludesEmptyObject) || - includes & TypeFlags.NonPrimitive && includes & (TypeFlags.DisjointDomains & ~TypeFlags.NonPrimitive) || - includes & TypeFlags.StringLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.StringLike) || - includes & TypeFlags.NumberLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.NumberLike) || - includes & TypeFlags.BigIntLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.BigIntLike) || - includes & TypeFlags.ESSymbolLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.ESSymbolLike) || - includes & TypeFlags.VoidLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) { - return neverType; - } - if (includes & TypeFlags.Any) { - return includes & TypeFlags.IncludesWildcard ? wildcardType : anyType; - } - if (!strictNullChecks && includes & TypeFlags.Nullable) { - return includes & TypeFlags.Undefined ? undefinedType : nullType; - } - if (includes & TypeFlags.String && includes & TypeFlags.StringLiteral || - includes & TypeFlags.Number && includes & TypeFlags.NumberLiteral || - includes & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || - includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol) { - removeRedundantPrimitiveTypes(typeSet, includes); - } - if (includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.Object) { - orderedRemoveItemAt(typeSet, findIndex(typeSet, isEmptyAnonymousObjectType)); - } - if (typeSet.length === 0) { - return unknownType; - } - if (typeSet.length === 1) { - return typeSet[0]; - } - const id = getTypeListId(typeSet); - let result = intersectionTypes.get(id); - if (!result) { - if (includes & TypeFlags.Union) { - if (intersectUnionsOfPrimitiveTypes(typeSet)) { - // When the intersection creates a reduced set (which might mean that *all* union types have - // disappeared), we restart the operation to get a new set of combined flags. Once we have - // reduced we'll never reduce again, so this occurs at most once. - result = getIntersectionType(typeSet, aliasSymbol, aliasTypeArguments); - } - else if (extractIrreducible(typeSet, TypeFlags.Undefined)) { - result = getUnionType([getIntersectionType(typeSet), undefinedType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); - } - else if (extractIrreducible(typeSet, TypeFlags.Null)) { - result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); - } - else { - // We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of - // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. - // If the estimated size of the resulting union type exceeds 100000 constituents, report an error. - const size = reduceLeft(typeSet, (n, t) => n * (t.flags & TypeFlags.Union ? (t).types.length : 1), 1); - if (size >= 100000) { - error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); - return errorType; - } - const unionIndex = findIndex(typeSet, t => (t.flags & TypeFlags.Union) !== 0); - const unionType = typeSet[unionIndex]; - result = getUnionType(map(unionType.types, t => getIntersectionType(replaceElement(typeSet, unionIndex, t))), - UnionReduction.Literal, aliasSymbol, aliasTypeArguments); - } - } - else { - result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments); - } - intersectionTypes.set(id, result); - } - return result; - } - - function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const aliasSymbol = getAliasSymbolForTypeNode(node); - links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode), - aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol)); - } - return links.resolvedType; - } - - function createIndexType(type: InstantiableType | UnionOrIntersectionType, stringsOnly: boolean) { - const result = createType(TypeFlags.Index); - result.type = type; - result.stringsOnly = stringsOnly; - return result; - } - - function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType, stringsOnly: boolean) { - return stringsOnly ? - type.resolvedStringIndexType || (type.resolvedStringIndexType = createIndexType(type, /*stringsOnly*/ true)) : - type.resolvedIndexType || (type.resolvedIndexType = createIndexType(type, /*stringsOnly*/ false)); - } - - function getLiteralTypeFromPropertyName(name: PropertyName) { - if (isPrivateIdentifier(name)) { - return neverType; - } - return isIdentifier(name) ? getLiteralType(unescapeLeadingUnderscores(name.escapedText)) : - getRegularTypeOfLiteralType(isComputedPropertyName(name) ? checkComputedPropertyName(name) : checkExpression(name)); - } - - function getBigIntLiteralType(node: BigIntLiteral): LiteralType { - return getLiteralType({ - negative: false, - base10Value: parsePseudoBigInt(node.text) - }); - } - - function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags) { - if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) { - let type = getLateBoundSymbol(prop).nameType; - if (!type && !isKnownSymbol(prop)) { - if (prop.escapedName === InternalSymbolName.Default) { - type = getLiteralType("default"); - } - else { - const name = prop.valueDeclaration && getNameOfDeclaration(prop.valueDeclaration) as PropertyName; - type = name && getLiteralTypeFromPropertyName(name) || getLiteralType(symbolName(prop)); - } - } - if (type && type.flags & include) { - return type; - } - } - return neverType; - } - - function getLiteralTypeFromProperties(type: Type, include: TypeFlags) { - return getUnionType(map(getPropertiesOfType(type), p => getLiteralTypeFromProperty(p, include))); - } - - function getNonEnumNumberIndexInfo(type: Type) { - const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number); - return numberIndexInfo !== enumNumberIndexInfo ? numberIndexInfo : undefined; - } - - function getIndexType(type: Type, stringsOnly = keyofStringsOnly, noIndexSignatures?: boolean): Type { - return type.flags & TypeFlags.Union ? getIntersectionType(map((type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) : - type.flags & TypeFlags.Intersection ? getUnionType(map((type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) : - maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(type, stringsOnly) : - getObjectFlags(type) & ObjectFlags.Mapped ? filterType(getConstraintTypeFromMappedType(type), t => !(noIndexSignatures && t.flags & (TypeFlags.Any | TypeFlags.String))) : - type === wildcardType ? wildcardType : - type.flags & TypeFlags.Unknown ? neverType : - type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType : - stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral) : - !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol)]) : - getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) : - getLiteralTypeFromProperties(type, TypeFlags.StringOrNumberLiteralOrUnique); - } - - function getExtractStringType(type: Type) { - if (keyofStringsOnly) { - return type; - } - const extractTypeAlias = getGlobalExtractSymbol(); - return extractTypeAlias ? getTypeAliasInstantiation(extractTypeAlias, [type, stringType]) : stringType; - } - - function getIndexTypeOrString(type: Type): Type { - const indexType = getExtractStringType(getIndexType(type)); - return indexType.flags & TypeFlags.Never ? stringType : indexType; - } - - function getTypeFromTypeOperatorNode(node: TypeOperatorNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - switch (node.operator) { - case SyntaxKind.KeyOfKeyword: - links.resolvedType = getIndexType(getTypeFromTypeNode(node.type)); - break; - case SyntaxKind.UniqueKeyword: - links.resolvedType = node.type.kind === SyntaxKind.SymbolKeyword - ? getESSymbolLikeTypeForNode(walkUpParenthesizedTypes(node.parent)) - : errorType; - break; - case SyntaxKind.ReadonlyKeyword: - links.resolvedType = getTypeFromTypeNode(node.type); - break; - default: - throw Debug.assertNever(node.operator); - } - } - return links.resolvedType; - } - - function createIndexedAccessType(objectType: Type, indexType: Type) { - const type = createType(TypeFlags.IndexedAccess); - type.objectType = objectType; - type.indexType = indexType; - return type; - } - - /** - * Returns if a type is or consists of a JSLiteral object type - * In addition to objects which are directly literals, - * * unions where every element is a jsliteral - * * intersections where at least one element is a jsliteral - * * and instantiable types constrained to a jsliteral - * Should all count as literals and not print errors on access or assignment of possibly existing properties. - * This mirrors the behavior of the index signature propagation, to which this behaves similarly (but doesn't affect assignability or inference). - */ - function isJSLiteralType(type: Type): boolean { - if (noImplicitAny) { - return false; // Flag is meaningless under `noImplicitAny` mode - } - if (getObjectFlags(type) & ObjectFlags.JSLiteral) { - return true; - } - if (type.flags & TypeFlags.Union) { - return every((type as UnionType).types, isJSLiteralType); - } - if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, isJSLiteralType); - } - if (type.flags & TypeFlags.Instantiable) { - return isJSLiteralType(getResolvedBaseConstraint(type)); - } - return false; - } - - function getPropertyNameFromIndex(indexType: Type, accessNode: StringLiteral | Identifier | PrivateIdentifier | ObjectBindingPattern | ArrayBindingPattern | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode | ElementAccessExpression | SyntheticExpression | undefined) { - const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; - return isTypeUsableAsPropertyName(indexType) ? - getPropertyNameFromType(indexType) : - accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? - getPropertyNameForKnownSymbolName(idText((accessExpression.argumentExpression).name)) : - accessNode && isPropertyName(accessNode) ? - // late bound names are handled in the first branch, so here we only need to handle normal names - getPropertyNameForPropertyNameNode(accessNode) : - undefined; - } - - function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) { - const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; - const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode); - if (propName !== undefined) { - const prop = getPropertyOfType(objectType, propName); - if (prop) { - if (accessExpression) { - markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword); - if (isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression))) { - error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, symbolToString(prop)); - return undefined; - } - if (accessFlags & AccessFlags.CacheSymbol) { - getNodeLinks(accessNode!).resolvedSymbol = prop; - } - } - const propType = getTypeOfSymbol(prop); - return accessExpression && getAssignmentTargetKind(accessExpression) !== AssignmentKind.Definite ? - getFlowTypeOfReference(accessExpression, propType) : - propType; - } - if (everyType(objectType, isTupleType) && isNumericLiteralName(propName) && +propName >= 0) { - if (accessNode && everyType(objectType, t => !(t).target.hasRestElement) && !(accessFlags & AccessFlags.NoTupleBoundsCheck)) { - const indexNode = getIndexNodeForAccessExpression(accessNode); - if (isTupleType(objectType)) { - error(indexNode, Diagnostics.Tuple_type_0_of_length_1_has_no_element_at_index_2, - typeToString(objectType), getTypeReferenceArity(objectType), unescapeLeadingUnderscores(propName)); - } - else { - error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType)); - } - } - errorIfWritingToReadonlyIndex(getIndexInfoOfType(objectType, IndexKind.Number)); - return mapType(objectType, t => getRestTypeOfTupleType(t) || undefinedType); - } - } - if (!(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike)) { - if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) { - return objectType; - } - const stringIndexInfo = getIndexInfoOfType(objectType, IndexKind.String); - const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) || stringIndexInfo; - if (indexInfo) { - if (accessFlags & AccessFlags.NoIndexSignatures && indexInfo === stringIndexInfo) { - if (accessExpression) { - error(accessExpression, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(originalObjectType)); - } - return undefined; - } - if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { - const indexNode = getIndexNodeForAccessExpression(accessNode); - error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType)); - return indexInfo.type; - } - errorIfWritingToReadonlyIndex(indexInfo); - return indexInfo.type; - } - if (indexType.flags & TypeFlags.Never) { - return neverType; - } - if (isJSLiteralType(objectType)) { - return anyType; - } - if (accessExpression && !isConstEnumObjectType(objectType)) { - if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports!.has(propName) && (globalThisSymbol.exports!.get(propName)!.flags & SymbolFlags.BlockScoped)) { - error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType)); - } - else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !suppressNoImplicitAnyError) { - if (propName !== undefined && typeHasStaticProperty(propName, objectType)) { - error(accessExpression, Diagnostics.Property_0_is_a_static_member_of_type_1, propName as string, typeToString(objectType)); - } - else if (getIndexTypeOfType(objectType, IndexKind.Number)) { - error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number); - } - else { - let suggestion: string | undefined; - if (propName !== undefined && (suggestion = getSuggestionForNonexistentProperty(propName as string, objectType))) { - if (suggestion !== undefined) { - error(accessExpression.argumentExpression, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName as string, typeToString(objectType), suggestion); - } - } - else { - const suggestion = getSuggestionForNonexistentIndexSignature(objectType, accessExpression, indexType); - if (suggestion !== undefined) { - error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature_Did_you_mean_to_call_1, typeToString(objectType), suggestion); - } - else { - let errorInfo: DiagnosticMessageChain | undefined; - if (indexType.flags & TypeFlags.EnumLiteral) { - errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + typeToString(indexType) + "]", typeToString(objectType)); - } - else if (indexType.flags & TypeFlags.UniqueESSymbol) { - const symbolName = getFullyQualifiedName((indexType as UniqueESSymbolType).symbol, accessExpression); - errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + symbolName + "]", typeToString(objectType)); - } - else if (indexType.flags & TypeFlags.StringLiteral) { - errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as StringLiteralType).value, typeToString(objectType)); - } - else if (indexType.flags & TypeFlags.NumberLiteral) { - errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as NumberLiteralType).value, typeToString(objectType)); - } - else if (indexType.flags & (TypeFlags.Number | TypeFlags.String)) { - errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.No_index_signature_with_a_parameter_of_type_0_was_found_on_type_1, typeToString(indexType), typeToString(objectType)); - } - - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics.Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1, typeToString(fullIndexType), typeToString(objectType) - ); - diagnostics.add(createDiagnosticForNodeFromMessageChain(accessExpression, errorInfo)); - } - } - } - } - return undefined; - } - } - if (isJSLiteralType(objectType)) { - return anyType; - } - if (accessNode) { - const indexNode = getIndexNodeForAccessExpression(accessNode); - if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { - error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, "" + (indexType).value, typeToString(objectType)); - } - else if (indexType.flags & (TypeFlags.String | TypeFlags.Number)) { - error(indexNode, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, typeToString(objectType), typeToString(indexType)); - } - else { - error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType)); - } - } - if (isTypeAny(indexType)) { - return indexType; - } - return undefined; - - function errorIfWritingToReadonlyIndex(indexInfo: IndexInfo | undefined): void { - if (indexInfo && indexInfo.isReadonly && accessExpression && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) { - error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType)); - } - } - } - - function getIndexNodeForAccessExpression(accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression) { - return accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode.argumentExpression : - accessNode.kind === SyntaxKind.IndexedAccessType ? accessNode.indexType : - accessNode.kind === SyntaxKind.ComputedPropertyName ? accessNode.expression : - accessNode; - } - - function isGenericObjectType(type: Type): boolean { - return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.GenericMappedType); - } - - function isGenericIndexType(type: Type): boolean { - return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.Index); - } - - function isThisTypeParameter(type: Type): boolean { - return !!(type.flags & TypeFlags.TypeParameter && (type).isThisType); - } - - function getSimplifiedType(type: Type, writing: boolean): Type { - return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type, writing) : - type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type, writing) : - type; - } - - function distributeIndexOverObjectType(objectType: Type, indexType: Type, writing: boolean) { - // (T | U)[K] -> T[K] | U[K] (reading) - // (T | U)[K] -> T[K] & U[K] (writing) - // (T & U)[K] -> T[K] & U[K] - if (objectType.flags & TypeFlags.UnionOrIntersection) { - const types = map((objectType as UnionOrIntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType), writing)); - return objectType.flags & TypeFlags.Intersection || writing ? getIntersectionType(types) : getUnionType(types); - } - } - - function distributeObjectOverIndexType(objectType: Type, indexType: Type, writing: boolean) { - // T[A | B] -> T[A] | T[B] (reading) - // T[A | B] -> T[A] & T[B] (writing) - if (indexType.flags & TypeFlags.Union) { - const types = map((indexType as UnionType).types, t => getSimplifiedType(getIndexedAccessType(objectType, t), writing)); - return writing ? getIntersectionType(types) : getUnionType(types); - } - } - - // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return - // the type itself if no transformation is possible. The writing flag indicates that the type is - // the target of an assignment. - function getSimplifiedIndexedAccessType(type: IndexedAccessType, writing: boolean): Type { - const cache = writing ? "simplifiedForWriting" : "simplifiedForReading"; - if (type[cache]) { - return type[cache] === circularConstraintType ? type : type[cache]!; - } - type[cache] = circularConstraintType; - // We recursively simplify the object type as it may in turn be an indexed access type. For example, with - // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - const objectType = getSimplifiedType(type.objectType, writing); - const indexType = getSimplifiedType(type.indexType, writing); - // T[A | B] -> T[A] | T[B] (reading) - // T[A | B] -> T[A] & T[B] (writing) - const distributedOverIndex = distributeObjectOverIndexType(objectType, indexType, writing); - if (distributedOverIndex) { - return type[cache] = distributedOverIndex; - } - // Only do the inner distributions if the index can no longer be instantiated to cause index distribution again - if (!(indexType.flags & TypeFlags.Instantiable)) { - // (T | U)[K] -> T[K] | U[K] (reading) - // (T | U)[K] -> T[K] & U[K] (writing) - // (T & U)[K] -> T[K] & U[K] - const distributedOverObject = distributeIndexOverObjectType(objectType, indexType, writing); - if (distributedOverObject) { - return type[cache] = distributedOverObject; - } - } - // So ultimately (reading): - // ((A & B) | C)[K1 | K2] -> ((A & B) | C)[K1] | ((A & B) | C)[K2] -> (A & B)[K1] | C[K1] | (A & B)[K2] | C[K2] -> (A[K1] & B[K1]) | C[K1] | (A[K2] & B[K2]) | C[K2] - - // If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper - // that substitutes the index type for P. For example, for an index access { [P in K]: Box }[X], we - // construct the type Box. - if (isGenericMappedType(objectType)) { - return type[cache] = mapType(substituteIndexedMappedType(objectType, type.indexType), t => getSimplifiedType(t, writing)); - } - return type[cache] = type; - } - - function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) { - const checkType = type.checkType; - const extendsType = type.extendsType; - const trueType = getTrueTypeFromConditionalType(type); - const falseType = getFalseTypeFromConditionalType(type); - // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. - if (falseType.flags & TypeFlags.Never && getActualTypeVariable(trueType) === getActualTypeVariable(checkType)) { - if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return getSimplifiedType(trueType, writing); - } - else if (isIntersectionEmpty(checkType, extendsType)) { // Always false - return neverType; - } - } - else if (trueType.flags & TypeFlags.Never && getActualTypeVariable(falseType) === getActualTypeVariable(checkType)) { - if (!(checkType.flags & TypeFlags.Any) && isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return neverType; - } - else if (checkType.flags & TypeFlags.Any || isIntersectionEmpty(checkType, extendsType)) { // Always false - return getSimplifiedType(falseType, writing); - } - } - return type; - } - - /** - * Invokes union simplification logic to determine if an intersection is considered empty as a union constituent - */ - function isIntersectionEmpty(type1: Type, type2: Type) { - return !!(getUnionType([intersectTypes(type1, type2), neverType]).flags & TypeFlags.Never); - } - - function substituteIndexedMappedType(objectType: MappedType, index: Type) { - const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [index]); - const templateMapper = combineTypeMappers(objectType.mapper, mapper); - return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper); - } - - function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression): Type { - return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, AccessFlags.None) || (accessNode ? errorType : unknownType); - } - - function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, accessFlags = AccessFlags.None): Type | undefined { - if (objectType === wildcardType || indexType === wildcardType) { - return wildcardType; - } - // If the object type has a string index signature and no other members we know that the result will - // always be the type of that index signature and we can simplify accordingly. - if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { - indexType = stringType; - } - // If the index type is generic, or if the object type is generic and doesn't originate in an expression, - // we are performing a higher-order index access where we cannot meaningfully access the properties of the - // object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in - // an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]' - // has always been resolved eagerly using the constraint type of 'this' at the given location. - if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind !== SyntaxKind.IndexedAccessType) && isGenericObjectType(objectType)) { - if (objectType.flags & TypeFlags.AnyOrUnknown) { - return objectType; - } - // Defer the operation by creating an indexed access type. - const id = objectType.id + "," + indexType.id; - let type = indexedAccessTypes.get(id); - if (!type) { - indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType)); - } - return type; - } - // In the following we resolve T[K] to the type of the property in T selected by K. - // We treat boolean as different from other unions to improve errors; - // skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'. - const apparentObjectType = getApparentType(objectType); - if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Boolean)) { - const propTypes: Type[] = []; - let wasMissingProp = false; - for (const t of (indexType).types) { - const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, wasMissingProp, accessNode, accessFlags); - if (propType) { - propTypes.push(propType); - } - else if (!accessNode) { - // If there's no error node, we can immeditely stop, since error reporting is off - return undefined; - } - else { - // Otherwise we set a flag and return at the end of the loop so we still mark all errors - wasMissingProp = true; - } - } - if (wasMissingProp) { - return undefined; - } - return accessFlags & AccessFlags.Writing ? getIntersectionType(propTypes) : getUnionType(propTypes); - } - return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol); - } - - function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const objectType = getTypeFromTypeNode(node.objectType); - const indexType = getTypeFromTypeNode(node.indexType); - const resolved = getIndexedAccessType(objectType, indexType, node); - links.resolvedType = resolved.flags & TypeFlags.IndexedAccess && - (resolved).objectType === objectType && - (resolved).indexType === indexType ? - getConstrainedTypeVariable(resolved, node) : resolved; - } - return links.resolvedType; - } - - function getTypeFromMappedTypeNode(node: MappedTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const type = createObjectType(ObjectFlags.Mapped, node.symbol); - type.declaration = node; - type.aliasSymbol = getAliasSymbolForTypeNode(node); - type.aliasTypeArguments = getTypeArgumentsForAliasSymbol(type.aliasSymbol); - links.resolvedType = type; - // Eagerly resolve the constraint type which forces an error if the constraint type circularly - // references itself through one or more type aliases. - getConstraintTypeFromMappedType(type); - } - return links.resolvedType; - } - - function getActualTypeVariable(type: Type): Type { - if (type.flags & TypeFlags.Substitution) { - return (type).typeVariable; - } - if (type.flags & TypeFlags.IndexedAccess && ( - (type).objectType.flags & TypeFlags.Substitution || - (type).indexType.flags & TypeFlags.Substitution)) { - return getIndexedAccessType(getActualTypeVariable((type).objectType), getActualTypeVariable((type).indexType)); - } - return type; - } - - function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type { - const checkType = instantiateType(root.checkType, mapper); - const extendsType = instantiateType(root.extendsType, mapper); - if (checkType === wildcardType || extendsType === wildcardType) { - return wildcardType; - } - const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); - let combinedMapper: TypeMapper | undefined; - if (root.inferTypeParameters) { - const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); - if (!checkTypeInstantiable) { - // We don't want inferences from constraints as they may cause us to eagerly resolve the - // conditional type instead of deferring resolution. Also, we always want strict function - // types rules (i.e. proper contravariance) for inferences. - inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); - } - combinedMapper = combineTypeMappers(mapper, context.mapper); - } - // Instantiate the extends type including inferences for 'infer T' type parameters - const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; - // We attempt to resolve the conditional type only when the check and extends types are non-generic - if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable | TypeFlags.GenericMappedType)) { - if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { - return instantiateType(root.trueType, combinedMapper || mapper); - } - // Return union of trueType and falseType for 'any' since it matches anything - if (checkType.flags & TypeFlags.Any) { - return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]); - } - // Return falseType for a definitely false extends check. We check an instantiations of the two - // types with type parameters mapped to the wildcard type, the most permissive instantiations - // possible (the wildcard type is assignable to and from all types). If those are not related, - // then no instantiations will be and we can just return the false branch type. - if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { - return instantiateType(root.falseType, mapper); - } - // Return trueType for a definitely true extends check. We check instantiations of the two - // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter - // that has no constraint. This ensures that, for example, the type - // type Foo = T extends { x: string } ? string : number - // doesn't immediately resolve to 'string' instead of being deferred. - if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { - return instantiateType(root.trueType, combinedMapper || mapper); - } - } - // Return a deferred type for a check that is neither definitely true nor definitely false - const erasedCheckType = getActualTypeVariable(checkType); - const result = createType(TypeFlags.Conditional); - result.root = root; - result.checkType = erasedCheckType; - result.extendsType = extendsType; - result.mapper = mapper; - result.combinedMapper = combinedMapper; - result.aliasSymbol = root.aliasSymbol; - result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217 - return result; - } - - function getTrueTypeFromConditionalType(type: ConditionalType) { - return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(type.root.trueType, type.mapper)); - } - - function getFalseTypeFromConditionalType(type: ConditionalType) { - return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper)); - } - - function getInferredTrueTypeFromConditionalType(type: ConditionalType) { - return type.resolvedInferredTrueType || (type.resolvedInferredTrueType = type.combinedMapper ? instantiateType(type.root.trueType, type.combinedMapper) : getTrueTypeFromConditionalType(type)); - } - - function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] | undefined { - let result: TypeParameter[] | undefined; - if (node.locals) { - node.locals.forEach(symbol => { - if (symbol.flags & SymbolFlags.TypeParameter) { - result = append(result, getDeclaredTypeOfSymbol(symbol)); - } - }); - } - return result; - } - - function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const checkType = getTypeFromTypeNode(node.checkType); - const aliasSymbol = getAliasSymbolForTypeNode(node); - const aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); - const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true); - const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, node)); - const root: ConditionalRoot = { - node, - checkType, - extendsType: getTypeFromTypeNode(node.extendsType), - trueType: getTypeFromTypeNode(node.trueType), - falseType: getTypeFromTypeNode(node.falseType), - isDistributive: !!(checkType.flags & TypeFlags.TypeParameter), - inferTypeParameters: getInferTypeParameters(node), - outerTypeParameters, - instantiations: undefined, - aliasSymbol, - aliasTypeArguments - }; - links.resolvedType = getConditionalType(root, /*mapper*/ undefined); - if (outerTypeParameters) { - root.instantiations = createMap(); - root.instantiations.set(getTypeListId(outerTypeParameters), links.resolvedType); - } - } - return links.resolvedType; - } - - function getTypeFromInferTypeNode(node: InferTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - links.resolvedType = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); - } - return links.resolvedType; - } - - function getIdentifierChain(node: EntityName): Identifier[] { - if (isIdentifier(node)) { - return [node]; - } - else { - return append(getIdentifierChain(node.left), node.right); - } - } - - function getTypeFromImportTypeNode(node: ImportTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - if (node.isTypeOf && node.typeArguments) { // Only the non-typeof form can make use of type arguments - error(node, Diagnostics.Type_arguments_cannot_be_used_here); - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } - if (!isLiteralImportTypeNode(node)) { - error(node.argument, Diagnostics.String_literal_expected); - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } - const targetMeaning = node.isTypeOf ? SymbolFlags.Value : node.flags & NodeFlags.JSDoc ? SymbolFlags.Value | SymbolFlags.Type : SymbolFlags.Type; - // TODO: Future work: support unions/generics/whatever via a deferred import-type - const innerModuleSymbol = resolveExternalModuleName(node, node.argument.literal); - if (!innerModuleSymbol) { - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } - const moduleSymbol = resolveExternalModuleSymbol(innerModuleSymbol, /*dontResolveAlias*/ false); - if (!nodeIsMissing(node.qualifier)) { - const nameStack: Identifier[] = getIdentifierChain(node.qualifier!); - let currentNamespace = moduleSymbol; - let current: Identifier | undefined; - while (current = nameStack.shift()) { - const meaning = nameStack.length ? SymbolFlags.Namespace : targetMeaning; - const next = getSymbol(getExportsOfSymbol(getMergedSymbol(resolveSymbol(currentNamespace))), current.escapedText, meaning); - if (!next) { - error(current, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(currentNamespace), declarationNameToString(current)); - return links.resolvedType = errorType; - } - getNodeLinks(current).resolvedSymbol = next; - getNodeLinks(current.parent).resolvedSymbol = next; - currentNamespace = next; - } - links.resolvedType = resolveImportSymbolType(node, links, currentNamespace, targetMeaning); - } - else { - if (moduleSymbol.flags & targetMeaning) { - links.resolvedType = resolveImportSymbolType(node, links, moduleSymbol, targetMeaning); - } - else { - const errorMessage = targetMeaning === SymbolFlags.Value - ? Diagnostics.Module_0_does_not_refer_to_a_value_but_is_used_as_a_value_here - : Diagnostics.Module_0_does_not_refer_to_a_type_but_is_used_as_a_type_here_Did_you_mean_typeof_import_0; - - error(node, errorMessage, node.argument.literal.text); - - links.resolvedSymbol = unknownSymbol; - links.resolvedType = errorType; - } - } - } - return links.resolvedType; - } - - function resolveImportSymbolType(node: ImportTypeNode, links: NodeLinks, symbol: Symbol, meaning: SymbolFlags) { - const resolvedSymbol = resolveSymbol(symbol); - links.resolvedSymbol = resolvedSymbol; - if (meaning === SymbolFlags.Value) { - return getTypeOfSymbol(symbol); // intentionally doesn't use resolved symbol so type is cached as expected on the alias - } - else { - return getTypeReferenceType(node, resolvedSymbol); // getTypeReferenceType doesn't handle aliases - it must get the resolved symbol - } - } - - function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: TypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - // Deferred resolution of members is handled by resolveObjectTypeMembers - const aliasSymbol = getAliasSymbolForTypeNode(node); - if (getMembersOfSymbol(node.symbol).size === 0 && !aliasSymbol) { - links.resolvedType = emptyTypeLiteralType; - } - else { - let type = createObjectType(ObjectFlags.Anonymous, node.symbol); - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); - if (isJSDocTypeLiteral(node) && node.isArrayType) { - type = createArrayType(type); - } - links.resolvedType = type; - } - } - return links.resolvedType; - } - - function getAliasSymbolForTypeNode(node: TypeNode) { - let host = node.parent; - while (isParenthesizedTypeNode(host) || isTypeOperatorNode(host) && host.operator === SyntaxKind.ReadonlyKeyword) { - host = host.parent; - } - return isTypeAlias(host) ? getSymbolOfNode(host) : undefined; - } - - function getTypeArgumentsForAliasSymbol(symbol: Symbol | undefined) { - return symbol ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined; - } - - function isNonGenericObjectType(type: Type) { - return !!(type.flags & TypeFlags.Object) && !isGenericMappedType(type); - } - - function isEmptyObjectTypeOrSpreadsIntoEmptyObject(type: Type) { - return isEmptyObjectType(type) || !!(type.flags & (TypeFlags.Null | TypeFlags.Undefined | TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)); - } - - function isSinglePropertyAnonymousObjectType(type: Type) { - return !!(type.flags & TypeFlags.Object) && - !!(getObjectFlags(type) & ObjectFlags.Anonymous) && - (length(getPropertiesOfType(type)) === 1 || every(getPropertiesOfType(type), p => !!(p.flags & SymbolFlags.Optional))); - } - - function tryMergeUnionOfObjectTypeAndEmptyObject(type: UnionType, readonly: boolean): Type | undefined { - if (type.types.length === 2) { - const firstType = type.types[0]; - const secondType = type.types[1]; - if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) { - return isEmptyObjectType(firstType) ? firstType : isEmptyObjectType(secondType) ? secondType : emptyObjectType; - } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(firstType) && isSinglePropertyAnonymousObjectType(secondType)) { - return getAnonymousPartialType(secondType); - } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(secondType) && isSinglePropertyAnonymousObjectType(firstType)) { - return getAnonymousPartialType(firstType); - } - } - - function getAnonymousPartialType(type: Type) { - // gets the type as if it had been spread, but where everything in the spread is made optional - const members = createSymbolTable(); - for (const prop of getPropertiesOfType(type)) { - if (getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected)) { - // do nothing, skip privates - } - else if (isSpreadableProperty(prop)) { - const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); - const flags = SymbolFlags.Property | SymbolFlags.Optional; - const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0); - result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); - result.declarations = prop.declarations; - result.nameType = prop.nameType; - result.syntheticOrigin = prop; - members.set(prop.escapedName, result); - } - } - const spread = createAnonymousType( - type.symbol, - members, - emptyArray, - emptyArray, - getIndexInfoOfType(type, IndexKind.String), - getIndexInfoOfType(type, IndexKind.Number)); - spread.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral; - return spread; - } - } - - /** - * Since the source of spread types are object literals, which are not binary, - * this function should be called in a left folding style, with left = previous result of getSpreadType - * and right = the new element to be spread. - */ - function getSpreadType(left: Type, right: Type, symbol: Symbol | undefined, objectFlags: ObjectFlags, readonly: boolean): Type { - if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) { - return anyType; - } - if (left.flags & TypeFlags.Unknown || right.flags & TypeFlags.Unknown) { - return unknownType; - } - if (left.flags & TypeFlags.Never) { - return right; - } - if (right.flags & TypeFlags.Never) { - return left; - } - if (left.flags & TypeFlags.Union) { - const merged = tryMergeUnionOfObjectTypeAndEmptyObject(left as UnionType, readonly); - if (merged) { - return getSpreadType(merged, right, symbol, objectFlags, readonly); - } - return mapType(left, t => getSpreadType(t, right, symbol, objectFlags, readonly)); - } - if (right.flags & TypeFlags.Union) { - const merged = tryMergeUnionOfObjectTypeAndEmptyObject(right as UnionType, readonly); - if (merged) { - return getSpreadType(left, merged, symbol, objectFlags, readonly); - } - return mapType(right, t => getSpreadType(left, t, symbol, objectFlags, readonly)); - } - if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)) { - return left; - } - - if (isGenericObjectType(left) || isGenericObjectType(right)) { - if (isEmptyObjectType(left)) { - return right; - } - // When the left type is an intersection, we may need to merge the last constituent of the - // intersection with the right type. For example when the left type is 'T & { a: string }' - // and the right type is '{ b: string }' we produce 'T & { a: string, b: string }'. - if (left.flags & TypeFlags.Intersection) { - const types = (left).types; - const lastLeft = types[types.length - 1]; - if (isNonGenericObjectType(lastLeft) && isNonGenericObjectType(right)) { - return getIntersectionType(concatenate(types.slice(0, types.length - 1), [getSpreadType(lastLeft, right, symbol, objectFlags, readonly)])); - } - } - return getIntersectionType([left, right]); - } - - const members = createSymbolTable(); - const skippedPrivateMembers = createUnderscoreEscapedMap(); - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - if (left === emptyObjectType) { - // for the first spread element, left === emptyObjectType, so take the right's string indexer - stringIndexInfo = getIndexInfoOfType(right, IndexKind.String); - numberIndexInfo = getIndexInfoOfType(right, IndexKind.Number); - } - else { - stringIndexInfo = unionSpreadIndexInfos(getIndexInfoOfType(left, IndexKind.String), getIndexInfoOfType(right, IndexKind.String)); - numberIndexInfo = unionSpreadIndexInfos(getIndexInfoOfType(left, IndexKind.Number), getIndexInfoOfType(right, IndexKind.Number)); - } - - for (const rightProp of getPropertiesOfType(right)) { - if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) { - skippedPrivateMembers.set(rightProp.escapedName, true); - } - else if (isSpreadableProperty(rightProp)) { - members.set(rightProp.escapedName, getSpreadSymbol(rightProp, readonly)); - } - } - - for (const leftProp of getPropertiesOfType(left)) { - if (skippedPrivateMembers.has(leftProp.escapedName) || !isSpreadableProperty(leftProp)) { - continue; - } - if (members.has(leftProp.escapedName)) { - const rightProp = members.get(leftProp.escapedName)!; - const rightType = getTypeOfSymbol(rightProp); - if (rightProp.flags & SymbolFlags.Optional) { - const declarations = concatenate(leftProp.declarations, rightProp.declarations); - const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional); - const result = createSymbol(flags, leftProp.escapedName); - result.type = getUnionType([getTypeOfSymbol(leftProp), getTypeWithFacts(rightType, TypeFacts.NEUndefined)]); - result.leftSpread = leftProp; - result.rightSpread = rightProp; - result.declarations = declarations; - result.nameType = leftProp.nameType; - members.set(leftProp.escapedName, result); - } - } - else { - members.set(leftProp.escapedName, getSpreadSymbol(leftProp, readonly)); - } - } - - const spread = createAnonymousType( - symbol, - members, - emptyArray, - emptyArray, - getIndexInfoWithReadonly(stringIndexInfo, readonly), - getIndexInfoWithReadonly(numberIndexInfo, readonly)); - spread.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral | ObjectFlags.ContainsSpread | objectFlags; - return spread; - } - - /** We approximate own properties as non-methods plus methods that are inside the object literal */ - function isSpreadableProperty(prop: Symbol): boolean { - return !(prop.flags & (SymbolFlags.Method | SymbolFlags.GetAccessor | SymbolFlags.SetAccessor)) || - !prop.declarations.some(decl => isClassLike(decl.parent)); - } - - function getSpreadSymbol(prop: Symbol, readonly: boolean) { - const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); - if (!isSetonlyAccessor && readonly === isReadonlySymbol(prop)) { - return prop; - } - const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional); - const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0); - result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); - result.declarations = prop.declarations; - result.nameType = prop.nameType; - result.syntheticOrigin = prop; - return result; - } - - function getIndexInfoWithReadonly(info: IndexInfo | undefined, readonly: boolean) { - return info && info.isReadonly !== readonly ? createIndexInfo(info.type, readonly, info.declaration) : info; - } - - function createLiteralType(flags: TypeFlags, value: string | number | PseudoBigInt, symbol: Symbol | undefined) { - const type = createType(flags); - type.symbol = symbol!; - type.value = value; - return type; - } - - function getFreshTypeOfLiteralType(type: Type): Type { - if (type.flags & TypeFlags.Literal) { - if (!(type).freshType) { - const freshType = createLiteralType(type.flags, (type).value, (type).symbol); - freshType.regularType = type; - freshType.freshType = freshType; - (type).freshType = freshType; - } - return (type).freshType; - } - return type; - } - - function getRegularTypeOfLiteralType(type: Type): Type { - return type.flags & TypeFlags.Literal ? (type).regularType : - type.flags & TypeFlags.Union ? getUnionType(sameMap((type).types, getRegularTypeOfLiteralType)) : - type; - } - - function isFreshLiteralType(type: Type) { - return !!(type.flags & TypeFlags.Literal) && (type).freshType === type; - } - - function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol) { - // We store all literal types in a single map with keys of the form '#NNN' and '@SSS', - // where NNN is the text representation of a numeric literal and SSS are the characters - // of a string literal. For literal enum members we use 'EEE#NNN' and 'EEE@SSS', where - // EEE is a unique id for the containing enum type. - const qualifier = typeof value === "number" ? "#" : typeof value === "string" ? "@" : "n"; - const key = (enumId ? enumId : "") + qualifier + (typeof value === "object" ? pseudoBigIntToString(value) : value); - let type = literalTypes.get(key); - if (!type) { - const flags = (typeof value === "number" ? TypeFlags.NumberLiteral : - typeof value === "string" ? TypeFlags.StringLiteral : TypeFlags.BigIntLiteral) | - (enumId ? TypeFlags.EnumLiteral : 0); - literalTypes.set(key, type = createLiteralType(flags, value, symbol)); - type.regularType = type; - } - return type; - } - - function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - links.resolvedType = getRegularTypeOfLiteralType(checkExpression(node.literal)); - } - return links.resolvedType; - } - - function createUniqueESSymbolType(symbol: Symbol) { - const type = createType(TypeFlags.UniqueESSymbol); - type.symbol = symbol; - type.escapedName = `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String; - return type; - } - - function getESSymbolLikeTypeForNode(node: Node) { - if (isValidESSymbolDeclaration(node)) { - const symbol = getSymbolOfNode(node); - const links = getSymbolLinks(symbol); - return links.uniqueESSymbolType || (links.uniqueESSymbolType = createUniqueESSymbolType(symbol)); - } - return esSymbolType; - } - - function getThisType(node: Node): Type { - const container = getThisContainer(node, /*includeArrowFunctions*/ false); - const parent = container && container.parent; - if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) { - if (!hasModifier(container, ModifierFlags.Static) && - (!isConstructorDeclaration(container) || isNodeDescendantOf(node, container.body))) { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent as ClassLikeDeclaration | InterfaceDeclaration)).thisType!; - } - } - - // inside x.prototype = { ... } - if (parent && isObjectLiteralExpression(parent) && isBinaryExpression(parent.parent) && getAssignmentDeclarationKind(parent.parent) === AssignmentDeclarationKind.Prototype) { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent.parent.left)!.parent!).thisType!; - } - // /** @return {this} */ - // x.prototype.m = function() { ... } - const host = node.flags & NodeFlags.JSDoc ? getHostSignatureFromJSDoc(node) : undefined; - if (host && isFunctionExpression(host) && isBinaryExpression(host.parent) && getAssignmentDeclarationKind(host.parent) === AssignmentDeclarationKind.PrototypeProperty) { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(host.parent.left)!.parent!).thisType!; - } - // inside constructor function C() { ... } - if (isJSConstructor(container) && isNodeDescendantOf(node, container.body)) { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(container)).thisType!; - } - error(node, Diagnostics.A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface); - return errorType; - } - - function getTypeFromThisTypeNode(node: ThisExpression | ThisTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - links.resolvedType = getThisType(node); - } - return links.resolvedType; - } - - function getTypeFromTypeNode(node: TypeNode): Type { - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - return anyType; - case SyntaxKind.UnknownKeyword: - return unknownType; - case SyntaxKind.StringKeyword: - return stringType; - case SyntaxKind.NumberKeyword: - return numberType; - case SyntaxKind.BigIntKeyword: - return bigintType; - case SyntaxKind.BooleanKeyword: - return booleanType; - case SyntaxKind.SymbolKeyword: - return esSymbolType; - case SyntaxKind.VoidKeyword: - return voidType; - case SyntaxKind.UndefinedKeyword: - return undefinedType; - case SyntaxKind.NullKeyword: - return nullType; - case SyntaxKind.NeverKeyword: - return neverType; - case SyntaxKind.ObjectKeyword: - return node.flags & NodeFlags.JavaScriptFile && !noImplicitAny ? anyType : nonPrimitiveType; - case SyntaxKind.ThisType: - case SyntaxKind.ThisKeyword: - return getTypeFromThisTypeNode(node as ThisExpression | ThisTypeNode); - case SyntaxKind.LiteralType: - return getTypeFromLiteralTypeNode(node); - case SyntaxKind.TypeReference: - return getTypeFromTypeReference(node); - case SyntaxKind.TypePredicate: - return (node).assertsModifier ? voidType : booleanType; - case SyntaxKind.ExpressionWithTypeArguments: - return getTypeFromTypeReference(node); - case SyntaxKind.TypeQuery: - return getTypeFromTypeQueryNode(node); - case SyntaxKind.ArrayType: - case SyntaxKind.TupleType: - return getTypeFromArrayOrTupleTypeNode(node); - case SyntaxKind.OptionalType: - return getTypeFromOptionalTypeNode(node); - case SyntaxKind.UnionType: - return getTypeFromUnionTypeNode(node); - case SyntaxKind.IntersectionType: - return getTypeFromIntersectionTypeNode(node); - case SyntaxKind.JSDocNullableType: - return getTypeFromJSDocNullableTypeNode(node); - case SyntaxKind.JSDocOptionalType: - return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType).type)); - case SyntaxKind.ParenthesizedType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocTypeExpression: - return getTypeFromTypeNode((node).type); - case SyntaxKind.RestType: - return getElementTypeOfArrayType(getTypeFromTypeNode((node).type)) || errorType; - case SyntaxKind.JSDocVariadicType: - return getTypeFromJSDocVariadicType(node as JSDocVariadicType); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.JSDocSignature: - return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); - case SyntaxKind.TypeOperator: - return getTypeFromTypeOperatorNode(node); - case SyntaxKind.IndexedAccessType: - return getTypeFromIndexedAccessTypeNode(node); - case SyntaxKind.MappedType: - return getTypeFromMappedTypeNode(node); - case SyntaxKind.ConditionalType: - return getTypeFromConditionalTypeNode(node); - case SyntaxKind.InferType: - return getTypeFromInferTypeNode(node); - case SyntaxKind.ImportType: - return getTypeFromImportTypeNode(node); - // This function assumes that an identifier or qualified name is a type expression - // Callers should first ensure this by calling isTypeNode - case SyntaxKind.Identifier: - case SyntaxKind.QualifiedName: - const symbol = getSymbolAtLocation(node); - return symbol ? getDeclaredTypeOfSymbol(symbol) : errorType; - default: - return errorType; - } - } - - function instantiateList(items: readonly T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): readonly T[]; - function instantiateList(items: readonly T[] | undefined, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): readonly T[] | undefined; - function instantiateList(items: readonly T[] | undefined, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): readonly T[] | undefined { - if (items && items.length) { - for (let i = 0; i < items.length; i++) { - const item = items[i]; - const mapped = instantiator(item, mapper); - if (item !== mapped) { - const result = i === 0 ? [] : items.slice(0, i); - result.push(mapped); - for (i++; i < items.length; i++) { - result.push(instantiator(items[i], mapper)); - } - return result; - } - } - } - return items; - } - - function instantiateTypes(types: readonly Type[], mapper: TypeMapper): readonly Type[]; - function instantiateTypes(types: readonly Type[] | undefined, mapper: TypeMapper): readonly Type[] | undefined; - function instantiateTypes(types: readonly Type[] | undefined, mapper: TypeMapper): readonly Type[] | undefined { - return instantiateList(types, mapper, instantiateType); - } - - function instantiateSignatures(signatures: readonly Signature[], mapper: TypeMapper): readonly Signature[] { - return instantiateList(signatures, mapper, instantiateSignature); - } - - function makeUnaryTypeMapper(source: Type, target: Type) { - return (t: Type) => t === source ? target : t; - } - - function makeBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type) { - return (t: Type) => t === source1 ? target1 : t === source2 ? target2 : t; - } - - function makeArrayTypeMapper(sources: readonly Type[], targets: readonly Type[] | undefined) { - return (t: Type) => { - for (let i = 0; i < sources.length; i++) { - if (t === sources[i]) { - return targets ? targets[i] : anyType; - } - } - return t; - }; - } - - function createTypeMapper(sources: readonly TypeParameter[], targets: readonly Type[] | undefined): TypeMapper { - Debug.assert(targets === undefined || sources.length === targets.length); - return sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) : - sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) : - makeArrayTypeMapper(sources, targets); - } - - function createTypeEraser(sources: readonly TypeParameter[]): TypeMapper { - return createTypeMapper(sources, /*targets*/ undefined); - } - - /** - * Maps forward-references to later types parameters to the empty object type. - * This is used during inference when instantiating type parameter defaults. - */ - function createBackreferenceMapper(context: InferenceContext, index: number): TypeMapper { - return t => findIndex(context.inferences, info => info.typeParameter === t) >= index ? unknownType : t; - } - - function combineTypeMappers(mapper1: TypeMapper | undefined, mapper2: TypeMapper): TypeMapper; - function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper | undefined): TypeMapper; - function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper { - if (!mapper1) return mapper2; - if (!mapper2) return mapper1; - return t => instantiateType(mapper1(t), mapper2); - } - - function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper): TypeMapper { - return t => t === source ? target : baseMapper(t); - } - - function permissiveMapper(type: Type) { - return type.flags & TypeFlags.TypeParameter ? wildcardType : type; - } - - function getRestrictiveTypeParameter(tp: TypeParameter) { - return tp.constraint === unknownType ? tp : tp.restrictiveInstantiation || ( - tp.restrictiveInstantiation = createTypeParameter(tp.symbol), - (tp.restrictiveInstantiation as TypeParameter).constraint = unknownType, - tp.restrictiveInstantiation - ); - } - - function restrictiveMapper(type: Type) { - return type.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(type) : type; - } - - function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter { - const result = createTypeParameter(typeParameter.symbol); - result.target = typeParameter; - return result; - } - - function instantiateTypePredicate(predicate: TypePredicate, mapper: TypeMapper): TypePredicate { - return createTypePredicate(predicate.kind, predicate.parameterName, predicate.parameterIndex, instantiateType(predicate.type, mapper)); - } - - function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature { - let freshTypeParameters: TypeParameter[] | undefined; - if (signature.typeParameters && !eraseTypeParameters) { - // First create a fresh set of type parameters, then include a mapping from the old to the - // new type parameters in the mapper function. Finally store this mapper in the new type - // parameters such that we can use it when instantiating constraints. - freshTypeParameters = map(signature.typeParameters, cloneTypeParameter); - mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper); - for (const tp of freshTypeParameters) { - tp.mapper = mapper; - } - } - // Don't compute resolvedReturnType and resolvedTypePredicate now, - // because using `mapper` now could trigger inferences to become fixed. (See `createInferenceContext`.) - // See GH#17600. - const result = createSignature(signature.declaration, freshTypeParameters, - signature.thisParameter && instantiateSymbol(signature.thisParameter, mapper), - instantiateList(signature.parameters, mapper, instantiateSymbol), - /*resolvedReturnType*/ undefined, - /*resolvedTypePredicate*/ undefined, - signature.minArgumentCount, - signature.flags & SignatureFlags.PropagatingFlags); - result.target = signature; - result.mapper = mapper; - return result; - } - - function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol { - const links = getSymbolLinks(symbol); - if (links.type && !maybeTypeOfKind(links.type, TypeFlags.Object | TypeFlags.Instantiable)) { - // If the type of the symbol is already resolved, and if that type could not possibly - // be affected by instantiation, simply return the symbol itself. - return symbol; - } - if (getCheckFlags(symbol) & CheckFlags.Instantiated) { - // If symbol being instantiated is itself a instantiation, fetch the original target and combine the - // type mappers. This ensures that original type identities are properly preserved and that aliases - // always reference a non-aliases. - symbol = links.target!; - mapper = combineTypeMappers(links.mapper, mapper); - } - // Keep the flags from the symbol we're instantiating. Mark that is instantiated, and - // also transient so that we can just store data on it directly. - const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Readonly | CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter)); - result.declarations = symbol.declarations; - result.parent = symbol.parent; - result.target = symbol; - result.mapper = mapper; - if (symbol.valueDeclaration) { - result.valueDeclaration = symbol.valueDeclaration; - } - if (symbol.nameType) { - result.nameType = symbol.nameType; - } - return result; - } - - function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) { - const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; - const node = type.objectFlags & ObjectFlags.Reference ? (type).node! : type.symbol.declarations[0]; - const links = getNodeLinks(node); - let typeParameters = links.outerTypeParameters; - if (!typeParameters) { - // The first time an anonymous type is instantiated we compute and store a list of the type - // parameters that are in scope (and therefore potentially referenced). For type literals that - // aren't the right hand side of a generic type alias declaration we optimize by reducing the - // set of type parameters to those that are possibly referenced in the literal. - let declaration = node; - if (isInJSFile(declaration)) { - const paramTag = findAncestor(declaration, isJSDocParameterTag); - if (paramTag) { - const paramSymbol = getParameterSymbolFromJSDoc(paramTag); - if (paramSymbol) { - declaration = paramSymbol.valueDeclaration; - } - } - } - let outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true); - if (isJSConstructor(declaration)) { - const templateTagParameters = getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters); - outerTypeParameters = addRange(outerTypeParameters, templateTagParameters); - } - typeParameters = outerTypeParameters || emptyArray; - typeParameters = (target.objectFlags & ObjectFlags.Reference || target.symbol.flags & SymbolFlags.TypeLiteral) && !target.aliasTypeArguments ? - filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) : - typeParameters; - links.outerTypeParameters = typeParameters; - if (typeParameters.length) { - links.instantiations = createMap(); - links.instantiations.set(getTypeListId(typeParameters), target); - } - } - if (typeParameters.length) { - // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the - // mapper to the type parameters to produce the effective list of type arguments, and compute the - // instantiation cache key from the type IDs of the type arguments. - const typeArguments = map(typeParameters, combineTypeMappers(type.mapper, mapper)); - const id = getTypeListId(typeArguments); - let result = links.instantiations!.get(id); - if (!result) { - const newMapper = createTypeMapper(typeParameters, typeArguments); - result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((type).target, (type).node, newMapper) : - target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : - instantiateAnonymousType(target, newMapper); - links.instantiations!.set(id, result); - } - return result; - } - return type; - } - - function maybeTypeParameterReference(node: Node) { - return !(node.kind === SyntaxKind.QualifiedName || - node.parent.kind === SyntaxKind.TypeReference && (node.parent).typeArguments && node === (node.parent).typeName || - node.parent.kind === SyntaxKind.ImportType && (node.parent as ImportTypeNode).typeArguments && node === (node.parent as ImportTypeNode).qualifier); - } - - function isTypeParameterPossiblyReferenced(tp: TypeParameter, node: Node) { - // If the type parameter doesn't have exactly one declaration, if there are invening statement blocks - // between the node and the type parameter declaration, if the node contains actual references to the - // type parameter, or if the node contains type queries, we consider the type parameter possibly referenced. - if (tp.symbol && tp.symbol.declarations && tp.symbol.declarations.length === 1) { - const container = tp.symbol.declarations[0].parent; - for (let n = node; n !== container; n = n.parent) { - if (!n || n.kind === SyntaxKind.Block || n.kind === SyntaxKind.ConditionalType && forEachChild((n).extendsType, containsReference)) { - return true; - } - } - return !!forEachChild(node, containsReference); - } - return true; - function containsReference(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.ThisType: - return !!tp.isThisType; - case SyntaxKind.Identifier: - return !tp.isThisType && isPartOfTypeNode(node) && maybeTypeParameterReference(node) && - getTypeFromTypeNode(node) === tp; - case SyntaxKind.TypeQuery: - return true; - } - return !!forEachChild(node, containsReference); - } - } - - function getHomomorphicTypeVariable(type: MappedType) { - const constraintType = getConstraintTypeFromMappedType(type); - if (constraintType.flags & TypeFlags.Index) { - const typeVariable = getActualTypeVariable((constraintType).type); - if (typeVariable.flags & TypeFlags.TypeParameter) { - return typeVariable; - } - } - return undefined; - } - - function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type { - // For a homomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping - // operation depends on T as follows: - // * If T is a primitive type no mapping is performed and the result is simply T. - // * If T is a union type we distribute the mapped type over the union. - // * If T is an array we map to an array where the element type has been transformed. - // * If T is a tuple we map to a tuple where the element types have been transformed. - // * Otherwise we map to an object type where the type of each property has been transformed. - // For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } | - // { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce - // { [P in keyof A]: X } | undefined. - const typeVariable = getHomomorphicTypeVariable(type); - if (typeVariable) { - const mappedTypeVariable = instantiateType(typeVariable, mapper); - if (typeVariable !== mappedTypeVariable) { - return mapType(mappedTypeVariable, t => { - if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && t !== errorType) { - const replacementMapper = createReplacementMapper(typeVariable, t, mapper); - return isArrayType(t) ? instantiateMappedArrayType(t, type, replacementMapper) : - isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) : - instantiateAnonymousType(type, replacementMapper); - } - return t; - }); - } - } - return instantiateAnonymousType(type, mapper); - } - - function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) { - return modifiers & MappedTypeModifiers.IncludeReadonly ? true : modifiers & MappedTypeModifiers.ExcludeReadonly ? false : state; - } - - function instantiateMappedArrayType(arrayType: Type, mappedType: MappedType, mapper: TypeMapper) { - const elementType = instantiateMappedTypeTemplate(mappedType, numberType, /*isOptional*/ true, mapper); - return elementType === errorType ? errorType : - createArrayType(elementType, getModifiedReadonlyState(isReadonlyArrayType(arrayType), getMappedTypeModifiers(mappedType))); - } - - function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) { - const minLength = tupleType.target.minLength; - const elementTypes = map(getTypeArguments(tupleType), (_, i) => - instantiateMappedTypeTemplate(mappedType, getLiteralType("" + i), i >= minLength, mapper)); - const modifiers = getMappedTypeModifiers(mappedType); - const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : - modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) : - minLength; - const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers); - return contains(elementTypes, errorType) ? errorType : - createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames); - } - - function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) { - const templateMapper = combineTypeMappers(mapper, createTypeMapper([getTypeParameterFromMappedType(type)], [key])); - const propType = instantiateType(getTemplateTypeFromMappedType(type.target || type), templateMapper); - const modifiers = getMappedTypeModifiers(type); - return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) : - strictNullChecks && modifiers & MappedTypeModifiers.ExcludeOptional && isOptional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) : - propType; - } - - function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { - const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol); - if (type.objectFlags & ObjectFlags.Mapped) { - (result).declaration = (type).declaration; - // C.f. instantiateSignature - const origTypeParameter = getTypeParameterFromMappedType(type); - const freshTypeParameter = cloneTypeParameter(origTypeParameter); - (result).typeParameter = freshTypeParameter; - mapper = combineTypeMappers(makeUnaryTypeMapper(origTypeParameter, freshTypeParameter), mapper); - freshTypeParameter.mapper = mapper; - } - result.target = type; - result.mapper = mapper; - result.aliasSymbol = type.aliasSymbol; - result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper); - return result; - } - - function getConditionalTypeInstantiation(type: ConditionalType, mapper: TypeMapper): Type { - const root = type.root; - if (root.outerTypeParameters) { - // We are instantiating a conditional type that has one or more type parameters in scope. Apply the - // mapper to the type parameters to produce the effective list of type arguments, and compute the - // instantiation cache key from the type IDs of the type arguments. - const typeArguments = map(root.outerTypeParameters, mapper); - const id = getTypeListId(typeArguments); - let result = root.instantiations!.get(id); - if (!result) { - const newMapper = createTypeMapper(root.outerTypeParameters, typeArguments); - result = instantiateConditionalType(root, newMapper); - root.instantiations!.set(id, result); - } - return result; - } - return type; - } - - function instantiateConditionalType(root: ConditionalRoot, mapper: TypeMapper): Type { - // Check if we have a conditional type where the check type is a naked type parameter. If so, - // the conditional type is distributive over union types and when T is instantiated to a union - // type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y). - if (root.isDistributive) { - const checkType = root.checkType; - const instantiatedType = mapper(checkType); - if (checkType !== instantiatedType && instantiatedType.flags & (TypeFlags.Union | TypeFlags.Never)) { - return mapType(instantiatedType, t => getConditionalType(root, createReplacementMapper(checkType, t, mapper))); - } - } - return getConditionalType(root, mapper); - } - - function instantiateType(type: Type, mapper: TypeMapper | undefined): Type; - function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined; - function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined { - if (!type || !mapper || mapper === identityMapper) { - return type; - } - if (instantiationDepth === 50 || instantiationCount >= 5000000) { - // We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing - // with a combination of infinite generic types that perpetually generate new type identities. We stop - // the recursion here by yielding the error type. - error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); - return errorType; - } - instantiationCount++; - instantiationDepth++; - const result = instantiateTypeWorker(type, mapper); - instantiationDepth--; - return result; - } - - function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type { - const flags = type.flags; - if (flags & TypeFlags.TypeParameter) { - return mapper(type); - } - if (flags & TypeFlags.Object) { - const objectFlags = (type).objectFlags; - if (objectFlags & ObjectFlags.Anonymous) { - // If the anonymous type originates in a declaration of a function, method, class, or - // interface, in an object type literal, or in an object literal expression, we may need - // to instantiate the type because it might reference a type parameter. - return couldContainTypeVariables(type) ? - getObjectTypeInstantiation(type, mapper) : type; - } - if (objectFlags & ObjectFlags.Mapped) { - return getObjectTypeInstantiation(type, mapper); - } - if (objectFlags & ObjectFlags.Reference) { - if ((type).node) { - return getObjectTypeInstantiation(type, mapper); - } - const resolvedTypeArguments = (type).resolvedTypeArguments; - const newTypeArguments = instantiateTypes(resolvedTypeArguments, mapper); - return newTypeArguments !== resolvedTypeArguments ? createTypeReference((type).target, newTypeArguments) : type; - } - return type; - } - if (flags & TypeFlags.Union && !(flags & TypeFlags.Primitive)) { - const types = (type).types; - const newTypes = instantiateTypes(types, mapper); - return newTypes !== types ? getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type; - } - if (flags & TypeFlags.Intersection) { - const types = (type).types; - const newTypes = instantiateTypes(types, mapper); - return newTypes !== types ? getIntersectionType(newTypes, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type; - } - if (flags & TypeFlags.Index) { - return getIndexType(instantiateType((type).type, mapper)); - } - if (flags & TypeFlags.IndexedAccess) { - return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); - } - if (flags & TypeFlags.Conditional) { - return getConditionalTypeInstantiation(type, combineTypeMappers((type).mapper, mapper)); - } - if (flags & TypeFlags.Substitution) { - const maybeVariable = instantiateType((type).typeVariable, mapper); - if (maybeVariable.flags & TypeFlags.TypeVariable) { - return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((type).substitute, mapper)); - } - else { - const sub = instantiateType((type).substitute, mapper); - if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) { - return maybeVariable; - } - return sub; - } - } - return type; - } - - function getPermissiveInstantiation(type: Type) { - return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type : - type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper)); - } - - function getRestrictiveInstantiation(type: Type) { - if (type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never)) { - return type; - } - if (type.restrictiveInstantiation) { - return type.restrictiveInstantiation; - } - type.restrictiveInstantiation = instantiateType(type, restrictiveMapper); - // We set the following so we don't attempt to set the restrictive instance of a restrictive instance - // which is redundant - we'll produce new type identities, but all type params have already been mapped. - // This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint" - // assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters - // are constrained to `unknown` and produce tons of false positives/negatives! - type.restrictiveInstantiation.restrictiveInstantiation = type.restrictiveInstantiation; - return type.restrictiveInstantiation; - } - - function instantiateIndexInfo(info: IndexInfo | undefined, mapper: TypeMapper): IndexInfo | undefined { - return info && createIndexInfo(instantiateType(info.type, mapper), info.isReadonly, info.declaration); - } - - // Returns true if the given expression contains (at any level of nesting) a function or arrow expression - // that is subject to contextual typing. - function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean { - Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - switch (node.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.FunctionDeclaration: // Function declarations can have context when annotated with a jsdoc @type - return isContextSensitiveFunctionLikeDeclaration(node); - case SyntaxKind.ObjectLiteralExpression: - return some((node).properties, isContextSensitive); - case SyntaxKind.ArrayLiteralExpression: - return some((node).elements, isContextSensitive); - case SyntaxKind.ConditionalExpression: - return isContextSensitive((node).whenTrue) || - isContextSensitive((node).whenFalse); - case SyntaxKind.BinaryExpression: - return ((node).operatorToken.kind === SyntaxKind.BarBarToken || (node).operatorToken.kind === SyntaxKind.QuestionQuestionToken) && - (isContextSensitive((node).left) || isContextSensitive((node).right)); - case SyntaxKind.PropertyAssignment: - return isContextSensitive((node).initializer); - case SyntaxKind.ParenthesizedExpression: - return isContextSensitive((node).expression); - case SyntaxKind.JsxAttributes: - return some((node).properties, isContextSensitive) || isJsxOpeningElement(node.parent) && some(node.parent.parent.children, isContextSensitive); - case SyntaxKind.JsxAttribute: { - // If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive. - const { initializer } = node as JsxAttribute; - return !!initializer && isContextSensitive(initializer); - } - case SyntaxKind.JsxExpression: { - // It is possible to that node.expression is undefined (e.g
) - const { expression } = node as JsxExpression; - return !!expression && isContextSensitive(expression); - } - } - - return false; - } - - function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { - if (isFunctionDeclaration(node) && (!isInJSFile(node) || !getTypeForDeclarationFromJSDocComment(node))) { - return false; - } - // Functions with type parameters are not context sensitive. - if (node.typeParameters) { - return false; - } - // Functions with any parameters that lack type annotations are context sensitive. - if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { - return true; - } - if (node.kind !== SyntaxKind.ArrowFunction) { - // If the first parameter is not an explicit 'this' parameter, then the function has - // an implicit 'this' parameter which is subject to contextual typing. - const parameter = firstOrUndefined(node.parameters); - if (!(parameter && parameterIsThisKeyword(parameter))) { - return true; - } - } - return hasContextSensitiveReturnExpression(node); - } - - function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) { - // TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value. - return !!node.body && node.body.kind !== SyntaxKind.Block && isContextSensitive(node.body); - } - - function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration { - return (isInJSFile(func) && isFunctionDeclaration(func) || isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && - isContextSensitiveFunctionLikeDeclaration(func); - } - - function getTypeWithoutSignatures(type: Type): Type { - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type); - if (resolved.constructSignatures.length || resolved.callSignatures.length) { - const result = createObjectType(ObjectFlags.Anonymous, type.symbol); - result.members = resolved.members; - result.properties = resolved.properties; - result.callSignatures = emptyArray; - result.constructSignatures = emptyArray; - return result; - } - } - else if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map((type).types, getTypeWithoutSignatures)); - } - return type; - } - - // TYPE CHECKING - - function isTypeIdenticalTo(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, identityRelation); - } - - function compareTypesIdentical(source: Type, target: Type): Ternary { - return isTypeRelatedTo(source, target, identityRelation) ? Ternary.True : Ternary.False; - } - - function compareTypesAssignable(source: Type, target: Type): Ternary { - return isTypeRelatedTo(source, target, assignableRelation) ? Ternary.True : Ternary.False; - } - - function compareTypesSubtypeOf(source: Type, target: Type): Ternary { - return isTypeRelatedTo(source, target, subtypeRelation) ? Ternary.True : Ternary.False; - } - - function isTypeSubtypeOf(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, subtypeRelation); - } - - function isTypeAssignableTo(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, assignableRelation); - } - - // An object type S is considered to be derived from an object type T if - // S is a union type and every constituent of S is derived from T, - // T is a union type and S is derived from at least one constituent of T, or - // S is a type variable with a base constraint that is derived from T, - // T is one of the global types Object and Function and S is a subtype of T, or - // T occurs directly or indirectly in an 'extends' clause of S. - // Note that this check ignores type parameters and only considers the - // inheritance hierarchy. - function isTypeDerivedFrom(source: Type, target: Type): boolean { - return source.flags & TypeFlags.Union ? every((source).types, t => isTypeDerivedFrom(t, target)) : - target.flags & TypeFlags.Union ? some((target).types, t => isTypeDerivedFrom(source, t)) : - source.flags & TypeFlags.InstantiableNonPrimitive ? isTypeDerivedFrom(getBaseConstraintOfType(source) || unknownType, target) : - target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) : - target === globalFunctionType ? !!(source.flags & TypeFlags.Object) && isFunctionObjectType(source as ObjectType) : - hasBaseType(source, getTargetType(target)); - } - - /** - * This is *not* a bi-directional relationship. - * If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'. - * - * A type S is comparable to a type T if some (but not necessarily all) of the possible values of S are also possible values of T. - * It is used to check following cases: - * - the types of the left and right sides of equality/inequality operators (`===`, `!==`, `==`, `!=`). - * - the types of `case` clause expressions and their respective `switch` expressions. - * - the type of an expression in a type assertion with the type being asserted. - */ - function isTypeComparableTo(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, comparableRelation); - } - - function areTypesComparable(type1: Type, type2: Type): boolean { - return isTypeComparableTo(type1, type2) || isTypeComparableTo(type2, type1); - } - - function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, errorOutputObject?: { errors?: Diagnostic[] }): boolean { - return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain, errorOutputObject); - } - - /** - * Like `checkTypeAssignableTo`, but if it would issue an error, instead performs structural comparisons of the types using the given expression node to - * attempt to issue more specific errors on, for example, specific object literal properties or tuple members. - */ - function checkTypeAssignableToAndOptionallyElaborate(source: Type, target: Type, errorNode: Node | undefined, expr: Expression | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean { - return checkTypeRelatedToAndOptionallyElaborate(source, target, assignableRelation, errorNode, expr, headMessage, containingMessageChain, /*errorOutputContainer*/ undefined); - } - - function checkTypeRelatedToAndOptionallyElaborate( - source: Type, - target: Type, - relation: Map, - errorNode: Node | undefined, - expr: Expression | undefined, - headMessage: DiagnosticMessage | undefined, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ): boolean { - if (isTypeRelatedTo(source, target, relation)) return true; - if (!errorNode || !elaborateError(expr, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { - return checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer); - } - return false; - } - - function isOrHasGenericConditional(type: Type): boolean { - return !!(type.flags & TypeFlags.Conditional || (type.flags & TypeFlags.Intersection && some((type as IntersectionType).types, isOrHasGenericConditional))); - } - - function elaborateError( - node: Expression | undefined, - source: Type, - target: Type, - relation: Map, - headMessage: DiagnosticMessage | undefined, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ): boolean { - if (!node || isOrHasGenericConditional(target)) return false; - if (!checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined) - && elaborateDidYouMeanToCallOrConstruct(node, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { - return true; - } - switch (node.kind) { - case SyntaxKind.JsxExpression: - case SyntaxKind.ParenthesizedExpression: - return elaborateError((node as ParenthesizedExpression | JsxExpression).expression, source, target, relation, headMessage, containingMessageChain, errorOutputContainer); - case SyntaxKind.BinaryExpression: - switch ((node as BinaryExpression).operatorToken.kind) { - case SyntaxKind.EqualsToken: - case SyntaxKind.CommaToken: - return elaborateError((node as BinaryExpression).right, source, target, relation, headMessage, containingMessageChain, errorOutputContainer); - } - break; - case SyntaxKind.ObjectLiteralExpression: - return elaborateObjectLiteral(node as ObjectLiteralExpression, source, target, relation, containingMessageChain, errorOutputContainer); - case SyntaxKind.ArrayLiteralExpression: - return elaborateArrayLiteral(node as ArrayLiteralExpression, source, target, relation, containingMessageChain, errorOutputContainer); - case SyntaxKind.JsxAttributes: - return elaborateJsxComponents(node as JsxAttributes, source, target, relation, containingMessageChain, errorOutputContainer); - case SyntaxKind.ArrowFunction: - return elaborateArrowFunction(node as ArrowFunction, source, target, relation, containingMessageChain, errorOutputContainer); - } - return false; - } - - function elaborateDidYouMeanToCallOrConstruct( - node: Expression, - source: Type, - target: Type, - relation: Map, - headMessage: DiagnosticMessage | undefined, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ): boolean { - const callSignatures = getSignaturesOfType(source, SignatureKind.Call); - const constructSignatures = getSignaturesOfType(source, SignatureKind.Construct); - for (const signatures of [constructSignatures, callSignatures]) { - if (some(signatures, s => { - const returnType = getReturnTypeOfSignature(s); - return !(returnType.flags & (TypeFlags.Any | TypeFlags.Never)) && checkTypeRelatedTo(returnType, target, relation, /*errorNode*/ undefined); - })) { - const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {}; - checkTypeAssignableTo(source, target, node, headMessage, containingMessageChain, resultObj); - const diagnostic = resultObj.errors![resultObj.errors!.length - 1]; - addRelatedInfo(diagnostic, createDiagnosticForNode( - node, - signatures === constructSignatures ? Diagnostics.Did_you_mean_to_use_new_with_this_expression : Diagnostics.Did_you_mean_to_call_this_expression - )); - return true; - } - } - return false; - } - - function elaborateArrowFunction( - node: ArrowFunction, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ): boolean { - // Don't elaborate blocks - if (isBlock(node.body)) { - return false; - } - // Or functions with annotated parameter types - if (some(node.parameters, ts.hasType)) { - return false; - } - const sourceSig = getSingleCallSignature(source); - if (!sourceSig) { - return false; - } - const targetSignatures = getSignaturesOfType(target, SignatureKind.Call); - if (!length(targetSignatures)) { - return false; - } - const returnExpression = node.body; - const sourceReturn = getReturnTypeOfSignature(sourceSig); - const targetReturn = getUnionType(map(targetSignatures, getReturnTypeOfSignature)); - if (!checkTypeRelatedTo(sourceReturn, targetReturn, relation, /*errorNode*/ undefined)) { - const elaborated = returnExpression && elaborateError(returnExpression, sourceReturn, targetReturn, relation, /*headMessage*/ undefined, containingMessageChain, errorOutputContainer); - if (elaborated) { - return elaborated; - } - const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {}; - checkTypeRelatedTo(sourceReturn, targetReturn, relation, returnExpression, /*message*/ undefined, containingMessageChain, resultObj); - if (resultObj.errors) { - if (target.symbol && length(target.symbol.declarations)) { - addRelatedInfo(resultObj.errors[resultObj.errors.length - 1], createDiagnosticForNode( - target.symbol.declarations[0], - Diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature, - )); - } - return true; - } - } - return false; - } - - function getBestMatchIndexedAccessTypeOrUndefined(source: Type, target: Type, nameType: Type) { - const idx = getIndexedAccessTypeOrUndefined(target, nameType); - if (idx) { - return idx; - } - if (target.flags & TypeFlags.Union) { - const best = getBestMatchingType(source, target as UnionType); - if (best) { - return getIndexedAccessTypeOrUndefined(best, nameType); - } - } - } - - type ElaborationIterator = IterableIterator<{ errorNode: Node, innerExpression: Expression | undefined, nameType: Type, errorMessage?: DiagnosticMessage | undefined }>; - /** - * For every element returned from the iterator, checks that element to issue an error on a property of that element's type - * If that element would issue an error, we first attempt to dive into that element's inner expression and issue a more specific error by recuring into `elaborateError` - * Otherwise, we issue an error on _every_ element which fail the assignability check - */ - function elaborateElementwise( - iterator: ElaborationIterator, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ) { - // Assignability failure - check each prop individually, and if that fails, fall back on the bad error span - let reportedError = false; - for (let status = iterator.next(); !status.done; status = iterator.next()) { - const { errorNode: prop, innerExpression: next, nameType, errorMessage } = status.value; - const targetPropType = getBestMatchIndexedAccessTypeOrUndefined(source, target, nameType); - if (!targetPropType || targetPropType.flags & TypeFlags.IndexedAccess) continue; // Don't elaborate on indexes on generic variables - const sourcePropType = getIndexedAccessTypeOrUndefined(source, nameType); - if (sourcePropType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) { - const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined, containingMessageChain, errorOutputContainer); - if (elaborated) { - reportedError = true; - } - else { - // Issue error on the prop itself, since the prop couldn't elaborate the error - const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {}; - // Use the expression type, if available - const specificSource = next ? checkExpressionForMutableLocation(next, CheckMode.Normal, sourcePropType) : sourcePropType; - const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj); - if (result && specificSource !== sourcePropType) { - // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType - checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj); - } - if (resultObj.errors) { - const reportedDiag = resultObj.errors[resultObj.errors.length - 1]; - const propertyName = isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined; - const targetProp = propertyName !== undefined ? getPropertyOfType(target, propertyName) : undefined; - - let issuedElaboration = false; - if (!targetProp) { - const indexInfo = isTypeAssignableToKind(nameType, TypeFlags.NumberLike) && getIndexInfoOfType(target, IndexKind.Number) || - getIndexInfoOfType(target, IndexKind.String) || - undefined; - if (indexInfo && indexInfo.declaration && !getSourceFileOfNode(indexInfo.declaration).hasNoDefaultLib) { - issuedElaboration = true; - addRelatedInfo(reportedDiag, createDiagnosticForNode(indexInfo.declaration, Diagnostics.The_expected_type_comes_from_this_index_signature)); - } - } - - if (!issuedElaboration && (targetProp && length(targetProp.declarations) || target.symbol && length(target.symbol.declarations))) { - const targetNode = targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0]; - if (!getSourceFileOfNode(targetNode).hasNoDefaultLib) { - addRelatedInfo(reportedDiag, createDiagnosticForNode( - targetNode, - Diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1, - propertyName && !(nameType.flags & TypeFlags.UniqueESSymbol) ? unescapeLeadingUnderscores(propertyName) : typeToString(nameType), - typeToString(target) - )); - } - } - } - reportedError = true; - } - } - } - return reportedError; - } - - function *generateJsxAttributes(node: JsxAttributes): ElaborationIterator { - if (!length(node.properties)) return; - for (const prop of node.properties) { - if (isJsxSpreadAttribute(prop)) continue; - yield { errorNode: prop.name, innerExpression: prop.initializer, nameType: getLiteralType(idText(prop.name)) }; - } - } - - function *generateJsxChildren(node: JsxElement, getInvalidTextDiagnostic: () => DiagnosticMessage): ElaborationIterator { - if (!length(node.children)) return; - let memberOffset = 0; - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - const nameType = getLiteralType(i - memberOffset); - const elem = getElaborationElementForJsxChild(child, nameType, getInvalidTextDiagnostic); - if (elem) { - yield elem; - } - else { - memberOffset++; - } - } - } - - function getElaborationElementForJsxChild(child: JsxChild, nameType: LiteralType, getInvalidTextDiagnostic: () => DiagnosticMessage) { - switch (child.kind) { - case SyntaxKind.JsxExpression: - // child is of the type of the expression - return { errorNode: child, innerExpression: child.expression, nameType }; - case SyntaxKind.JsxText: - if (child.containsOnlyTriviaWhiteSpaces) { - break; // Whitespace only jsx text isn't real jsx text - } - // child is a string - return { errorNode: child, innerExpression: undefined, nameType, errorMessage: getInvalidTextDiagnostic() }; - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxFragment: - // child is of type JSX.Element - return { errorNode: child, innerExpression: child, nameType }; - default: - return Debug.assertNever(child, "Found invalid jsx child"); - } - } - - function getSemanticJsxChildren(children: NodeArray) { - return filter(children, i => !isJsxText(i) || !i.containsOnlyTriviaWhiteSpaces); - } - - function elaborateJsxComponents( - node: JsxAttributes, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ) { - let result = elaborateElementwise(generateJsxAttributes(node), source, target, relation, containingMessageChain, errorOutputContainer); - let invalidTextDiagnostic: DiagnosticMessage | undefined; - if (isJsxOpeningElement(node.parent) && isJsxElement(node.parent.parent)) { - const containingElement = node.parent.parent; - const childPropName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - const childrenPropName = childPropName === undefined ? "children" : unescapeLeadingUnderscores(childPropName); - const childrenNameType = getLiteralType(childrenPropName); - const childrenTargetType = getIndexedAccessType(target, childrenNameType); - const validChildren = getSemanticJsxChildren(containingElement.children); - if (!length(validChildren)) { - return result; - } - const moreThanOneRealChildren = length(validChildren) > 1; - const arrayLikeTargetParts = filterType(childrenTargetType, isArrayOrTupleLikeType); - const nonArrayLikeTargetParts = filterType(childrenTargetType, t => !isArrayOrTupleLikeType(t)); - if (moreThanOneRealChildren) { - if (arrayLikeTargetParts !== neverType) { - const realSource = createTupleType(checkJsxChildren(containingElement, CheckMode.Normal)); - const children = generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic); - result = elaborateElementwise(children, realSource, arrayLikeTargetParts, relation, containingMessageChain, errorOutputContainer) || result; - } - else if (!isTypeRelatedTo(getIndexedAccessType(source, childrenNameType), childrenTargetType, relation)) { - // arity mismatch - result = true; - const diag = error( - containingElement.openingElement.tagName, - Diagnostics.This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, - childrenPropName, - typeToString(childrenTargetType) - ); - if (errorOutputContainer && errorOutputContainer.skipLogging) { - (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); - } - } - } - else { - if (nonArrayLikeTargetParts !== neverType) { - const child = validChildren[0]; - const elem = getElaborationElementForJsxChild(child, childrenNameType, getInvalidTextualChildDiagnostic); - if (elem) { - result = elaborateElementwise( - (function*() { yield elem; })(), - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ) || result; - } - } - else if (!isTypeRelatedTo(getIndexedAccessType(source, childrenNameType), childrenTargetType, relation)) { - // arity mismatch - result = true; - const diag = error( - containingElement.openingElement.tagName, - Diagnostics.This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, - childrenPropName, - typeToString(childrenTargetType) - ); - if (errorOutputContainer && errorOutputContainer.skipLogging) { - (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); - } - } - } - } - return result; - - function getInvalidTextualChildDiagnostic() { - if (!invalidTextDiagnostic) { - const tagNameText = getTextOfNode(node.parent.tagName); - const childPropName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - const childrenPropName = childPropName === undefined ? "children" : unescapeLeadingUnderscores(childPropName); - const childrenTargetType = getIndexedAccessType(target, getLiteralType(childrenPropName)); - const diagnostic = Diagnostics._0_components_don_t_accept_text_as_child_elements_Text_in_JSX_has_the_type_string_but_the_expected_type_of_1_is_2; - invalidTextDiagnostic = { ...diagnostic, key: "!!ALREADY FORMATTED!!", message: formatMessage(/*_dummy*/ undefined, diagnostic, tagNameText, childrenPropName, typeToString(childrenTargetType)) }; - } - return invalidTextDiagnostic; - } - } - - function *generateLimitedTupleElements(node: ArrayLiteralExpression, target: Type): ElaborationIterator { - const len = length(node.elements); - if (!len) return; - for (let i = 0; i < len; i++) { - // Skip elements which do not exist in the target - a length error on the tuple overall is likely better than an error on a mismatched index signature - if (isTupleLikeType(target) && !getPropertyOfType(target, ("" + i) as __String)) continue; - const elem = node.elements[i]; - if (isOmittedExpression(elem)) continue; - const nameType = getLiteralType(i); - yield { errorNode: elem, innerExpression: elem, nameType }; - } - } - - function elaborateArrayLiteral( - node: ArrayLiteralExpression, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ) { - if (target.flags & TypeFlags.Primitive) return false; - if (isTupleLikeType(source)) { - return elaborateElementwise(generateLimitedTupleElements(node, target), source, target, relation, containingMessageChain, errorOutputContainer); - } - // recreate a tuple from the elements, if possible - const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true); - if (isTupleLikeType(tupleizedType)) { - return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation, containingMessageChain, errorOutputContainer); - } - return false; - } - - function *generateObjectLiteralElements(node: ObjectLiteralExpression): ElaborationIterator { - if (!length(node.properties)) return; - for (const prop of node.properties) { - if (isSpreadAssignment(prop)) continue; - const type = getLiteralTypeFromProperty(getSymbolOfNode(prop), TypeFlags.StringOrNumberLiteralOrUnique); - if (!type || (type.flags & TypeFlags.Never)) { - continue; - } - switch (prop.kind) { - case SyntaxKind.SetAccessor: - case SyntaxKind.GetAccessor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.ShorthandPropertyAssignment: - yield { errorNode: prop.name, innerExpression: undefined, nameType: type }; - break; - case SyntaxKind.PropertyAssignment: - yield { errorNode: prop.name, innerExpression: prop.initializer, nameType: type, errorMessage: isComputedNonLiteralName(prop.name) ? Diagnostics.Type_of_computed_property_s_value_is_0_which_is_not_assignable_to_type_1 : undefined }; - break; - default: - Debug.assertNever(prop); - } - } - } - - function elaborateObjectLiteral( - node: ObjectLiteralExpression, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined - ) { - if (target.flags & TypeFlags.Primitive) return false; - return elaborateElementwise(generateObjectLiteralElements(node), source, target, relation, containingMessageChain, errorOutputContainer); - } - - /** - * This is *not* a bi-directional relationship. - * If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'. - */ - function checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean { - return checkTypeRelatedTo(source, target, comparableRelation, errorNode, headMessage, containingMessageChain); - } - - function isSignatureAssignableTo(source: Signature, - target: Signature, - ignoreReturnTypes: boolean): boolean { - return compareSignaturesRelated(source, target, ignoreReturnTypes ? SignatureCheckMode.IgnoreReturnTypes : 0, /*reportErrors*/ false, - /*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False; - } - - type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void; - - /** - * Returns true if `s` is `(...args: any[]) => any` or `(this: any, ...args: any[]) => any` - */ - function isAnySignature(s: Signature) { - return !s.typeParameters && (!s.thisParameter || isTypeAny(getTypeOfParameter(s.thisParameter))) && s.parameters.length === 1 && - signatureHasRestParameter(s) && (getTypeOfParameter(s.parameters[0]) === anyArrayType || isTypeAny(getTypeOfParameter(s.parameters[0]))) && - isTypeAny(getReturnTypeOfSignature(s)); - } - - /** - * See signatureRelatedTo, compareSignaturesIdentical - */ - function compareSignaturesRelated(source: Signature, - target: Signature, - checkMode: SignatureCheckMode, - reportErrors: boolean, - errorReporter: ErrorReporter | undefined, - incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined, - compareTypes: TypeComparer, - reportUnreliableMarkers: TypeMapper | undefined): Ternary { - // TODO (drosen): De-duplicate code between related functions. - if (source === target) { - return Ternary.True; - } - - if (isAnySignature(target)) { - return Ternary.True; - } - - const targetCount = getParameterCount(target); - const sourceHasMoreParameters = !hasEffectiveRestParameter(target) && - (checkMode & SignatureCheckMode.StrictArity ? hasEffectiveRestParameter(source) || getParameterCount(source) > targetCount : getMinArgumentCount(source) > targetCount); - if (sourceHasMoreParameters) { - return Ternary.False; - } - - if (source.typeParameters && source.typeParameters !== target.typeParameters) { - target = getCanonicalSignature(target); - source = instantiateSignatureInContextOf(source, target, /*inferenceContext*/ undefined, compareTypes); - } - - const sourceCount = getParameterCount(source); - const sourceRestType = getNonArrayRestType(source); - const targetRestType = getNonArrayRestType(target); - if (sourceRestType || targetRestType) { - void instantiateType(sourceRestType || targetRestType, reportUnreliableMarkers); - } - if (sourceRestType && targetRestType && sourceCount !== targetCount) { - // We're not able to relate misaligned complex rest parameters - return Ternary.False; - } - - const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; - const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration && - kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor; - let result = Ternary.True; - - const sourceThisType = getThisTypeOfSignature(source); - if (sourceThisType && sourceThisType !== voidType) { - const targetThisType = getThisTypeOfSignature(target); - if (targetThisType) { - // void sources are assignable to anything. - const related = !strictVariance && compareTypes(sourceThisType, targetThisType, /*reportErrors*/ false) - || compareTypes(targetThisType, sourceThisType, reportErrors); - if (!related) { - if (reportErrors) { - errorReporter!(Diagnostics.The_this_types_of_each_signature_are_incompatible); - } - return Ternary.False; - } - result &= related; - } - } - - const paramCount = sourceRestType || targetRestType ? Math.min(sourceCount, targetCount) : Math.max(sourceCount, targetCount); - const restIndex = sourceRestType || targetRestType ? paramCount - 1 : -1; - - for (let i = 0; i < paramCount; i++) { - const sourceType = i === restIndex ? getRestTypeAtPosition(source, i) : getTypeAtPosition(source, i); - const targetType = i === restIndex ? getRestTypeAtPosition(target, i) : getTypeAtPosition(target, i); - // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter - // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, - // they naturally relate only contra-variantly). However, if the source and target parameters both have - // function types with a single call signature, we know we are relating two callback parameters. In - // that case it is sufficient to only relate the parameters of the signatures co-variantly because, - // similar to return values, callback parameters are output positions. This means that a Promise, - // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) - // with respect to T. - const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType)); - const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType)); - const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) && - (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable); - let related = callbacks ? - compareSignaturesRelated(targetSig!, sourceSig!, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) : - !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); - // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void - if (related && checkMode & SignatureCheckMode.StrictArity && i >= getMinArgumentCount(source) && i < getMinArgumentCount(target) && compareTypes(sourceType, targetType, /*reportErrors*/ false)) { - related = Ternary.False; - } - if (!related) { - if (reportErrors) { - errorReporter!(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, - unescapeLeadingUnderscores(getParameterNameAtPosition(source, i)), - unescapeLeadingUnderscores(getParameterNameAtPosition(target, i))); - } - return Ternary.False; - } - result &= related; - } - - if (!(checkMode & SignatureCheckMode.IgnoreReturnTypes)) { - // If a signature resolution is already in-flight, skip issuing a circularity error - // here and just use the `any` type directly - const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType - : target.declaration && isJSConstructor(target.declaration) ? getDeclaredTypeOfClassOrInterface(getMergedSymbol(target.declaration.symbol)) - : getReturnTypeOfSignature(target); - if (targetReturnType === voidType) { - return result; - } - const sourceReturnType = isResolvingReturnTypeOfSignature(source) ? anyType - : source.declaration && isJSConstructor(source.declaration) ? getDeclaredTypeOfClassOrInterface(getMergedSymbol(source.declaration.symbol)) - : getReturnTypeOfSignature(source); - - // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions - const targetTypePredicate = getTypePredicateOfSignature(target); - if (targetTypePredicate) { - const sourceTypePredicate = getTypePredicateOfSignature(source); - if (sourceTypePredicate) { - result &= compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes); - } - else if (isIdentifierTypePredicate(targetTypePredicate)) { - if (reportErrors) { - errorReporter!(Diagnostics.Signature_0_must_be_a_type_predicate, signatureToString(source)); - } - return Ternary.False; - } - } - else { - // When relating callback signatures, we still need to relate return types bi-variantly as otherwise - // the containing type wouldn't be co-variant. For example, interface Foo { add(cb: () => T): void } - // wouldn't be co-variant for T without this rule. - result &= checkMode & SignatureCheckMode.BivariantCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) || - compareTypes(sourceReturnType, targetReturnType, reportErrors); - if (!result && reportErrors && incompatibleErrorReporter) { - incompatibleErrorReporter(sourceReturnType, targetReturnType); - } - } - - } - - return result; - } - - function compareTypePredicateRelatedTo( - source: TypePredicate, - target: TypePredicate, - reportErrors: boolean, - errorReporter: ErrorReporter | undefined, - compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary { - if (source.kind !== target.kind) { - if (reportErrors) { - errorReporter!(Diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard); - errorReporter!(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target)); - } - return Ternary.False; - } - - if (source.kind === TypePredicateKind.Identifier || source.kind === TypePredicateKind.AssertsIdentifier) { - if (source.parameterIndex !== (target as IdentifierTypePredicate).parameterIndex) { - if (reportErrors) { - errorReporter!(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, source.parameterName, (target as IdentifierTypePredicate).parameterName); - errorReporter!(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target)); - } - return Ternary.False; - } - } - - const related = source.type === target.type ? Ternary.True : - source.type && target.type ? compareTypes(source.type, target.type, reportErrors) : - Ternary.False; - if (related === Ternary.False && reportErrors) { - errorReporter!(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target)); - } - return related; - } - - function isImplementationCompatibleWithOverload(implementation: Signature, overload: Signature): boolean { - const erasedSource = getErasedSignature(implementation); - const erasedTarget = getErasedSignature(overload); - - // First see if the return types are compatible in either direction. - const sourceReturnType = getReturnTypeOfSignature(erasedSource); - const targetReturnType = getReturnTypeOfSignature(erasedTarget); - if (targetReturnType === voidType - || isTypeRelatedTo(targetReturnType, sourceReturnType, assignableRelation) - || isTypeRelatedTo(sourceReturnType, targetReturnType, assignableRelation)) { - - return isSignatureAssignableTo(erasedSource, erasedTarget, /*ignoreReturnTypes*/ true); - } - - return false; - } - - function isEmptyResolvedType(t: ResolvedType) { - return t.properties.length === 0 && - t.callSignatures.length === 0 && - t.constructSignatures.length === 0 && - !t.stringIndexInfo && - !t.numberIndexInfo; - } - - function isEmptyObjectType(type: Type): boolean { - return type.flags & TypeFlags.Object ? !isGenericMappedType(type) && isEmptyResolvedType(resolveStructuredTypeMembers(type)) : - type.flags & TypeFlags.NonPrimitive ? true : - type.flags & TypeFlags.Union ? some((type).types, isEmptyObjectType) : - type.flags & TypeFlags.Intersection ? every((type).types, isEmptyObjectType) : - false; - } - - function isEmptyAnonymousObjectType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.Anonymous) && isEmptyObjectType(type); - } - - function isStringIndexSignatureOnlyType(type: Type): boolean { - return type.flags & TypeFlags.Object && !isGenericMappedType(type) && getPropertiesOfType(type).length === 0 && getIndexInfoOfType(type, IndexKind.String) && !getIndexInfoOfType(type, IndexKind.Number) || - type.flags & TypeFlags.UnionOrIntersection && every((type).types, isStringIndexSignatureOnlyType) || - false; - } - - function isEnumTypeRelatedTo(sourceSymbol: Symbol, targetSymbol: Symbol, errorReporter?: ErrorReporter) { - if (sourceSymbol === targetSymbol) { - return true; - } - const id = getSymbolId(sourceSymbol) + "," + getSymbolId(targetSymbol); - const entry = enumRelation.get(id); - if (entry !== undefined && !(!(entry & RelationComparisonResult.Reported) && entry & RelationComparisonResult.Failed && errorReporter)) { - return !!(entry & RelationComparisonResult.Succeeded); - } - if (sourceSymbol.escapedName !== targetSymbol.escapedName || !(sourceSymbol.flags & SymbolFlags.RegularEnum) || !(targetSymbol.flags & SymbolFlags.RegularEnum)) { - enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported); - return false; - } - const targetEnumType = getTypeOfSymbol(targetSymbol); - for (const property of getPropertiesOfType(getTypeOfSymbol(sourceSymbol))) { - if (property.flags & SymbolFlags.EnumMember) { - const targetProperty = getPropertyOfType(targetEnumType, property.escapedName); - if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) { - if (errorReporter) { - errorReporter(Diagnostics.Property_0_is_missing_in_type_1, symbolName(property), - typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); - enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported); - } - else { - enumRelation.set(id, RelationComparisonResult.Failed); - } - return false; - } - } - } - enumRelation.set(id, RelationComparisonResult.Succeeded); - return true; - } - - function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { - const s = source.flags; - const t = target.flags; - if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true; - if (t & TypeFlags.Never) return false; - if (s & TypeFlags.StringLike && t & TypeFlags.String) return true; - if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral && - t & TypeFlags.StringLiteral && !(t & TypeFlags.EnumLiteral) && - (source).value === (target).value) return true; - if (s & TypeFlags.NumberLike && t & TypeFlags.Number) return true; - if (s & TypeFlags.NumberLiteral && s & TypeFlags.EnumLiteral && - t & TypeFlags.NumberLiteral && !(t & TypeFlags.EnumLiteral) && - (source).value === (target).value) return true; - if (s & TypeFlags.BigIntLike && t & TypeFlags.BigInt) return true; - if (s & TypeFlags.BooleanLike && t & TypeFlags.Boolean) return true; - if (s & TypeFlags.ESSymbolLike && t & TypeFlags.ESSymbol) return true; - if (s & TypeFlags.Enum && t & TypeFlags.Enum && isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter)) return true; - if (s & TypeFlags.EnumLiteral && t & TypeFlags.EnumLiteral) { - if (s & TypeFlags.Union && t & TypeFlags.Union && isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter)) return true; - if (s & TypeFlags.Literal && t & TypeFlags.Literal && - (source).value === (target).value && - isEnumTypeRelatedTo(getParentOfSymbol(source.symbol)!, getParentOfSymbol(target.symbol)!, errorReporter)) return true; - } - if (s & TypeFlags.Undefined && (!strictNullChecks || t & (TypeFlags.Undefined | TypeFlags.Void))) return true; - if (s & TypeFlags.Null && (!strictNullChecks || t & TypeFlags.Null)) return true; - if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true; - if (relation === assignableRelation || relation === comparableRelation) { - if (s & TypeFlags.Any) return true; - // Type number or any numeric literal type is assignable to any numeric enum type or any - // numeric enum literal type. This rule exists for backwards compatibility reasons because - // bit-flag enum types sometimes look like literal enum types with numeric literal values. - if (s & (TypeFlags.Number | TypeFlags.NumberLiteral) && !(s & TypeFlags.EnumLiteral) && ( - t & TypeFlags.Enum || t & TypeFlags.NumberLiteral && t & TypeFlags.EnumLiteral)) return true; - } - return false; - } - - function isTypeRelatedTo(source: Type, target: Type, relation: Map) { - if (isFreshLiteralType(source)) { - source = (source).regularType; - } - if (isFreshLiteralType(target)) { - target = (target).regularType; - } - if (source === target || - relation === comparableRelation && !(target.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(target, source, relation) || - relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) { - return true; - } - if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { - const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, relation)); - if (related !== undefined) { - return !!(related & RelationComparisonResult.Succeeded); - } - } - if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) { - return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined); - } - return false; - } - - function isIgnoredJsxProperty(source: Type, sourceProp: Symbol) { - return getObjectFlags(source) & ObjectFlags.JsxAttributes && !isUnhyphenatedJsxName(sourceProp.escapedName); - } - - function getNormalizedType(type: Type, writing: boolean): Type { - return isFreshLiteralType(type) ? (type).regularType : - getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : - type.flags & TypeFlags.Substitution ? writing ? (type).typeVariable : (type).substitute : - type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : - type; - } - - /** - * Checks if 'source' is related to 'target' (e.g.: is a assignable to). - * @param source The left-hand-side of the relation. - * @param target The right-hand-side of the relation. - * @param relation The relation considered. One of 'identityRelation', 'subtypeRelation', 'assignableRelation', or 'comparableRelation'. - * Used as both to determine which checks are performed and as a cache of previously computed results. - * @param errorNode The suggested node upon which all errors will be reported, if defined. This may or may not be the actual node used. - * @param headMessage If the error chain should be prepended by a head message, then headMessage will be used. - * @param containingMessageChain A chain of errors to prepend any new errors found. - * @param errorOutputContainer Return the diagnostic. Do not log if 'skipLogging' is truthy. - */ - function checkTypeRelatedTo( - source: Type, - target: Type, - relation: Map, - errorNode: Node | undefined, - headMessage?: DiagnosticMessage, - containingMessageChain?: () => DiagnosticMessageChain | undefined, - errorOutputContainer?: { errors?: Diagnostic[], skipLogging?: boolean }, - ): boolean { - let errorInfo: DiagnosticMessageChain | undefined; - let relatedInfo: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined; - let maybeKeys: string[]; - let sourceStack: Type[]; - let targetStack: Type[]; - let maybeCount = 0; - let depth = 0; - let expandingFlags = ExpandingFlags.None; - let overflow = false; - let overrideNextErrorInfo = 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid - let lastSkippedInfo: [Type, Type] | undefined; - let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = []; - - Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking"); - - const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage); - if (incompatibleStack.length) { - reportIncompatibleStack(); - } - if (overflow) { - const diag = error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target)); - if (errorOutputContainer) { - (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); - } - } - else if (errorInfo) { - if (containingMessageChain) { - const chain = containingMessageChain(); - if (chain) { - concatenateDiagnosticMessageChains(chain, errorInfo); - errorInfo = chain; - } - } - - let relatedInformation: DiagnosticRelatedInformation[] | undefined; - // Check if we should issue an extra diagnostic to produce a quickfix for a slightly incorrect import statement - if (headMessage && errorNode && !result && source.symbol) { - const links = getSymbolLinks(source.symbol); - if (links.originatingImport && !isImportCall(links.originatingImport)) { - const helpfulRetry = checkTypeRelatedTo(getTypeOfSymbol(links.target!), target, relation, /*errorNode*/ undefined); - if (helpfulRetry) { - // Likely an incorrect import. Issue a helpful diagnostic to produce a quickfix to change the import - const diag = createDiagnosticForNode(links.originatingImport, Diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead); - relatedInformation = append(relatedInformation, diag); // Cause the error to appear with the error that triggered it - } - } - } - const diag = createDiagnosticForNodeFromMessageChain(errorNode!, errorInfo, relatedInformation); - if (relatedInfo) { - addRelatedInfo(diag, ...relatedInfo); - } - if (errorOutputContainer) { - (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); - } - if (!errorOutputContainer || !errorOutputContainer.skipLogging) { - diagnostics.add(diag); - } - } - if (errorNode && errorOutputContainer && errorOutputContainer.skipLogging && result === Ternary.False) { - Debug.assert(!!errorOutputContainer.errors, "missed opportunity to interact with error."); - } - return result !== Ternary.False; - - function resetErrorInfo(saved: ReturnType) { - errorInfo = saved.errorInfo; - lastSkippedInfo = saved.lastSkippedInfo; - incompatibleStack = saved.incompatibleStack; - overrideNextErrorInfo = saved.overrideNextErrorInfo; - relatedInfo = saved.relatedInfo; - } - - function captureErrorCalculationState() { - return { - errorInfo, - lastSkippedInfo, - incompatibleStack: incompatibleStack.slice(), - overrideNextErrorInfo, - relatedInfo: !relatedInfo ? undefined : relatedInfo.slice() as ([DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined) - }; - } - - function reportIncompatibleError(message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number) { - overrideNextErrorInfo++; // Suppress the next relation error - lastSkippedInfo = undefined; // Reset skipped info cache - incompatibleStack.push([message, arg0, arg1, arg2, arg3]); - } - - function reportIncompatibleStack() { - const stack = incompatibleStack; - incompatibleStack = []; - const info = lastSkippedInfo; - lastSkippedInfo = undefined; - if (stack.length === 1) { - reportError(...stack[0]); - if (info) { - // Actually do the last relation error - reportRelationError(/*headMessage*/ undefined, ...info); - } - return; - } - // The first error will be the innermost, while the last will be the outermost - so by popping off the end, - // we can build from left to right - let path = ""; - const secondaryRootErrors: typeof incompatibleStack = []; - while (stack.length) { - const [msg, ...args] = stack.pop()!; - switch (msg.code) { - case Diagnostics.Types_of_property_0_are_incompatible.code: { - // Parenthesize a `new` if there is one - if (path.indexOf("new ") === 0) { - path = `(${path})`; - } - const str = "" + args[0]; - // If leading, just print back the arg (irrespective of if it's a valid identifier) - if (path.length === 0) { - path = `${str}`; - } - // Otherwise write a dotted name if possible - else if (isIdentifierText(str, compilerOptions.target)) { - path = `${path}.${str}`; - } - // Failing that, check if the name is already a computed name - else if (str[0] === "[" && str[str.length - 1] === "]") { - path = `${path}${str}`; - } - // And finally write out a computed name as a last resort - else { - path = `${path}[${str}]`; - } - break; - } - case Diagnostics.Call_signature_return_types_0_and_1_are_incompatible.code: - case Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code: - case Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code: - case Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code: { - if (path.length === 0) { - // Don't flatten signature compatability errors at the start of a chain - instead prefer - // to unify (the with no arguments bit is excessive for printback) and print them back - let mappedMsg = msg; - if (msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) { - mappedMsg = Diagnostics.Call_signature_return_types_0_and_1_are_incompatible; - } - else if (msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) { - mappedMsg = Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible; - } - secondaryRootErrors.unshift([mappedMsg, args[0], args[1]]); - } - else { - const prefix = (msg.code === Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code || - msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) - ? "new " - : ""; - const params = (msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code || - msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) - ? "" - : "..."; - path = `${prefix}${path}(${params})`; - } - break; - } - default: - return Debug.fail(`Unhandled Diagnostic: ${msg.code}`); - } - } - if (path) { - reportError(path[path.length - 1] === ")" - ? Diagnostics.The_types_returned_by_0_are_incompatible_between_these_types - : Diagnostics.The_types_of_0_are_incompatible_between_these_types, - path - ); - } - else { - // Remove the innermost secondary error as it will duplicate the error already reported by `reportRelationError` on entry - secondaryRootErrors.shift(); - } - for (const [msg, ...args] of secondaryRootErrors) { - const originalValue = msg.elidedInCompatabilityPyramid; - msg.elidedInCompatabilityPyramid = false; // Teporarily override elision to ensure error is reported - reportError(msg, ...args); - msg.elidedInCompatabilityPyramid = originalValue; - } - if (info) { - // Actually do the last relation error - reportRelationError(/*headMessage*/ undefined, ...info); - } - } - - function reportError(message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void { - Debug.assert(!!errorNode); - if (incompatibleStack.length) reportIncompatibleStack(); - if (message.elidedInCompatabilityPyramid) return; - errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2, arg3); - } - - function associateRelatedInfo(info: DiagnosticRelatedInformation) { - Debug.assert(!!errorInfo); - if (!relatedInfo) { - relatedInfo = [info]; - } - else { - relatedInfo.push(info); - } - } - - function reportRelationError(message: DiagnosticMessage | undefined, source: Type, target: Type) { - if (incompatibleStack.length) reportIncompatibleStack(); - const [sourceType, targetType] = getTypeNamesForErrorDisplay(source, target); - - if (target.flags & TypeFlags.TypeParameter && target.immediateBaseConstraint !== undefined && isTypeAssignableTo(source, target.immediateBaseConstraint)) { - reportError( - Diagnostics._0_is_assignable_to_the_constraint_of_type_1_but_1_could_be_instantiated_with_a_different_subtype_of_constraint_2, - sourceType, - targetType, - typeToString(target.immediateBaseConstraint), - ); - } - - if (!message) { - if (relation === comparableRelation) { - message = Diagnostics.Type_0_is_not_comparable_to_type_1; - } - else if (sourceType === targetType) { - message = Diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated; - } - else { - message = Diagnostics.Type_0_is_not_assignable_to_type_1; - } - } - - reportError(message, sourceType, targetType); - } - - function tryElaborateErrorsForPrimitivesAndObjects(source: Type, target: Type) { - const sourceType = symbolValueDeclarationIsContextSensitive(source.symbol) ? typeToString(source, source.symbol.valueDeclaration) : typeToString(source); - const targetType = symbolValueDeclarationIsContextSensitive(target.symbol) ? typeToString(target, target.symbol.valueDeclaration) : typeToString(target); - - if ((globalStringType === source && stringType === target) || - (globalNumberType === source && numberType === target) || - (globalBooleanType === source && booleanType === target) || - (getGlobalESSymbolType(/*reportErrors*/ false) === source && esSymbolType === target)) { - reportError(Diagnostics._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, targetType, sourceType); - } - } - - /** - * Try and elaborate array and tuple errors. Returns false - * if we have found an elaboration, or we should ignore - * any other elaborations when relating the `source` and - * `target` types. - */ - function tryElaborateArrayLikeErrors(source: Type, target: Type, reportErrors: boolean): boolean { - /** - * The spec for elaboration is: - * - If the source is a readonly tuple and the target is a mutable array or tuple, elaborate on mutability and skip property elaborations. - * - If the source is a tuple then skip property elaborations if the target is an array or tuple. - * - If the source is a readonly array and the target is a mutable array or tuple, elaborate on mutability and skip property elaborations. - * - If the source an array then skip property elaborations if the target is a tuple. - */ - if (isTupleType(source)) { - if (source.target.readonly && isMutableArrayOrTuple(target)) { - if (reportErrors) { - reportError(Diagnostics.The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1, typeToString(source), typeToString(target)); - } - return false; - } - return isTupleType(target) || isArrayType(target); - } - if (isReadonlyArrayType(source) && isMutableArrayOrTuple(target)) { - if (reportErrors) { - reportError(Diagnostics.The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1, typeToString(source), typeToString(target)); - } - return false; - } - if (isTupleType(target)) { - return isArrayType(source); - } - return true; - } - - /** - * Compare two types and return - * * Ternary.True if they are related with no assumptions, - * * Ternary.Maybe if they are related with assumptions of other relationships, or - * * Ternary.False if they are not related. - */ - function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, isApparentIntersectionConstituent?: boolean): Ternary { - // Normalize the source and target types: Turn fresh literal types into regular literal types, - // turn deferred type references into regular type references, simplify indexed access and - // conditional types, and resolve substitution types to either the substitution (on the source - // side) or the type variable (on the target side). - let source = getNormalizedType(originalSource, /*writing*/ false); - let target = getNormalizedType(originalTarget, /*writing*/ true); - - // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. - // If so, reporting the `null` and `undefined` in the type is hardly useful. - // First, see if we're even relating an object type to a union. - // Then see if the target is stripped down to a single non-union type. - // Note - // * We actually want to remove null and undefined naively here (rather than using getNonNullableType), - // since we don't want to end up with a worse error like "`Foo` is not assignable to `NonNullable`" - // when dealing with generics. - // * We also don't deal with primitive source types, since we already halt elaboration below. - if (target.flags & TypeFlags.Union && source.flags & TypeFlags.Object && - (target as UnionType).types.length <= 3 && maybeTypeOfKind(target, TypeFlags.Nullable)) { - const nullStrippedTarget = extractTypesOfKind(target, ~TypeFlags.Nullable); - if (!(nullStrippedTarget.flags & (TypeFlags.Union | TypeFlags.Never))) { - target = nullStrippedTarget; - } - } - - // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases - if (source === target) return Ternary.True; - - if (relation === identityRelation) { - return isIdenticalTo(source, target); - } - - if (relation === comparableRelation && !(target.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(target, source, relation) || - isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; - - const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - const isPerformingExcessPropertyChecks = !isApparentIntersectionConstituent && (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral); - if (isPerformingExcessPropertyChecks) { - if (hasExcessProperties(source, target, reportErrors)) { - if (reportErrors) { - reportRelationError(headMessage, source, target); - } - return Ternary.False; - } - } - - const isPerformingCommonPropertyChecks = relation !== comparableRelation && !isApparentIntersectionConstituent && - source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType && - target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) && - (getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source)); - if (isPerformingCommonPropertyChecks && !hasCommonProperties(source, target, isComparingJsxAttributes)) { - if (reportErrors) { - const calls = getSignaturesOfType(source, SignatureKind.Call); - const constructs = getSignaturesOfType(source, SignatureKind.Construct); - if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || - constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { - reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, typeToString(source), typeToString(target)); - } - else { - reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); - } - } - return Ternary.False; - } - - let result = Ternary.False; - const saveErrorInfo = captureErrorCalculationState(); - let isIntersectionConstituent = !!isApparentIntersectionConstituent; - - // Note that these checks are specifically ordered to produce correct results. In particular, - // we need to deconstruct unions before intersections (because unions are always at the top), - // and we need to handle "each" relations before "some" relations for the same kind of type. - if (source.flags & TypeFlags.Union) { - result = relation === comparableRelation ? - someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)) : - eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); - } - else { - if (target.flags & TypeFlags.Union) { - result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); - } - else if (target.flags & TypeFlags.Intersection) { - isIntersectionConstituent = true; // set here to affect the following trio of checks - result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors); - if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks)) { - // Validate against excess props using the original `source` - if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*isIntersectionConstituent*/ false)) { - return Ternary.False; - } - } - } - else if (source.flags & TypeFlags.Intersection) { - // Check to see if any constituents of the intersection are immediately related to the target. - // - // Don't report errors though. Checking whether a constituent is related to the source is not actually - // useful and leads to some confusing error messages. Instead it is better to let the below checks - // take care of this, or to not elaborate at all. For instance, - // - // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. - // - // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection - // than to report that 'D' is not assignable to 'A' or 'B'. - // - // - For a primitive type or type parameter (such as 'number = A & B') there is no point in - // breaking the intersection apart. - result = someTypeRelatedToType(source, target, /*reportErrors*/ false); - } - if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) { - if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) { - resetErrorInfo(saveErrorInfo); - } - } - } - if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) { - // The combined constraint of an intersection type is the intersection of the constraints of - // the constituents. When an intersection type contains instantiable types with union type - // constraints, there are situations where we need to examine the combined constraint. One is - // when the target is a union type. Another is when the intersection contains types belonging - // to one of the disjoint domains. For example, given type variables T and U, each with the - // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and - // we need to check this constraint against a union on the target side. Also, given a type - // variable V constrained to 'string | number', 'V & number' has a combined constraint of - // 'string & number | number & number' which reduces to just 'number'. - // This also handles type parameters, as a type parameter with a union constraint compared against a union - // needs to have its constraint hoisted into an intersection with said type parameter, this way - // the type param can be compared with itself in the target (with the influence of its constraint to match other parts) - // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)` - const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source).types: [source], !!(target.flags & TypeFlags.Union)); - if (constraint && (source.flags & TypeFlags.Intersection || target.flags & TypeFlags.Union)) { - if (everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself - // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this - if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) { - resetErrorInfo(saveErrorInfo); - } - } - } - } - - if (!result && reportErrors) { - source = originalSource.aliasSymbol ? originalSource : source; - target = originalTarget.aliasSymbol ? originalTarget : target; - let maybeSuppress = overrideNextErrorInfo > 0; - if (maybeSuppress) { - overrideNextErrorInfo--; - } - if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { - const currentError = errorInfo; - tryElaborateArrayLikeErrors(source, target, reportErrors); - if (errorInfo !== currentError) { - maybeSuppress = !!errorInfo; - } - } - if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) { - tryElaborateErrorsForPrimitivesAndObjects(source, target); - } - else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) { - reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead); - } - else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) { - const targetTypes = (target as IntersectionType).types; - const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode); - const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode); - if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType && - (contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) { - // do not report top error - return result; - } - } - if (!headMessage && maybeSuppress) { - lastSkippedInfo = [source, target]; - // Used by, eg, missing property checking to replace the top-level message with a more informative one - return result; - } - reportRelationError(headMessage, source, target); - } - return result; - } - - function isIdenticalTo(source: Type, target: Type): Ternary { - let result: Ternary; - const flags = source.flags & target.flags; - if (flags & TypeFlags.Object || flags & TypeFlags.IndexedAccess || flags & TypeFlags.Conditional || flags & TypeFlags.Index || flags & TypeFlags.Substitution) { - return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ false); - } - if (flags & (TypeFlags.Union | TypeFlags.Intersection)) { - if (result = eachTypeRelatedToSomeType(source, target)) { - if (result &= eachTypeRelatedToSomeType(target, source)) { - return result; - } - } - } - return Ternary.False; - } - - function getTypeOfPropertyInTypes(types: Type[], name: __String) { - const appendPropType = (propTypes: Type[] | undefined, type: Type) => { - type = getApparentType(type); - const prop = type.flags & TypeFlags.UnionOrIntersection ? getPropertyOfUnionOrIntersectionType(type, name) : getPropertyOfObjectType(type, name); - const propType = prop && getTypeOfSymbol(prop) || isNumericLiteralName(name) && getIndexTypeOfType(type, IndexKind.Number) || getIndexTypeOfType(type, IndexKind.String) || undefinedType; - return append(propTypes, propType); - }; - return getUnionType(reduceLeft(types, appendPropType, /*initial*/ undefined) || emptyArray); - } - - function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { - if (!isExcessPropertyCheckTarget(target) || !noImplicitAny && getObjectFlags(target) & ObjectFlags.JSLiteral) { - return false; // Disable excess property checks on JS literals to simulate having an implicit "index signature" - but only outside of noImplicitAny - } - const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - if ((relation === assignableRelation || relation === comparableRelation) && - (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { - return false; - } - let reducedTarget = target; - let checkTypes: Type[] | undefined; - if (target.flags & TypeFlags.Union) { - reducedTarget = findMatchingDiscriminantType(source, target, isRelatedTo) || filterPrimitivesIfContainsNonPrimitive(target); - checkTypes = reducedTarget.flags & TypeFlags.Union ? (reducedTarget).types : [reducedTarget]; - } - for (const prop of getPropertiesOfType(source)) { - if (shouldCheckAsExcessProperty(prop, source.symbol)) { - if (!isKnownProperty(reducedTarget, prop.escapedName, isComparingJsxAttributes)) { - if (reportErrors) { - // Report error in terms of object types in the target as those are the only ones - // we check in isKnownProperty. - const errorTarget = filterType(reducedTarget, isExcessPropertyCheckTarget); - // We know *exactly* where things went wrong when comparing the types. - // Use this property as the error node as this will be more helpful in - // reasoning about what went wrong. - if (!errorNode) return Debug.fail(); - if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode) || isJsxOpeningLikeElement(errorNode.parent)) { - // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. - // However, using an object-literal error message will be very confusing to the users so we give different a message. - // TODO: Spelling suggestions for excess jsx attributes (needs new diagnostic messages) - if (prop.valueDeclaration && isJsxAttribute(prop.valueDeclaration) && getSourceFileOfNode(errorNode) === getSourceFileOfNode(prop.valueDeclaration.name)) { - // Note that extraneous children (as in `extra`) don't pass this check, - // since `children` is a SyntaxKind.PropertySignature instead of a SyntaxKind.JsxAttribute. - errorNode = prop.valueDeclaration.name; - } - reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(errorTarget)); - } - else { - // use the property's value declaration if the property is assigned inside the literal itself - const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations); - let suggestion; - if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration) && getSourceFileOfNode(objectLiteralDeclaration) === getSourceFileOfNode(errorNode)) { - const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike; - Debug.assertNode(propDeclaration, isObjectLiteralElementLike); - - errorNode = propDeclaration; - - const name = propDeclaration.name!; - if (isIdentifier(name)) { - suggestion = getSuggestionForNonexistentProperty(name, errorTarget); - } - } - if (suggestion !== undefined) { - reportError(Diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, - symbolToString(prop), typeToString(errorTarget), suggestion); - } - else { - reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - symbolToString(prop), typeToString(errorTarget)); - } - } - } - return true; - } - if (checkTypes && !isRelatedTo(getTypeOfSymbol(prop), getTypeOfPropertyInTypes(checkTypes, prop.escapedName), reportErrors)) { - if (reportErrors) { - reportIncompatibleError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(prop)); - } - return true; - } - } - } - return false; - } - - function shouldCheckAsExcessProperty(prop: Symbol, container: Symbol) { - return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent === container.valueDeclaration; - } - - function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary { - let result = Ternary.True; - const sourceTypes = source.types; - for (const sourceType of sourceTypes) { - const related = typeRelatedToSomeType(sourceType, target, /*reportErrors*/ false); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { - const targetTypes = target.types; - if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) { - return Ternary.True; - } - for (const type of targetTypes) { - const related = isRelatedTo(source, type, /*reportErrors*/ false); - if (related) { - return related; - } - } - if (reportErrors) { - const bestMatchingType = getBestMatchingType(source, target, isRelatedTo); - isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true); - } - return Ternary.False; - } - - function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary { - let result = Ternary.True; - const targetTypes = target.types; - for (const targetType of targetTypes) { - const related = isRelatedTo(source, targetType, reportErrors, /*headMessage*/ undefined, /*isIntersectionConstituent*/ true); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { - const sourceTypes = source.types; - if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) { - return Ternary.True; - } - const len = sourceTypes.length; - for (let i = 0; i < len; i++) { - const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1); - if (related) { - return related; - } - } - return Ternary.False; - } - - function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { - let result = Ternary.True; - const sourceTypes = source.types; - for (const sourceType of sourceTypes) { - const related = isRelatedTo(sourceType, target, reportErrors); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function typeArgumentsRelatedTo(sources: readonly Type[] = emptyArray, targets: readonly Type[] = emptyArray, variances: readonly VarianceFlags[] = emptyArray, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { - if (sources.length !== targets.length && relation === identityRelation) { - return Ternary.False; - } - const length = sources.length <= targets.length ? sources.length : targets.length; - let result = Ternary.True; - for (let i = 0; i < length; i++) { - // When variance information isn't available we default to covariance. This happens - // in the process of computing variance information for recursive types and when - // comparing 'this' type arguments. - const varianceFlags = i < variances.length ? variances[i] : VarianceFlags.Covariant; - const variance = varianceFlags & VarianceFlags.VarianceMask; - // We ignore arguments for independent type parameters (because they're never witnessed). - if (variance !== VarianceFlags.Independent) { - const s = sources[i]; - const t = targets[i]; - let related = Ternary.True; - if (varianceFlags & VarianceFlags.Unmeasurable) { - // Even an `Unmeasurable` variance works out without a structural check if the source and target are _identical_. - // We can't simply assume invariance, because `Unmeasurable` marks nonlinear relations, for example, a relation tained by - // the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be) - related = relation === identityRelation ? isRelatedTo(s, t, /*reportErrors*/ false) : compareTypesIdentical(s, t); - } - else if (variance === VarianceFlags.Covariant) { - related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); - } - else if (variance === VarianceFlags.Contravariant) { - related = isRelatedTo(t, s, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); - } - else if (variance === VarianceFlags.Bivariant) { - // In the bivariant case we first compare contravariantly without reporting - // errors. Then, if that doesn't succeed, we compare covariantly with error - // reporting. Thus, error elaboration will be based on the the covariant check, - // which is generally easier to reason about. - related = isRelatedTo(t, s, /*reportErrors*/ false); - if (!related) { - related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); - } - } - else { - // In the invariant case we first compare covariantly, and only when that - // succeeds do we proceed to compare contravariantly. Thus, error elaboration - // will typically be based on the covariant check. - related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); - if (related) { - related &= isRelatedTo(t, s, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); - } - } - if (!related) { - return Ternary.False; - } - result &= related; - } - } - return result; - } - - // Determine if possibly recursive types are related. First, check if the result is already available in the global cache. - // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true. - // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are - // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion - // and issue an error. Otherwise, actually compare the structure of the two types. - function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { - if (overflow) { - return Ternary.False; - } - const id = getRelationKey(source, target, isIntersectionConstituent, relation); - const entry = relation.get(id); - if (entry !== undefined) { - if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) { - // We are elaborating errors and the cached result is an unreported failure. The result will be reported - // as a failure, and should be updated as a reported failure by the bottom of this function. - } - else { - if (outofbandVarianceMarkerHandler) { - // We're in the middle of variance checking - integrate any unmeasurable/unreliable flags from this cached component - const saved = entry & RelationComparisonResult.ReportsMask; - if (saved & RelationComparisonResult.ReportsUnmeasurable) { - instantiateType(source, reportUnmeasurableMarkers); - } - if (saved & RelationComparisonResult.ReportsUnreliable) { - instantiateType(source, reportUnreliableMarkers); - } - } - return entry & RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; - } - } - if (!maybeKeys) { - maybeKeys = []; - sourceStack = []; - targetStack = []; - } - else { - for (let i = 0; i < maybeCount; i++) { - // If source and target are already being compared, consider them related with assumptions - if (id === maybeKeys[i]) { - return Ternary.Maybe; - } - } - if (depth === 100) { - overflow = true; - return Ternary.False; - } - } - const maybeStart = maybeCount; - maybeKeys[maybeCount] = id; - maybeCount++; - sourceStack[depth] = source; - targetStack[depth] = target; - depth++; - const saveExpandingFlags = expandingFlags; - if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= ExpandingFlags.Source; - if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= ExpandingFlags.Target; - let originalHandler: typeof outofbandVarianceMarkerHandler; - let propagatingVarianceFlags: RelationComparisonResult = 0; - if (outofbandVarianceMarkerHandler) { - originalHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = onlyUnreliable => { - propagatingVarianceFlags |= onlyUnreliable ? RelationComparisonResult.ReportsUnreliable : RelationComparisonResult.ReportsUnmeasurable; - return originalHandler!(onlyUnreliable); - }; - } - const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent) : Ternary.Maybe; - if (outofbandVarianceMarkerHandler) { - outofbandVarianceMarkerHandler = originalHandler; - } - expandingFlags = saveExpandingFlags; - depth--; - if (result) { - if (result === Ternary.True || depth === 0) { - // If result is definitely true, record all maybe keys as having succeeded - for (let i = maybeStart; i < maybeCount; i++) { - relation.set(maybeKeys[i], RelationComparisonResult.Succeeded | propagatingVarianceFlags); - } - maybeCount = maybeStart; - } - } - else { - // A false result goes straight into global cache (when something is false under - // assumptions it will also be false without assumptions) - relation.set(id, (reportErrors ? RelationComparisonResult.Reported : 0) | RelationComparisonResult.Failed | propagatingVarianceFlags); - maybeCount = maybeStart; - } - return result; - } - - function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { - const flags = source.flags & target.flags; - if (relation === identityRelation && !(flags & TypeFlags.Object)) { - if (flags & TypeFlags.Index) { - return isRelatedTo((source).type, (target).type, /*reportErrors*/ false); - } - let result = Ternary.False; - if (flags & TypeFlags.IndexedAccess) { - if (result = isRelatedTo((source).objectType, (target).objectType, /*reportErrors*/ false)) { - if (result &= isRelatedTo((source).indexType, (target).indexType, /*reportErrors*/ false)) { - return result; - } - } - } - if (flags & TypeFlags.Conditional) { - if ((source).root.isDistributive === (target).root.isDistributive) { - if (result = isRelatedTo((source).checkType, (target).checkType, /*reportErrors*/ false)) { - if (result &= isRelatedTo((source).extendsType, (target).extendsType, /*reportErrors*/ false)) { - if (result &= isRelatedTo(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target), /*reportErrors*/ false)) { - if (result &= isRelatedTo(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target), /*reportErrors*/ false)) { - return result; - } - } - } - } - } - } - if (flags & TypeFlags.Substitution) { - return isRelatedTo((source).substitute, (target).substitute, /*reportErrors*/ false); - } - return Ternary.False; - } - - let result: Ternary; - let originalErrorInfo: DiagnosticMessageChain | undefined; - let varianceCheckFailed = false; - const saveErrorInfo = captureErrorCalculationState(); - - // We limit alias variance probing to only object and conditional types since their alias behavior - // is more predictable than other, interned types, which may or may not have an alias depending on - // the order in which things were checked. - if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol && - source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol && - !(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) { - const variances = getAliasVariances(source.aliasSymbol); - const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent); - if (varianceResult !== undefined) { - return varianceResult; - } - } - - if (target.flags & TypeFlags.TypeParameter) { - // A source type { [P in Q]: X } is related to a target type T if keyof T is related to Q and X is related to T[Q]. - if (getObjectFlags(source) & ObjectFlags.Mapped && isRelatedTo(getIndexType(target), getConstraintTypeFromMappedType(source))) { - if (!(getMappedTypeModifiers(source) & MappedTypeModifiers.IncludeOptional)) { - const templateType = getTemplateTypeFromMappedType(source); - const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(source)); - if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) { - return result; - } - } - } - } - else if (target.flags & TypeFlags.Index) { - // A keyof S is related to a keyof T if T is related to S. - if (source.flags & TypeFlags.Index) { - if (result = isRelatedTo((target).type, (source).type, /*reportErrors*/ false)) { - return result; - } - } - // A type S is assignable to keyof T if S is assignable to keyof C, where C is the - // simplified form of T or, if T doesn't simplify, the constraint of T. - const constraint = getSimplifiedTypeOrConstraint((target).type); - if (constraint) { - // We require Ternary.True here such that circular constraints don't cause - // false positives. For example, given 'T extends { [K in keyof T]: string }', - // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when - // related to other types. - if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) { - return Ternary.True; - } - } - } - else if (target.flags & TypeFlags.IndexedAccess) { - // A type S is related to a type T[K] if S is related to C, where C is the base - // constraint of T[K] for writing. - if (relation !== identityRelation) { - const objectType = (target).objectType; - const indexType = (target).indexType; - const baseObjectType = getBaseConstraintOfType(objectType) || objectType; - const baseIndexType = getBaseConstraintOfType(indexType) || indexType; - if (!isGenericObjectType(baseObjectType) && !isGenericIndexType(baseIndexType)) { - const accessFlags = AccessFlags.Writing | (baseObjectType !== objectType ? AccessFlags.NoIndexSignatures : 0); - const constraint = getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, /*accessNode*/ undefined, accessFlags); - if (constraint && (result = isRelatedTo(source, constraint, reportErrors))) { - return result; - } - } - } - } - else if (isGenericMappedType(target)) { - // A source type T is related to a target type { [P in X]: T[P] } - const template = getTemplateTypeFromMappedType(target); - const modifiers = getMappedTypeModifiers(target); - if (!(modifiers & MappedTypeModifiers.ExcludeOptional)) { - if (template.flags & TypeFlags.IndexedAccess && (template).objectType === source && - (template).indexType === getTypeParameterFromMappedType(target)) { - return Ternary.True; - } - if (!isGenericMappedType(source)) { - const targetConstraint = getConstraintTypeFromMappedType(target); - const sourceKeys = getIndexType(source, /*stringsOnly*/ undefined, /*noIndexSignatures*/ true); - const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional; - const filteredByApplicability = includeOptional ? intersectTypes(targetConstraint, sourceKeys) : undefined; - // A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X. - // A source type T is related to a target type { [P in Q]?: X } if some constituent Q' of Q is related to keyof T and T[Q'] is related to X. - if (includeOptional - ? !(filteredByApplicability!.flags & TypeFlags.Never) - : isRelatedTo(targetConstraint, sourceKeys)) { - const typeParameter = getTypeParameterFromMappedType(target); - const indexingType = filteredByApplicability ? getIntersectionType([filteredByApplicability, typeParameter]) : typeParameter; - const indexedAccessType = getIndexedAccessType(source, indexingType); - const templateType = getTemplateTypeFromMappedType(target); - if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { - return result; - } - } - originalErrorInfo = errorInfo; - resetErrorInfo(saveErrorInfo); - } - } - } - - if (source.flags & TypeFlags.TypeVariable) { - if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) { - // A type S[K] is related to a type T[J] if S is related to T and K is related to J. - if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) { - result &= isRelatedTo((source).indexType, (target).indexType, reportErrors); - } - if (result) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - else { - const constraint = getConstraintOfType(source); - if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { - // A type variable with no constraint is not related to the non-primitive object type. - if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed - else if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) { - resetErrorInfo(saveErrorInfo); - return result; - } - // slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example - else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - } - else if (source.flags & TypeFlags.Index) { - if (result = isRelatedTo(keyofConstraintType, target, reportErrors)) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - else if (source.flags & TypeFlags.Conditional) { - if (target.flags & TypeFlags.Conditional) { - // Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if - // one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2, - // and Y1 is related to Y2. - const sourceParams = (source as ConditionalType).root.inferTypeParameters; - let sourceExtends = (source).extendsType; - let mapper: TypeMapper | undefined; - if (sourceParams) { - // If the source has infer type parameters, we instantiate them in the context of the target - const ctx = createInferenceContext(sourceParams, /*signature*/ undefined, InferenceFlags.None, isRelatedTo); - inferTypes(ctx.inferences, (target).extendsType, sourceExtends, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); - sourceExtends = instantiateType(sourceExtends, ctx.mapper); - mapper = ctx.mapper; - } - if (isTypeIdenticalTo(sourceExtends, (target).extendsType) && - (isRelatedTo((source).checkType, (target).checkType) || isRelatedTo((target).checkType, (source).checkType))) { - if (result = isRelatedTo(instantiateType(getTrueTypeFromConditionalType(source), mapper), getTrueTypeFromConditionalType(target), reportErrors)) { - result &= isRelatedTo(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target), reportErrors); - } - if (result) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - } - else { - const distributiveConstraint = getConstraintOfDistributiveConditionalType(source); - if (distributiveConstraint) { - if (result = isRelatedTo(distributiveConstraint, target, reportErrors)) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - const defaultConstraint = getDefaultConstraintOfConditionalType(source); - if (defaultConstraint) { - if (result = isRelatedTo(defaultConstraint, target, reportErrors)) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - } - } - else { - // An empty object type is related to any mapped type that includes a '?' modifier. - if (relation !== subtypeRelation && relation !== strictSubtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) { - return Ternary.True; - } - if (isGenericMappedType(target)) { - if (isGenericMappedType(source)) { - if (result = mappedTypeRelatedTo(source, target, reportErrors)) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - return Ternary.False; - } - const sourceIsPrimitive = !!(source.flags & TypeFlags.Primitive); - if (relation !== identityRelation) { - source = getApparentType(source); - } - else if (isGenericMappedType(source)) { - return Ternary.False; - } - if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target && - !(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) { - // We have type references to the same generic type, and the type references are not marker - // type references (which are intended by be compared structurally). Obtain the variance - // information for the type parameters and relate the type arguments accordingly. - const variances = getVariances((source).target); - const varianceResult = relateVariances(getTypeArguments(source), getTypeArguments(target), variances, isIntersectionConstituent); - if (varianceResult !== undefined) { - return varianceResult; - } - } - else if (isReadonlyArrayType(target) ? isArrayType(source) || isTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) { - if (relation !== identityRelation) { - return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors); - } - else { - // By flags alone, we know that the `target` is a readonly array while the source is a normal array or tuple - // or `target` is an array and source is a tuple - in both cases the types cannot be identical, by construction - return Ternary.False; - } - } - // Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})` - // and not `{} <- fresh({}) <- {[idx: string]: any}` - else if ((relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) { - return Ternary.False; - } - // Even if relationship doesn't hold for unions, intersections, or generic type references, - // it may hold in a structural comparison. - // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates - // to X. Failing both of those we want to check if the aggregation of A and B's members structurally - // relates to X. Thus, we include intersection types on the source side here. - if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { - // Report structural errors only if we haven't reported any errors yet - const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive; - result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent); - if (result) { - result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors); - if (result) { - result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportStructuralErrors); - if (result) { - result &= indexTypesRelatedTo(source, target, IndexKind.String, sourceIsPrimitive, reportStructuralErrors); - if (result) { - result &= indexTypesRelatedTo(source, target, IndexKind.Number, sourceIsPrimitive, reportStructuralErrors); - } - } - } - } - if (varianceCheckFailed && result) { - errorInfo = originalErrorInfo || errorInfo || saveErrorInfo.errorInfo; // Use variance error (there is no structural one) and return false - } - else if (result) { - return result; - } - } - // If S is an object type and T is a discriminated union, S may be related to T if - // there exists a constituent of T for every combination of the discriminants of S - // with respect to T. We do not report errors here, as we will use the existing - // error result from checking each constituent of the union. - if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Union) { - const objectOnlyTarget = extractTypesOfKind(target, TypeFlags.Object); - if (objectOnlyTarget.flags & TypeFlags.Union) { - const result = typeRelatedToDiscriminatedType(source, objectOnlyTarget as UnionType); - if (result) { - return result; - } - } - } - } - return Ternary.False; - - function relateVariances(sourceTypeArguments: readonly Type[] | undefined, targetTypeArguments: readonly Type[] | undefined, variances: VarianceFlags[], isIntersectionConstituent: boolean) { - if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, isIntersectionConstituent)) { - return result; - } - if (some(variances, v => !!(v & VarianceFlags.AllowsStructuralFallback))) { - // If some type parameter was `Unmeasurable` or `Unreliable`, and we couldn't pass by assuming it was identical, then we - // have to allow a structural fallback check - // We elide the variance-based error elaborations, since those might not be too helpful, since we'll potentially - // be assuming identity of the type parameter. - originalErrorInfo = undefined; - resetErrorInfo(saveErrorInfo); - return undefined; - } - const allowStructuralFallback = targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances); - varianceCheckFailed = !allowStructuralFallback; - // The type arguments did not relate appropriately, but it may be because we have no variance - // information (in which case typeArgumentsRelatedTo defaulted to covariance for all type - // arguments). It might also be the case that the target type has a 'void' type argument for - // a covariant type parameter that is only used in return positions within the generic type - // (in which case any type argument is permitted on the source side). In those cases we proceed - // with a structural comparison. Otherwise, we know for certain the instantiations aren't - // related and we can return here. - if (variances !== emptyArray && !allowStructuralFallback) { - // In some cases generic types that are covariant in regular type checking mode become - // invariant in --strictFunctionTypes mode because one or more type parameters are used in - // both co- and contravariant positions. In order to make it easier to diagnose *why* such - // types are invariant, if any of the type parameters are invariant we reset the reported - // errors and instead force a structural comparison (which will include elaborations that - // reveal the reason). - // We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`, - // we can return `False` early here to skip calculating the structural error message we don't need. - if (varianceCheckFailed && !(reportErrors && some(variances, v => (v & VarianceFlags.VarianceMask) === VarianceFlags.Invariant))) { - return Ternary.False; - } - // We remember the original error information so we can restore it in case the structural - // comparison unexpectedly succeeds. This can happen when the structural comparison result - // is a Ternary.Maybe for example caused by the recursion depth limiter. - originalErrorInfo = errorInfo; - resetErrorInfo(saveErrorInfo); - } - } - } - - function reportUnmeasurableMarkers(p: TypeParameter) { - if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { - outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false); - } - return p; - } - - function reportUnreliableMarkers(p: TypeParameter) { - if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { - outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true); - } - return p; - } - - // A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is - // related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice - // that S and T are contra-variant whereas X and Y are co-variant. - function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary { - const modifiersRelated = relation === comparableRelation || (relation === identityRelation ? getMappedTypeModifiers(source) === getMappedTypeModifiers(target) : - getCombinedMappedTypeOptionality(source) <= getCombinedMappedTypeOptionality(target)); - if (modifiersRelated) { - let result: Ternary; - const targetConstraint = getConstraintTypeFromMappedType(target); - const sourceConstraint = instantiateType(getConstraintTypeFromMappedType(source), getCombinedMappedTypeOptionality(source) < 0 ? reportUnmeasurableMarkers : reportUnreliableMarkers); - if (result = isRelatedTo(targetConstraint, sourceConstraint, reportErrors)) { - const mapper = createTypeMapper([getTypeParameterFromMappedType(source)], [getTypeParameterFromMappedType(target)]); - return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(source), mapper), getTemplateTypeFromMappedType(target), reportErrors); - } - } - return Ternary.False; - } - - function typeRelatedToDiscriminatedType(source: Type, target: UnionType) { - // 1. Generate the combinations of discriminant properties & types 'source' can satisfy. - // a. If the number of combinations is above a set limit, the comparison is too complex. - // 2. Filter 'target' to the subset of types whose discriminants exist in the matrix. - // a. If 'target' does not satisfy all discriminants in the matrix, 'source' is not related. - // 3. For each type in the filtered 'target', determine if all non-discriminant properties of - // 'target' are related to a property in 'source'. - // - // NOTE: See ~/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts - // for examples. - - const sourceProperties = getPropertiesOfObjectType(source); - const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target); - if (!sourcePropertiesFiltered) return Ternary.False; - - // Though we could compute the number of combinations as we generate - // the matrix, this would incur additional memory overhead due to - // array allocations. To reduce this overhead, we first compute - // the number of combinations to ensure we will not surpass our - // fixed limit before incurring the cost of any allocations: - let numCombinations = 1; - for (const sourceProperty of sourcePropertiesFiltered) { - numCombinations *= countTypes(getTypeOfSymbol(sourceProperty)); - if (numCombinations > 25) { - // We've reached the complexity limit. - return Ternary.False; - } - } - - // Compute the set of types for each discriminant property. - const sourceDiscriminantTypes: Type[][] = new Array(sourcePropertiesFiltered.length); - const excludedProperties = createUnderscoreEscapedMap(); - for (let i = 0; i < sourcePropertiesFiltered.length; i++) { - const sourceProperty = sourcePropertiesFiltered[i]; - const sourcePropertyType = getTypeOfSymbol(sourceProperty); - sourceDiscriminantTypes[i] = sourcePropertyType.flags & TypeFlags.Union - ? (sourcePropertyType as UnionType).types - : [sourcePropertyType]; - excludedProperties.set(sourceProperty.escapedName, true); - } - - // Match each combination of the cartesian product of discriminant properties to one or more - // constituents of 'target'. If any combination does not have a match then 'source' is not relatable. - const discriminantCombinations = cartesianProduct(sourceDiscriminantTypes); - const matchingTypes: Type[] = []; - for (const combination of discriminantCombinations) { - let hasMatch = false; - outer: for (const type of target.types) { - for (let i = 0; i < sourcePropertiesFiltered.length; i++) { - const sourceProperty = sourcePropertiesFiltered[i]; - const targetProperty = getPropertyOfObjectType(type, sourceProperty.escapedName); - if (!targetProperty) continue outer; - if (sourceProperty === targetProperty) continue; - // We compare the source property to the target in the context of a single discriminant type. - const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, /*isIntersectionConstituent*/ false); - // If the target property could not be found, or if the properties were not related, - // then this constituent is not a match. - if (!related) { - continue outer; - } - } - pushIfUnique(matchingTypes, type, equateValues); - hasMatch = true; - } - if (!hasMatch) { - // We failed to match any type for this combination. - return Ternary.False; - } - } - - // Compare the remaining non-discriminant properties of each match. - let result = Ternary.True; - for (const type of matchingTypes) { - result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*isIntersectionConstituent*/ false); - if (result) { - result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false); - if (result) { - result &= signaturesRelatedTo(source, type, SignatureKind.Construct, /*reportStructuralErrors*/ false); - if (result) { - result &= indexTypesRelatedTo(source, type, IndexKind.String, /*sourceIsPrimitive*/ false, /*reportStructuralErrors*/ false); - if (result) { - result &= indexTypesRelatedTo(source, type, IndexKind.Number, /*sourceIsPrimitive*/ false, /*reportStructuralErrors*/ false); - } - } - } - } - if (!result) { - return result; - } - } - return result; - } - - function excludeProperties(properties: Symbol[], excludedProperties: UnderscoreEscapedMap | undefined) { - if (!excludedProperties || properties.length === 0) return properties; - let result: Symbol[] | undefined; - for (let i = 0; i < properties.length; i++) { - if (!excludedProperties.has(properties[i].escapedName)) { - if (result) { - result.push(properties[i]); - } - } - else if (!result) { - result = properties.slice(0, i); - } - } - return result || properties; - } - - function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { - const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial); - const source = getTypeOfSourceProperty(sourceProp); - if (getCheckFlags(targetProp) & CheckFlags.DeferredType && !getSymbolLinks(targetProp).type) { - // Rather than resolving (and normalizing) the type, relate constituent-by-constituent without performing normalization or seconadary passes - const links = getSymbolLinks(targetProp); - Debug.assertDefined(links.deferralParent); - Debug.assertDefined(links.deferralConstituents); - const unionParent = !!(links.deferralParent!.flags & TypeFlags.Union); - let result = unionParent ? Ternary.False : Ternary.True; - const targetTypes = links.deferralConstituents!; - for (const targetType of targetTypes) { - const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ !unionParent); - if (!unionParent) { - if (!related) { - // Can't assign to a target individually - have to fallback to assigning to the _whole_ intersection (which forces normalization) - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); - } - result &= related; - } - else { - if (related) { - return related; - } - } - } - if (unionParent && !result && targetIsOptional) { - result = isRelatedTo(source, undefinedType); - } - if (unionParent && !result && reportErrors) { - // The easiest way to get the right errors here is to un-defer (which may be costly) - // If it turns out this is too costly too often, we can replicate the error handling logic within - // typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union - // type on which to hand discriminable properties, which we are expressly trying to avoid here) - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); - } - return result; - } - else { - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); - } - } - - function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { - const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); - const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); - if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { - const hasDifferingDeclarations = sourceProp.valueDeclaration !== targetProp.valueDeclaration; - if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate && hasDifferingDeclarations) { - if (reportErrors) { - reportError(Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(sourceProp), typeToString(source)); - } - return Ternary.False; - } - if (hasDifferingDeclarations) { - if (reportErrors) { - if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) { - reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp)); - } - else { - reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp), - typeToString(sourcePropFlags & ModifierFlags.Private ? source : target), - typeToString(sourcePropFlags & ModifierFlags.Private ? target : source)); - } - } - return Ternary.False; - } - } - else if (targetPropFlags & ModifierFlags.Protected) { - if (!isValidOverrideOf(sourceProp, targetProp)) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp), - typeToString(getDeclaringClass(sourceProp) || source), typeToString(getDeclaringClass(targetProp) || target)); - } - return Ternary.False; - } - } - else if (sourcePropFlags & ModifierFlags.Protected) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, - symbolToString(targetProp), typeToString(source), typeToString(target)); - } - return Ternary.False; - } - // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, isIntersectionConstituent); - if (!related) { - if (reportErrors) { - reportIncompatibleError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); - } - return Ternary.False; - } - // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { - // TypeScript 1.0 spec (April 2014): 3.8.3 - // S is a subtype of a type T, and T is a supertype of S if ... - // S' and T are object types and, for each member M in T.. - // M is a property and S' contains a property N where - // if M is a required property, N is also a required property - // (M - property in T) - // (N - property in S) - if (reportErrors) { - reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, - symbolToString(targetProp), typeToString(source), typeToString(target)); - } - return Ternary.False; - } - return related; - } - - function reportUnmatchedProperty(source: Type, target: Type, unmatchedProperty: Symbol, requireOptionalProperties: boolean) { - let shouldSkipElaboration = false; - // give specific error in case where private names have the same description - if ( - unmatchedProperty.valueDeclaration - && isNamedDeclaration(unmatchedProperty.valueDeclaration) - && isPrivateIdentifier(unmatchedProperty.valueDeclaration.name) - && isClassDeclaration(source.symbol.valueDeclaration) - ) { - const privateIdentifierDescription = unmatchedProperty.valueDeclaration.name.escapedText; - const symbolTableKey = getSymbolNameForPrivateIdentifier(source.symbol, privateIdentifierDescription); - if (symbolTableKey && !!getPropertyOfType(source, symbolTableKey)) { - const sourceName = source.symbol.valueDeclaration.name; - const targetName = isClassDeclaration(target.symbol.valueDeclaration) ? target.symbol.valueDeclaration.name : undefined; - reportError( - Diagnostics.Property_0_in_type_1_refers_to_a_different_member_that_cannot_be_accessed_from_within_type_2, - diagnosticName(privateIdentifierDescription), - diagnosticName(sourceName || anon), - diagnosticName(targetName || anon), - ); - return; - } - } - const props = arrayFrom(getUnmatchedProperties(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false)); - if (!headMessage || (headMessage.code !== Diagnostics.Class_0_incorrectly_implements_interface_1.code && - headMessage.code !== Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass.code)) { - shouldSkipElaboration = true; // Retain top-level error for interface implementing issues, otherwise omit it - } - if (props.length === 1) { - const propName = symbolToString(unmatchedProperty); - reportError(Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, ...getTypeNamesForErrorDisplay(source, target)); - if (length(unmatchedProperty.declarations)) { - associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations[0], Diagnostics._0_is_declared_here, propName)); - } - if (shouldSkipElaboration && errorInfo) { - overrideNextErrorInfo++; - } - } - else if (tryElaborateArrayLikeErrors(source, target, /*reportErrors*/ false)) { - if (props.length > 5) { // arbitrary cutoff for too-long list form - reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, typeToString(source), typeToString(target), map(props.slice(0, 4), p => symbolToString(p)).join(", "), props.length - 4); - } - else { - reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2, typeToString(source), typeToString(target), map(props, p => symbolToString(p)).join(", ")); - } - if (shouldSkipElaboration && errorInfo) { - overrideNextErrorInfo++; - } - } - // No array like or unmatched property error - just issue top level error (errorInfo = undefined) - } - - function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, isIntersectionConstituent: boolean): Ternary { - - if (relation === identityRelation) { - return propertiesIdenticalTo(source, target, excludedProperties); - } - const requireOptionalProperties = (relation === subtypeRelation || relation === strictSubtypeRelation) && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source); - const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false); - if (unmatchedProperty) { - if (reportErrors) { - reportUnmatchedProperty(source, target, unmatchedProperty, requireOptionalProperties); - } - return Ternary.False; - } - if (isObjectLiteralType(target)) { - for (const sourceProp of excludeProperties(getPropertiesOfType(source), excludedProperties)) { - if (!getPropertyOfObjectType(target, sourceProp.escapedName)) { - const sourceType = getTypeOfSymbol(sourceProp); - if (!(sourceType === undefinedType || sourceType === undefinedWideningType || sourceType === optionalType)) { - if (reportErrors) { - reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target)); - } - return Ternary.False; - } - } - } - } - let result = Ternary.True; - if (isTupleType(target)) { - const targetRestType = getRestTypeOfTupleType(target); - if (targetRestType) { - if (!isTupleType(source)) { - return Ternary.False; - } - const sourceRestType = getRestTypeOfTupleType(source); - if (sourceRestType && !isRelatedTo(sourceRestType, targetRestType, reportErrors)) { - if (reportErrors) { - reportError(Diagnostics.Rest_signatures_are_incompatible); - } - return Ternary.False; - } - const targetCount = getTypeReferenceArity(target) - 1; - const sourceCount = getTypeReferenceArity(source) - (sourceRestType ? 1 : 0); - const sourceTypeArguments = getTypeArguments(source); - for (let i = targetCount; i < sourceCount; i++) { - const related = isRelatedTo(sourceTypeArguments[i], targetRestType, reportErrors); - if (!related) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_incompatible_with_rest_element_type, "" + i); - } - return Ternary.False; - } - result &= related; - } - } - } - // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ - // from the target union, across all members - const properties = getPropertiesOfType(target); - const numericNamesOnly = isTupleType(source) && isTupleType(target); - for (const targetProp of excludeProperties(properties, excludedProperties)) { - const name = targetProp.escapedName; - if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { - const sourceProp = getPropertyOfType(source, name); - if (sourceProp && sourceProp !== targetProp) { - const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, isIntersectionConstituent); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - } - return result; - } - - function propertiesIdenticalTo(source: Type, target: Type, excludedProperties: UnderscoreEscapedMap | undefined): Ternary { - if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) { - return Ternary.False; - } - const sourceProperties = excludeProperties(getPropertiesOfObjectType(source), excludedProperties); - const targetProperties = excludeProperties(getPropertiesOfObjectType(target), excludedProperties); - if (sourceProperties.length !== targetProperties.length) { - return Ternary.False; - } - let result = Ternary.True; - for (const sourceProp of sourceProperties) { - const targetProp = getPropertyOfObjectType(target, sourceProp.escapedName); - if (!targetProp) { - return Ternary.False; - } - const related = compareProperties(sourceProp, targetProp, isRelatedTo); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean): Ternary { - if (relation === identityRelation) { - return signaturesIdenticalTo(source, target, kind); - } - if (target === anyFunctionType || source === anyFunctionType) { - return Ternary.True; - } - - const sourceIsJSConstructor = source.symbol && isJSConstructor(source.symbol.valueDeclaration); - const targetIsJSConstructor = target.symbol && isJSConstructor(target.symbol.valueDeclaration); - - const sourceSignatures = getSignaturesOfType(source, (sourceIsJSConstructor && kind === SignatureKind.Construct) ? - SignatureKind.Call : kind); - const targetSignatures = getSignaturesOfType(target, (targetIsJSConstructor && kind === SignatureKind.Construct) ? - SignatureKind.Call : kind); - - if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) { - if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) { - // An abstract constructor type is not assignable to a non-abstract constructor type - // as it would otherwise be possible to new an abstract class. Note that the assignability - // check we perform for an extends clause excludes construct signatures from the target, - // so this check never proceeds. - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); - } - return Ternary.False; - } - if (!constructorVisibilitiesAreCompatible(sourceSignatures[0], targetSignatures[0], reportErrors)) { - return Ternary.False; - } - } - - let result = Ternary.True; - const saveErrorInfo = captureErrorCalculationState(); - const incompatibleReporter = kind === SignatureKind.Construct ? reportIncompatibleConstructSignatureReturn : reportIncompatibleCallSignatureReturn; - - if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) { - // We have instantiations of the same anonymous type (which typically will be the type of a - // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We erase type parameters - // as they are known to always be the same. - for (let i = 0; i < targetSignatures.length; i++) { - const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, incompatibleReporter(sourceSignatures[i], targetSignatures[i])); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - else if (sourceSignatures.length === 1 && targetSignatures.length === 1) { - // For simple functions (functions with a single signature) we only erase type parameters for - // the comparable relation. Otherwise, if the source signature is generic, we instantiate it - // in the context of the target signature before checking the relationship. Ideally we'd do - // this regardless of the number of signatures, but the potential costs are prohibitive due - // to the quadratic nature of the logic below. - const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks; - result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors, incompatibleReporter(sourceSignatures[0], targetSignatures[0])); - } - else { - outer: for (const t of targetSignatures) { - // Only elaborate errors from the first failure - let shouldElaborateErrors = reportErrors; - for (const s of sourceSignatures) { - const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors, incompatibleReporter(s, t)); - if (related) { - result &= related; - resetErrorInfo(saveErrorInfo); - continue outer; - } - shouldElaborateErrors = false; - } - - if (shouldElaborateErrors) { - reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1, - typeToString(source), - signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind)); - } - return Ternary.False; - } - } - return result; - } - - function reportIncompatibleCallSignatureReturn(siga: Signature, sigb: Signature) { - if (siga.parameters.length === 0 && sigb.parameters.length === 0) { - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target)); - } - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target)); - } - - function reportIncompatibleConstructSignatureReturn(siga: Signature, sigb: Signature) { - if (siga.parameters.length === 0 && sigb.parameters.length === 0) { - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target)); - } - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target)); - } - - /** - * See signatureAssignableTo, compareSignaturesIdentical - */ - function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary { - return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target, - relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers); - } - - function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary { - const sourceSignatures = getSignaturesOfType(source, kind); - const targetSignatures = getSignaturesOfType(target, kind); - if (sourceSignatures.length !== targetSignatures.length) { - return Ternary.False; - } - let result = Ternary.True; - for (let i = 0; i < sourceSignatures.length; i++) { - const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { - let result = Ternary.True; - for (const prop of getPropertiesOfObjectType(source)) { - if (isIgnoredJsxProperty(source, prop)) { - continue; - } - // Skip over symbol-named members - if (prop.nameType && prop.nameType.flags & TypeFlags.UniqueESSymbol) { - continue; - } - if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) { - const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors); - if (!related) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); - } - return Ternary.False; - } - result &= related; - } - } - return result; - } - - function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean) { - const related = isRelatedTo(sourceInfo.type, targetInfo.type, reportErrors); - if (!related && reportErrors) { - reportError(Diagnostics.Index_signatures_are_incompatible); - } - return related; - } - - function indexTypesRelatedTo(source: Type, target: Type, kind: IndexKind, sourceIsPrimitive: boolean, reportErrors: boolean): Ternary { - if (relation === identityRelation) { - return indexTypesIdenticalTo(source, target, kind); - } - const targetInfo = getIndexInfoOfType(target, kind); - if (!targetInfo || targetInfo.type.flags & TypeFlags.Any && !sourceIsPrimitive) { - // Index signature of type any permits assignment from everything but primitives - return Ternary.True; - } - const sourceInfo = getIndexInfoOfType(source, kind) || - kind === IndexKind.Number && getIndexInfoOfType(source, IndexKind.String); - if (sourceInfo) { - return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors); - } - if (isGenericMappedType(source)) { - // A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U } - // if T is related to U. - return kind === IndexKind.String ? isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, reportErrors) : Ternary.False; - } - if (isObjectTypeWithInferableIndex(source)) { - let related = Ternary.True; - if (kind === IndexKind.String) { - const sourceNumberInfo = getIndexInfoOfType(source, IndexKind.Number); - if (sourceNumberInfo) { - related = indexInfoRelatedTo(sourceNumberInfo, targetInfo, reportErrors); - } - } - if (related) { - related &= eachPropertyRelatedTo(source, targetInfo.type, kind, reportErrors); - } - return related; - } - if (reportErrors) { - reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source)); - } - return Ternary.False; - } - - function indexTypesIdenticalTo(source: Type, target: Type, indexKind: IndexKind): Ternary { - const targetInfo = getIndexInfoOfType(target, indexKind); - const sourceInfo = getIndexInfoOfType(source, indexKind); - if (!sourceInfo && !targetInfo) { - return Ternary.True; - } - if (sourceInfo && targetInfo && sourceInfo.isReadonly === targetInfo.isReadonly) { - return isRelatedTo(sourceInfo.type, targetInfo.type); - } - return Ternary.False; - } - - function constructorVisibilitiesAreCompatible(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) { - if (!sourceSignature.declaration || !targetSignature.declaration) { - return true; - } - - const sourceAccessibility = getSelectedModifierFlags(sourceSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); - const targetAccessibility = getSelectedModifierFlags(targetSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); - - // A public, protected and private signature is assignable to a private signature. - if (targetAccessibility === ModifierFlags.Private) { - return true; - } - - // A public and protected signature is assignable to a protected signature. - if (targetAccessibility === ModifierFlags.Protected && sourceAccessibility !== ModifierFlags.Private) { - return true; - } - - // Only a public signature is assignable to public signature. - if (targetAccessibility !== ModifierFlags.Protected && !sourceAccessibility) { - return true; - } - - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, visibilityToString(sourceAccessibility), visibilityToString(targetAccessibility)); - } - - return false; - } - } - - function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) { - return findMatchingDiscriminantType(source, target, isRelatedTo) || - findMatchingTypeReferenceOrTypeAliasReference(source, target) || - findBestTypeForObjectLiteral(source, target) || - findBestTypeForInvokable(source, target) || - findMostOverlappyType(source, target); - } - - function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary): Type | undefined; - function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue: Type): Type; - function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue?: Type) { - // undefined=unknown, true=discriminated, false=not discriminated - // The state of each type progresses from left to right. Discriminated types stop at 'true'. - const discriminable = target.types.map(_ => undefined) as (boolean | undefined)[]; - for (const [getDiscriminatingType, propertyName] of discriminators) { - let i = 0; - for (const type of target.types) { - const targetType = getTypeOfPropertyOfType(type, propertyName); - if (targetType && related(getDiscriminatingType(), targetType)) { - discriminable[i] = discriminable[i] === undefined ? true : discriminable[i]; - } - else { - discriminable[i] = false; - } - i++; - } - } - const match = discriminable.indexOf(/*searchElement*/ true); - // make sure exactly 1 matches before returning it - return match === -1 || discriminable.indexOf(/*searchElement*/ true, match + 1) !== -1 ? defaultValue : target.types[match]; - } - - /** - * A type is 'weak' if it is an object type with at least one optional property - * and no required properties, call/construct signatures or index signatures - */ - function isWeakType(type: Type): boolean { - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type); - return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 && - !resolved.stringIndexInfo && !resolved.numberIndexInfo && - resolved.properties.length > 0 && - every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional)); - } - if (type.flags & TypeFlags.Intersection) { - return every((type).types, isWeakType); - } - return false; - } - - function hasCommonProperties(source: Type, target: Type, isComparingJsxAttributes: boolean) { - for (const prop of getPropertiesOfType(source)) { - if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { - return true; - } - } - return false; - } - - // Return a type reference where the source type parameter is replaced with the target marker - // type, and flag the result as a marker type reference. - function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) { - const result = createTypeReference(type, map(type.typeParameters, t => t === source ? target : t)); - result.objectFlags |= ObjectFlags.MarkerType; - return result; - } - - function getAliasVariances(symbol: Symbol) { - const links = getSymbolLinks(symbol); - return getVariancesWorker(links.typeParameters, links, (_links, param, marker) => { - const type = getTypeAliasInstantiation(symbol, instantiateTypes(links.typeParameters!, makeUnaryTypeMapper(param, marker))); - type.aliasTypeArgumentsContainsMarker = true; - return type; - }); - } - - // Return an array containing the variance of each type parameter. The variance is effectively - // a digest of the type comparisons that occur for each type argument when instantiations of the - // generic type are structurally compared. We infer the variance information by comparing - // instantiations of the generic type for type arguments with known relations. The function - // returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function - // has been invoked recursively for the given generic type. - function getVariancesWorker(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] { - let variances = cache.variances; - if (!variances) { - // The emptyArray singleton is used to signal a recursive invocation. - cache.variances = emptyArray; - variances = []; - for (const tp of typeParameters) { - let unmeasurable = false; - let unreliable = false; - const oldHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = (onlyUnreliable) => onlyUnreliable ? unreliable = true : unmeasurable = true; - // We first compare instantiations where the type parameter is replaced with - // marker types that have a known subtype relationship. From this we can infer - // invariance, covariance, contravariance or bivariance. - const typeWithSuper = createMarkerType(cache, tp, markerSuperType); - const typeWithSub = createMarkerType(cache, tp, markerSubType); - let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) | - (isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0); - // If the instantiations appear to be related bivariantly it may be because the - // type parameter is independent (i.e. it isn't witnessed anywhere in the generic - // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. - if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(createMarkerType(cache, tp, markerOtherType), typeWithSuper)) { - variance = VarianceFlags.Independent; - } - outofbandVarianceMarkerHandler = oldHandler; - if (unmeasurable || unreliable) { - if (unmeasurable) { - variance |= VarianceFlags.Unmeasurable; - } - if (unreliable) { - variance |= VarianceFlags.Unreliable; - } - } - variances.push(variance); - } - cache.variances = variances; - } - return variances; - } - - function getVariances(type: GenericType): VarianceFlags[] { - // Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters) - if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) { - return emptyArray; - } - return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference); - } - - // Return true if the given type reference has a 'void' type argument for a covariant type parameter. - // See comment at call in recursiveTypeRelatedTo for when this case matters. - function hasCovariantVoidArgument(typeArguments: readonly Type[], variances: VarianceFlags[]): boolean { - for (let i = 0; i < variances.length; i++) { - if ((variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Covariant && typeArguments[i].flags & TypeFlags.Void) { - return true; - } - } - return false; - } - - function isUnconstrainedTypeParameter(type: Type) { - return type.flags & TypeFlags.TypeParameter && !getConstraintOfTypeParameter(type); - } - - function isNonDeferredTypeReference(type: Type): type is TypeReference { - return !!(getObjectFlags(type) & ObjectFlags.Reference) && !(type).node; - } - - function isTypeReferenceWithGenericArguments(type: Type): boolean { - return isNonDeferredTypeReference(type) && some(getTypeArguments(type), t => isUnconstrainedTypeParameter(t) || isTypeReferenceWithGenericArguments(t)); - } - - /** - * getTypeReferenceId(A) returns "111=0-12=1" - * where A.id=111 and number.id=12 - */ - function getTypeReferenceId(type: TypeReference, typeParameters: Type[], depth = 0) { - let result = "" + type.target.id; - for (const t of getTypeArguments(type)) { - if (isUnconstrainedTypeParameter(t)) { - let index = typeParameters.indexOf(t); - if (index < 0) { - index = typeParameters.length; - typeParameters.push(t); - } - result += "=" + index; - } - else if (depth < 4 && isTypeReferenceWithGenericArguments(t)) { - result += "<" + getTypeReferenceId(t as TypeReference, typeParameters, depth + 1) + ">"; - } - else { - result += "-" + t.id; - } - } - return result; - } - - /** - * To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters. - * For other cases, the types ids are used. - */ - function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, relation: Map) { - if (relation === identityRelation && source.id > target.id) { - const temp = source; - source = target; - target = temp; - } - const delimiter = isIntersectionConstituent ? ";" : ","; - if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) { - const typeParameters: Type[] = []; - return getTypeReferenceId(source, typeParameters) + delimiter + getTypeReferenceId(target, typeParameters); - } - return source.id + delimiter + target.id; - } - - // Invoke the callback for each underlying property symbol of the given symbol and return the first - // value that isn't undefined. - function forEachProperty(prop: Symbol, callback: (p: Symbol) => T): T | undefined { - if (getCheckFlags(prop) & CheckFlags.Synthetic) { - for (const t of (prop).containingType!.types) { - const p = getPropertyOfType(t, prop.escapedName); - const result = p && forEachProperty(p, callback); - if (result) { - return result; - } - } - return undefined; - } - return callback(prop); - } - - // Return the declaring class type of a property or undefined if property not declared in class - function getDeclaringClass(prop: Symbol) { - return prop.parent && prop.parent.flags & SymbolFlags.Class ? getDeclaredTypeOfSymbol(getParentOfSymbol(prop)!) : undefined; - } - - // Return true if some underlying source property is declared in a class that derives - // from the given base class. - function isPropertyInClassDerivedFrom(prop: Symbol, baseClass: Type | undefined) { - return forEachProperty(prop, sp => { - const sourceClass = getDeclaringClass(sp); - return sourceClass ? hasBaseType(sourceClass, baseClass) : false; - }); - } - - // Return true if source property is a valid override of protected parts of target property. - function isValidOverrideOf(sourceProp: Symbol, targetProp: Symbol) { - return !forEachProperty(targetProp, tp => getDeclarationModifierFlagsFromSymbol(tp) & ModifierFlags.Protected ? - !isPropertyInClassDerivedFrom(sourceProp, getDeclaringClass(tp)) : false); - } - - // Return true if the given class derives from each of the declaring classes of the protected - // constituents of the given property. - function isClassDerivedFromDeclaringClasses(checkClass: Type, prop: Symbol) { - return forEachProperty(prop, p => getDeclarationModifierFlagsFromSymbol(p) & ModifierFlags.Protected ? - !hasBaseType(checkClass, getDeclaringClass(p)) : false) ? undefined : checkClass; - } - - // Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons - // for 5 or more occurrences or instantiations of the type have been recorded on the given stack. It is possible, - // though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely - // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5 - // levels, but unequal at some level beyond that. - // In addition, this will also detect when an indexed access has been chained off of 5 or more times (which is essentially - // the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding false positives - // for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`). - function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean { - // We track all object types that have an associated symbol (representing the origin of the type) - if (depth >= 5 && type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) { - const symbol = type.symbol; - if (symbol) { - let count = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (t.flags & TypeFlags.Object && t.symbol === symbol) { - count++; - if (count >= 5) return true; - } - } - } - } - if (depth >= 5 && type.flags & TypeFlags.IndexedAccess) { - const root = getRootObjectTypeFromIndexedAccessChain(type); - let count = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (getRootObjectTypeFromIndexedAccessChain(t) === root) { - count++; - if (count >= 5) return true; - } - } - } - return false; - } - - /** - * Gets the leftmost object type in a chain of indexed accesses, eg, in A[P][Q], returns A - */ - function getRootObjectTypeFromIndexedAccessChain(type: Type) { - let t = type; - while (t.flags & TypeFlags.IndexedAccess) { - t = (t as IndexedAccessType).objectType; - } - return t; - } - - function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean { - return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False; - } - - function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary { - // Two members are considered identical when - // - they are public properties with identical names, optionality, and types, - // - they are private or protected properties originating in the same declaration and having identical types - if (sourceProp === targetProp) { - return Ternary.True; - } - const sourcePropAccessibility = getDeclarationModifierFlagsFromSymbol(sourceProp) & ModifierFlags.NonPublicAccessibilityModifier; - const targetPropAccessibility = getDeclarationModifierFlagsFromSymbol(targetProp) & ModifierFlags.NonPublicAccessibilityModifier; - if (sourcePropAccessibility !== targetPropAccessibility) { - return Ternary.False; - } - if (sourcePropAccessibility) { - if (getTargetSymbol(sourceProp) !== getTargetSymbol(targetProp)) { - return Ternary.False; - } - } - else { - if ((sourceProp.flags & SymbolFlags.Optional) !== (targetProp.flags & SymbolFlags.Optional)) { - return Ternary.False; - } - } - if (isReadonlySymbol(sourceProp) !== isReadonlySymbol(targetProp)) { - return Ternary.False; - } - return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp)); - } - - function isMatchingSignature(source: Signature, target: Signature, partialMatch: boolean) { - const sourceParameterCount = getParameterCount(source); - const targetParameterCount = getParameterCount(target); - const sourceMinArgumentCount = getMinArgumentCount(source); - const targetMinArgumentCount = getMinArgumentCount(target); - const sourceHasRestParameter = hasEffectiveRestParameter(source); - const targetHasRestParameter = hasEffectiveRestParameter(target); - // A source signature matches a target signature if the two signatures have the same number of required, - // optional, and rest parameters. - if (sourceParameterCount === targetParameterCount && - sourceMinArgumentCount === targetMinArgumentCount && - sourceHasRestParameter === targetHasRestParameter) { - return true; - } - // A source signature partially matches a target signature if the target signature has no fewer required - // parameters - if (partialMatch && sourceMinArgumentCount <= targetMinArgumentCount) { - return true; - } - return false; - } - - /** - * See signatureRelatedTo, compareSignaturesIdentical - */ - function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary { - // TODO (drosen): De-duplicate code between related functions. - if (source === target) { - return Ternary.True; - } - if (!(isMatchingSignature(source, target, partialMatch))) { - return Ternary.False; - } - // Check that the two signatures have the same number of type parameters. - if (length(source.typeParameters) !== length(target.typeParameters)) { - return Ternary.False; - } - // Check that type parameter constraints and defaults match. If they do, instantiate the source - // signature with the type parameters of the target signature and continue the comparison. - if (target.typeParameters) { - const mapper = createTypeMapper(source.typeParameters!, target.typeParameters); - for (let i = 0; i < target.typeParameters.length; i++) { - const s = source.typeParameters![i]; - const t = target.typeParameters[i]; - if (!(s === t || compareTypes(instantiateType(getConstraintFromTypeParameter(s), mapper) || unknownType, getConstraintFromTypeParameter(t) || unknownType) && - compareTypes(instantiateType(getDefaultFromTypeParameter(s), mapper) || unknownType, getDefaultFromTypeParameter(t) || unknownType))) { - return Ternary.False; - } - } - source = instantiateSignature(source, mapper, /*eraseTypeParameters*/ true); - } - let result = Ternary.True; - if (!ignoreThisTypes) { - const sourceThisType = getThisTypeOfSignature(source); - if (sourceThisType) { - const targetThisType = getThisTypeOfSignature(target); - if (targetThisType) { - const related = compareTypes(sourceThisType, targetThisType); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - } - const targetLen = getParameterCount(target); - for (let i = 0; i < targetLen; i++) { - const s = getTypeAtPosition(source, i); - const t = getTypeAtPosition(target, i); - const related = compareTypes(t, s); - if (!related) { - return Ternary.False; - } - result &= related; - } - if (!ignoreReturnTypes) { - const sourceTypePredicate = getTypePredicateOfSignature(source); - const targetTypePredicate = getTypePredicateOfSignature(target); - result &= sourceTypePredicate || targetTypePredicate ? - compareTypePredicatesIdentical(sourceTypePredicate, targetTypePredicate, compareTypes) : - compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target)); - } - return result; - } - - function compareTypePredicatesIdentical(source: TypePredicate | undefined, target: TypePredicate | undefined, compareTypes: (s: Type, t: Type) => Ternary): Ternary { - return !(source && target && typePredicateKindsMatch(source, target)) ? Ternary.False : - source.type === target.type ? Ternary.True : - source.type && target.type ? compareTypes(source.type, target.type) : - Ternary.False; - } - - function literalTypesWithSameBaseType(types: Type[]): boolean { - let commonBaseType: Type | undefined; - for (const t of types) { - const baseType = getBaseTypeOfLiteralType(t); - if (!commonBaseType) { - commonBaseType = baseType; - } - if (baseType === t || baseType !== commonBaseType) { - return false; - } - } - return true; - } - - // When the candidate types are all literal types with the same base type, return a union - // of those literal types. Otherwise, return the leftmost type for which no type to the - // right is a supertype. - function getSupertypeOrUnion(types: Type[]): Type { - return literalTypesWithSameBaseType(types) ? - getUnionType(types) : - reduceLeft(types, (s, t) => isTypeSubtypeOf(s, t) ? t : s)!; - } - - function getCommonSupertype(types: Type[]): Type { - if (!strictNullChecks) { - return getSupertypeOrUnion(types); - } - const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); - return primaryTypes.length ? - getNullableType(getSupertypeOrUnion(primaryTypes), getFalsyFlagsOfTypes(types) & TypeFlags.Nullable) : - getUnionType(types, UnionReduction.Subtype); - } - - // Return the leftmost type for which no type to the right is a subtype. - function getCommonSubtype(types: Type[]) { - return reduceLeft(types, (s, t) => isTypeSubtypeOf(t, s) ? t : s)!; - } - - function isArrayType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Reference) && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType); - } - - function isReadonlyArrayType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Reference) && (type).target === globalReadonlyArrayType; - } - - function isMutableArrayOrTuple(type: Type): boolean { - return isArrayType(type) && !isReadonlyArrayType(type) || isTupleType(type) && !type.target.readonly; - } - - function getElementTypeOfArrayType(type: Type): Type | undefined { - return isArrayType(type) ? getTypeArguments(type as TypeReference)[0] : undefined; - } - - function isArrayLikeType(type: Type): boolean { - // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, - // or if it is not the undefined or null type and if it is assignable to ReadonlyArray - return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); - } - - function isEmptyArrayLiteralType(type: Type): boolean { - const elementType = isArrayType(type) ? getTypeArguments(type)[0] : undefined; - return elementType === undefinedWideningType || elementType === implicitNeverType; - } - - function isTupleLikeType(type: Type): boolean { - return isTupleType(type) || !!getPropertyOfType(type, "0" as __String); - } - - function isArrayOrTupleLikeType(type: Type): boolean { - return isArrayLikeType(type) || isTupleLikeType(type); - } - - function getTupleElementType(type: Type, index: number) { - const propType = getTypeOfPropertyOfType(type, "" + index as __String); - if (propType) { - return propType; - } - if (everyType(type, isTupleType)) { - return mapType(type, t => getRestTypeOfTupleType(t) || undefinedType); - } - return undefined; - } - - function isNeitherUnitTypeNorNever(type: Type): boolean { - return !(type.flags & (TypeFlags.Unit | TypeFlags.Never)); - } - - function isUnitType(type: Type): boolean { - return !!(type.flags & TypeFlags.Unit); - } - - function isLiteralType(type: Type): boolean { - return type.flags & TypeFlags.Boolean ? true : - type.flags & TypeFlags.Union ? type.flags & TypeFlags.EnumLiteral ? true : every((type).types, isUnitType) : - isUnitType(type); - } - - function getBaseTypeOfLiteralType(type: Type): Type { - return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(type) : - type.flags & TypeFlags.StringLiteral ? stringType : - type.flags & TypeFlags.NumberLiteral ? numberType : - type.flags & TypeFlags.BigIntLiteral ? bigintType : - type.flags & TypeFlags.BooleanLiteral ? booleanType : - type.flags & TypeFlags.Union ? getUnionType(sameMap((type).types, getBaseTypeOfLiteralType)) : - type; - } - - function getWidenedLiteralType(type: Type): Type { - return type.flags & TypeFlags.EnumLiteral && isFreshLiteralType(type) ? getBaseTypeOfEnumLiteralType(type) : - type.flags & TypeFlags.StringLiteral && isFreshLiteralType(type) ? stringType : - type.flags & TypeFlags.NumberLiteral && isFreshLiteralType(type) ? numberType : - type.flags & TypeFlags.BigIntLiteral && isFreshLiteralType(type) ? bigintType : - type.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(type) ? booleanType : - type.flags & TypeFlags.Union ? getUnionType(sameMap((type).types, getWidenedLiteralType)) : - type; - } - - function getWidenedUniqueESSymbolType(type: Type): Type { - return type.flags & TypeFlags.UniqueESSymbol ? esSymbolType : - type.flags & TypeFlags.Union ? getUnionType(sameMap((type).types, getWidenedUniqueESSymbolType)) : - type; - } - - function getWidenedLiteralLikeTypeForContextualType(type: Type, contextualType: Type | undefined) { - if (!isLiteralOfContextualType(type, contextualType)) { - type = getWidenedUniqueESSymbolType(getWidenedLiteralType(type)); - } - return type; - } - - function getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(type: Type | undefined, contextualSignatureReturnType: Type | undefined, isAsync: boolean) { - if (type && isUnitType(type)) { - const contextualType = !contextualSignatureReturnType ? undefined : - isAsync ? getPromisedTypeOfPromise(contextualSignatureReturnType) : - contextualSignatureReturnType; - type = getWidenedLiteralLikeTypeForContextualType(type, contextualType); - } - return type; - } - - function getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(type: Type | undefined, contextualSignatureReturnType: Type | undefined, kind: IterationTypeKind, isAsyncGenerator: boolean) { - if (type && isUnitType(type)) { - const contextualType = !contextualSignatureReturnType ? undefined : - getIterationTypeOfGeneratorFunctionReturnType(kind, contextualSignatureReturnType, isAsyncGenerator); - type = getWidenedLiteralLikeTypeForContextualType(type, contextualType); - } - return type; - } - - /** - * Check if a Type was written as a tuple type literal. - * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. - */ - function isTupleType(type: Type): type is TupleTypeReference { - return !!(getObjectFlags(type) & ObjectFlags.Reference && (type).target.objectFlags & ObjectFlags.Tuple); - } - - function getRestTypeOfTupleType(type: TupleTypeReference) { - return type.target.hasRestElement ? getTypeArguments(type)[type.target.typeParameters!.length - 1] : undefined; - } - - function getRestArrayTypeOfTupleType(type: TupleTypeReference) { - const restType = getRestTypeOfTupleType(type); - return restType && createArrayType(restType); - } - - function getLengthOfTupleType(type: TupleTypeReference) { - return getTypeReferenceArity(type) - (type.target.hasRestElement ? 1 : 0); - } - - function isZeroBigInt({value}: BigIntLiteralType) { - return value.base10Value === "0"; - } - - function getFalsyFlagsOfTypes(types: Type[]): TypeFlags { - let result: TypeFlags = 0; - for (const t of types) { - result |= getFalsyFlags(t); - } - return result; - } - - // Returns the String, Number, Boolean, StringLiteral, NumberLiteral, BooleanLiteral, Void, Undefined, or Null - // flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns - // no flags for all other types (including non-falsy literal types). - function getFalsyFlags(type: Type): TypeFlags { - return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((type).types) : - type.flags & TypeFlags.StringLiteral ? (type).value === "" ? TypeFlags.StringLiteral : 0 : - type.flags & TypeFlags.NumberLiteral ? (type).value === 0 ? TypeFlags.NumberLiteral : 0 : - type.flags & TypeFlags.BigIntLiteral ? isZeroBigInt(type) ? TypeFlags.BigIntLiteral : 0 : - type.flags & TypeFlags.BooleanLiteral ? (type === falseType || type === regularFalseType) ? TypeFlags.BooleanLiteral : 0 : - type.flags & TypeFlags.PossiblyFalsy; - } - - function removeDefinitelyFalsyTypes(type: Type): Type { - return getFalsyFlags(type) & TypeFlags.DefinitelyFalsy ? - filterType(type, t => !(getFalsyFlags(t) & TypeFlags.DefinitelyFalsy)) : - type; - } - - function extractDefinitelyFalsyTypes(type: Type): Type { - return mapType(type, getDefinitelyFalsyPartOfType); - } - - function getDefinitelyFalsyPartOfType(type: Type): Type { - return type.flags & TypeFlags.String ? emptyStringType : - type.flags & TypeFlags.Number ? zeroType : - type.flags & TypeFlags.BigInt ? zeroBigIntType : - type === regularFalseType || - type === falseType || - type.flags & (TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null) || - type.flags & TypeFlags.StringLiteral && (type).value === "" || - type.flags & TypeFlags.NumberLiteral && (type).value === 0 || - type.flags & TypeFlags.BigIntLiteral && isZeroBigInt(type) ? type : - neverType; - } - - /** - * Add undefined or null or both to a type if they are missing. - * @param type - type to add undefined and/or null to if not present - * @param flags - Either TypeFlags.Undefined or TypeFlags.Null, or both - */ - function getNullableType(type: Type, flags: TypeFlags): Type { - const missing = (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null); - return missing === 0 ? type : - missing === TypeFlags.Undefined ? getUnionType([type, undefinedType]) : - missing === TypeFlags.Null ? getUnionType([type, nullType]) : - getUnionType([type, undefinedType, nullType]); - } - - function getOptionalType(type: Type): Type { - Debug.assert(strictNullChecks); - return type.flags & TypeFlags.Undefined ? type : getUnionType([type, undefinedType]); - } - - function getGlobalNonNullableTypeInstantiation(type: Type) { - if (!deferredGlobalNonNullableTypeAlias) { - deferredGlobalNonNullableTypeAlias = getGlobalSymbol("NonNullable" as __String, SymbolFlags.TypeAlias, /*diagnostic*/ undefined) || unknownSymbol; - } - // Use NonNullable global type alias if available to improve quick info/declaration emit - if (deferredGlobalNonNullableTypeAlias !== unknownSymbol) { - return getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [type]); - } - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); // Type alias unavailable, fall back to non-higher-order behavior - } - - function getNonNullableType(type: Type): Type { - return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type; - } - - function addOptionalTypeMarker(type: Type) { - return strictNullChecks ? getUnionType([type, optionalType]) : type; - } - - function isNotOptionalTypeMarker(type: Type) { - return type !== optionalType; - } - - function removeOptionalTypeMarker(type: Type): Type { - return strictNullChecks ? filterType(type, isNotOptionalTypeMarker) : type; - } - - function propagateOptionalTypeMarker(type: Type, node: OptionalChain, wasOptional: boolean) { - return wasOptional ? isOutermostOptionalChain(node) ? getOptionalType(type) : addOptionalTypeMarker(type) : type; - } - - function getOptionalExpressionType(exprType: Type, expression: Expression) { - return isExpressionOfOptionalChainRoot(expression) ? getNonNullableType(exprType) : - isOptionalChain(expression) ? removeOptionalTypeMarker(exprType) : - exprType; - } - - /** - * Is source potentially coercible to target type under `==`. - * Assumes that `source` is a constituent of a union, hence - * the boolean literal flag on the LHS, but not on the RHS. - * - * This does not fully replicate the semantics of `==`. The - * intention is to catch cases that are clearly not right. - * - * Comparing (string | number) to number should not remove the - * string element. - * - * Comparing (string | number) to 1 will remove the string - * element, though this is not sound. This is a pragmatic - * choice. - * - * @see narrowTypeByEquality - * - * @param source - * @param target - */ - function isCoercibleUnderDoubleEquals(source: Type, target: Type): boolean { - return ((source.flags & (TypeFlags.Number | TypeFlags.String | TypeFlags.BooleanLiteral)) !== 0) - && ((target.flags & (TypeFlags.Number | TypeFlags.String | TypeFlags.Boolean)) !== 0); - } - - /** - * Return true if type was inferred from an object literal, written as an object type literal, or is the shape of a module - * with no call or construct signatures. - */ - function isObjectTypeWithInferableIndex(type: Type): boolean { - return !!(type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 && - !typeHasCallOrConstructSignatures(type)) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source)); - } - - function createSymbolWithType(source: Symbol, type: Type | undefined) { - const symbol = createSymbol(source.flags, source.escapedName, getCheckFlags(source) & CheckFlags.Readonly); - symbol.declarations = source.declarations; - symbol.parent = source.parent; - symbol.type = type; - symbol.target = source; - if (source.valueDeclaration) { - symbol.valueDeclaration = source.valueDeclaration; - } - if (source.nameType) { - symbol.nameType = source.nameType; - } - return symbol; - } - - function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) { - const members = createSymbolTable(); - for (const property of getPropertiesOfObjectType(type)) { - const original = getTypeOfSymbol(property); - const updated = f(original); - members.set(property.escapedName, updated === original ? property : createSymbolWithType(property, updated)); - } - return members; - } - - /** - * If the the provided object literal is subject to the excess properties check, - * create a new that is exempt. Recursively mark object literal members as exempt. - * Leave signatures alone since they are not subject to the check. - */ - function getRegularTypeOfObjectLiteral(type: Type): Type { - if (!(isObjectLiteralType(type) && getObjectFlags(type) & ObjectFlags.FreshLiteral)) { - return type; - } - const regularType = (type).regularType; - if (regularType) { - return regularType; - } - - const resolved = type; - const members = transformTypeOfMembers(type, getRegularTypeOfObjectLiteral); - const regularNew = createAnonymousType(resolved.symbol, - members, - resolved.callSignatures, - resolved.constructSignatures, - resolved.stringIndexInfo, - resolved.numberIndexInfo); - regularNew.flags = resolved.flags; - regularNew.objectFlags |= resolved.objectFlags & ~ObjectFlags.FreshLiteral; - (type).regularType = regularNew; - return regularNew; - } - - function createWideningContext(parent: WideningContext | undefined, propertyName: __String | undefined, siblings: Type[] | undefined): WideningContext { - return { parent, propertyName, siblings, resolvedProperties: undefined }; - } - - function getSiblingsOfContext(context: WideningContext): Type[] { - if (!context.siblings) { - const siblings: Type[] = []; - for (const type of getSiblingsOfContext(context.parent!)) { - if (isObjectLiteralType(type)) { - const prop = getPropertyOfObjectType(type, context.propertyName!); - if (prop) { - forEachType(getTypeOfSymbol(prop), t => { - siblings.push(t); - }); - } - } - } - context.siblings = siblings; - } - return context.siblings; - } - - function getPropertiesOfContext(context: WideningContext): Symbol[] { - if (!context.resolvedProperties) { - const names = createMap() as UnderscoreEscapedMap; - for (const t of getSiblingsOfContext(context)) { - if (isObjectLiteralType(t) && !(getObjectFlags(t) & ObjectFlags.ContainsSpread)) { - for (const prop of getPropertiesOfType(t)) { - names.set(prop.escapedName, prop); - } - } - } - context.resolvedProperties = arrayFrom(names.values()); - } - return context.resolvedProperties; - } - - function getWidenedProperty(prop: Symbol, context: WideningContext | undefined): Symbol { - if (!(prop.flags & SymbolFlags.Property)) { - // Since get accessors already widen their return value there is no need to - // widen accessor based properties here. - return prop; - } - const original = getTypeOfSymbol(prop); - const propContext = context && createWideningContext(context, prop.escapedName, /*siblings*/ undefined); - const widened = getWidenedTypeWithContext(original, propContext); - return widened === original ? prop : createSymbolWithType(prop, widened); - } - - function getUndefinedProperty(prop: Symbol) { - const cached = undefinedProperties.get(prop.escapedName); - if (cached) { - return cached; - } - const result = createSymbolWithType(prop, undefinedType); - result.flags |= SymbolFlags.Optional; - undefinedProperties.set(prop.escapedName, result); - return result; - } - - function getWidenedTypeOfObjectLiteral(type: Type, context: WideningContext | undefined): Type { - const members = createSymbolTable(); - for (const prop of getPropertiesOfObjectType(type)) { - members.set(prop.escapedName, getWidenedProperty(prop, context)); - } - if (context) { - for (const prop of getPropertiesOfContext(context)) { - if (!members.has(prop.escapedName)) { - members.set(prop.escapedName, getUndefinedProperty(prop)); - } - } - } - const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String); - const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number); - const result = createAnonymousType(type.symbol, members, emptyArray, emptyArray, - stringIndexInfo && createIndexInfo(getWidenedType(stringIndexInfo.type), stringIndexInfo.isReadonly), - numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly)); - result.objectFlags |= (getObjectFlags(type) & (ObjectFlags.JSLiteral | ObjectFlags.NonInferrableType)); // Retain js literal flag through widening - return result; - } - - function getWidenedType(type: Type) { - return getWidenedTypeWithContext(type, /*context*/ undefined); - } - - function getWidenedTypeWithContext(type: Type, context: WideningContext | undefined): Type { - if (getObjectFlags(type) & ObjectFlags.RequiresWidening) { - if (context === undefined && type.widened) { - return type.widened; - } - let result: Type | undefined; - if (type.flags & (TypeFlags.Any | TypeFlags.Nullable)) { - result = anyType; - } - else if (isObjectLiteralType(type)) { - result = getWidenedTypeOfObjectLiteral(type, context); - } - else if (type.flags & TypeFlags.Union) { - const unionContext = context || createWideningContext(/*parent*/ undefined, /*propertyName*/ undefined, (type).types); - const widenedTypes = sameMap((type).types, t => t.flags & TypeFlags.Nullable ? t : getWidenedTypeWithContext(t, unionContext)); - // Widening an empty object literal transitions from a highly restrictive type to - // a highly inclusive one. For that reason we perform subtype reduction here if the - // union includes empty object types (e.g. reducing {} | string to just {}). - result = getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType) ? UnionReduction.Subtype : UnionReduction.Literal); - } - else if (type.flags & TypeFlags.Intersection) { - result = getIntersectionType(sameMap((type).types, getWidenedType)); - } - else if (isArrayType(type) || isTupleType(type)) { - result = createTypeReference((type).target, sameMap(getTypeArguments(type), getWidenedType)); - } - if (result && context === undefined) { - type.widened = result; - } - return result || type; - } - return type; - } - - /** - * Reports implicit any errors that occur as a result of widening 'null' and 'undefined' - * to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to - * getWidenedType. But in some cases getWidenedType is called without reporting errors - * (type argument inference is an example). - * - * The return value indicates whether an error was in fact reported. The particular circumstances - * are on a best effort basis. Currently, if the null or undefined that causes widening is inside - * an object literal property (arbitrarily deeply), this function reports an error. If no error is - * reported, reportImplicitAnyError is a suitable fallback to report a general error. - */ - function reportWideningErrorsInType(type: Type): boolean { - let errorReported = false; - if (getObjectFlags(type) & ObjectFlags.ContainsWideningType) { - if (type.flags & TypeFlags.Union) { - if (some((type).types, isEmptyObjectType)) { - errorReported = true; - } - else { - for (const t of (type).types) { - if (reportWideningErrorsInType(t)) { - errorReported = true; - } - } - } - } - if (isArrayType(type) || isTupleType(type)) { - for (const t of getTypeArguments(type)) { - if (reportWideningErrorsInType(t)) { - errorReported = true; - } - } - } - if (isObjectLiteralType(type)) { - for (const p of getPropertiesOfObjectType(type)) { - const t = getTypeOfSymbol(p); - if (getObjectFlags(t) & ObjectFlags.ContainsWideningType) { - if (!reportWideningErrorsInType(t)) { - error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolToString(p), typeToString(getWidenedType(t))); - } - errorReported = true; - } - } - } - } - return errorReported; - } - - function reportImplicitAny(declaration: Declaration, type: Type, wideningKind?: WideningKind) { - const typeAsString = typeToString(getWidenedType(type)); - if (isInJSFile(declaration) && !isCheckJsEnabledForFile(getSourceFileOfNode(declaration), compilerOptions)) { - // Only report implicit any errors/suggestions in TS and ts-check JS files - return; - } - let diagnostic: DiagnosticMessage; - switch (declaration.kind) { - case SyntaxKind.BinaryExpression: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - diagnostic = noImplicitAny ? Diagnostics.Member_0_implicitly_has_an_1_type : Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; - break; - case SyntaxKind.Parameter: - const param = declaration as ParameterDeclaration; - if (isIdentifier(param.name) && - (isCallSignatureDeclaration(param.parent) || isMethodSignature(param.parent) || isFunctionTypeNode(param.parent)) && - param.parent.parameters.indexOf(param) > -1 && - (resolveName(param, param.name.escapedText, SymbolFlags.Type, undefined, param.name.escapedText, /*isUse*/ true) || - param.name.originalKeywordKind && isTypeNodeKind(param.name.originalKeywordKind))) { - const newName = "arg" + param.parent.parameters.indexOf(param); - errorOrSuggestion(noImplicitAny, declaration, Diagnostics.Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1, newName, declarationNameToString(param.name)); - return; - } - diagnostic = (declaration).dotDotDotToken ? - noImplicitAny ? Diagnostics.Rest_parameter_0_implicitly_has_an_any_type : Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage : - noImplicitAny ? Diagnostics.Parameter_0_implicitly_has_an_1_type : Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; - break; - case SyntaxKind.BindingElement: - diagnostic = Diagnostics.Binding_element_0_implicitly_has_an_1_type; - if (!noImplicitAny) { - // Don't issue a suggestion for binding elements since the codefix doesn't yet support them. - return; - } - break; - case SyntaxKind.JSDocFunctionType: - error(declaration, Diagnostics.Function_type_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); - return; - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - if (noImplicitAny && !(declaration as NamedDeclaration).name) { - if (wideningKind === WideningKind.GeneratorYield) { - error(declaration, Diagnostics.Generator_implicitly_has_yield_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type_annotation, typeAsString); - } - else { - error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); - } - return; - } - diagnostic = !noImplicitAny ? Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage : - wideningKind === WideningKind.GeneratorYield ? Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_yield_type : - Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type; - break; - case SyntaxKind.MappedType: - if (noImplicitAny) { - error(declaration, Diagnostics.Mapped_object_type_implicitly_has_an_any_template_type); - } - return; - default: - diagnostic = noImplicitAny ? Diagnostics.Variable_0_implicitly_has_an_1_type : Diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; - } - errorOrSuggestion(noImplicitAny, declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString); - } - - function reportErrorsFromWidening(declaration: Declaration, type: Type, wideningKind?: WideningKind) { - if (produceDiagnostics && noImplicitAny && getObjectFlags(type) & ObjectFlags.ContainsWideningType) { - // Report implicit any error within type if possible, otherwise report error on declaration - if (!reportWideningErrorsInType(type)) { - reportImplicitAny(declaration, type, wideningKind); - } - } - } - - function applyToParameterTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) { - const sourceCount = getParameterCount(source); - const targetCount = getParameterCount(target); - const sourceRestType = getEffectiveRestType(source); - const targetRestType = getEffectiveRestType(target); - const targetNonRestCount = targetRestType ? targetCount - 1 : targetCount; - const paramCount = sourceRestType ? targetNonRestCount : Math.min(sourceCount, targetNonRestCount); - const sourceThisType = getThisTypeOfSignature(source); - if (sourceThisType) { - const targetThisType = getThisTypeOfSignature(target); - if (targetThisType) { - callback(sourceThisType, targetThisType); - } - } - for (let i = 0; i < paramCount; i++) { - callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); - } - if (targetRestType) { - callback(getRestTypeAtPosition(source, paramCount), targetRestType); - } - } - - function applyToReturnTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) { - const sourceTypePredicate = getTypePredicateOfSignature(source); - const targetTypePredicate = getTypePredicateOfSignature(target); - if (sourceTypePredicate && targetTypePredicate && typePredicateKindsMatch(sourceTypePredicate, targetTypePredicate) && sourceTypePredicate.type && targetTypePredicate.type) { - callback(sourceTypePredicate.type, targetTypePredicate.type); - } - else { - callback(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target)); - } - } - - function createInferenceContext(typeParameters: readonly TypeParameter[], signature: Signature | undefined, flags: InferenceFlags, compareTypes?: TypeComparer): InferenceContext { - return createInferenceContextWorker(typeParameters.map(createInferenceInfo), signature, flags, compareTypes || compareTypesAssignable); - } - - function cloneInferenceContext(context: T, extraFlags: InferenceFlags = 0): InferenceContext | T & undefined { - return context && createInferenceContextWorker(map(context.inferences, cloneInferenceInfo), context.signature, context.flags | extraFlags, context.compareTypes); - } - - function createInferenceContextWorker(inferences: InferenceInfo[], signature: Signature | undefined, flags: InferenceFlags, compareTypes: TypeComparer): InferenceContext { - const context: InferenceContext = { - inferences, - signature, - flags, - compareTypes, - mapper: t => mapToInferredType(context, t, /*fix*/ true), - nonFixingMapper: t => mapToInferredType(context, t, /*fix*/ false), - }; - return context; - } - - function mapToInferredType(context: InferenceContext, t: Type, fix: boolean): Type { - const inferences = context.inferences; - for (let i = 0; i < inferences.length; i++) { - const inference = inferences[i]; - if (t === inference.typeParameter) { - if (fix && !inference.isFixed) { - clearCachedInferences(inferences); - inference.isFixed = true; - } - return getInferredType(context, i); - } - } - return t; - } - - function clearCachedInferences(inferences: InferenceInfo[]) { - for (const inference of inferences) { - if (!inference.isFixed) { - inference.inferredType = undefined; - } - } - } - - function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo { - return { - typeParameter, - candidates: undefined, - contraCandidates: undefined, - inferredType: undefined, - priority: undefined, - topLevel: true, - isFixed: false - }; - } - - function cloneInferenceInfo(inference: InferenceInfo): InferenceInfo { - return { - typeParameter: inference.typeParameter, - candidates: inference.candidates && inference.candidates.slice(), - contraCandidates: inference.contraCandidates && inference.contraCandidates.slice(), - inferredType: inference.inferredType, - priority: inference.priority, - topLevel: inference.topLevel, - isFixed: inference.isFixed - }; - } - - function cloneInferredPartOfContext(context: InferenceContext): InferenceContext | undefined { - const inferences = filter(context.inferences, hasInferenceCandidates); - return inferences.length ? - createInferenceContextWorker(map(inferences, cloneInferenceInfo), context.signature, context.flags, context.compareTypes) : - undefined; - } - - function getMapperFromContext(context: T): TypeMapper | T & undefined { - return context && context.mapper; - } - - // Return true if the given type could possibly reference a type parameter for which - // we perform type inference (i.e. a type parameter of a generic function). We cache - // results for union and intersection types for performance reasons. - function couldContainTypeVariables(type: Type): boolean { - const objectFlags = getObjectFlags(type); - return !!(type.flags & TypeFlags.Instantiable || - objectFlags & ObjectFlags.Reference && ((type).node || forEach(getTypeArguments(type), couldContainTypeVariables)) || - objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations || - objectFlags & (ObjectFlags.Mapped | ObjectFlags.ObjectRestType) || - type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && couldUnionOrIntersectionContainTypeVariables(type)); - } - - function couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean { - if (type.couldContainTypeVariables === undefined) { - type.couldContainTypeVariables = some(type.types, couldContainTypeVariables); - } - return type.couldContainTypeVariables; - } - - function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean { - return !!(type === typeParameter || - type.flags & TypeFlags.UnionOrIntersection && some((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)) || - type.flags & TypeFlags.Conditional && ( - isTypeParameterAtTopLevel(getTrueTypeFromConditionalType(type), typeParameter) || - isTypeParameterAtTopLevel(getFalseTypeFromConditionalType(type), typeParameter))); - } - - /** Create an object with properties named in the string literal type. Every property has type `any` */ - function createEmptyObjectTypeFromStringLiteral(type: Type) { - const members = createSymbolTable(); - forEachType(type, t => { - if (!(t.flags & TypeFlags.StringLiteral)) { - return; - } - const name = escapeLeadingUnderscores((t as StringLiteralType).value); - const literalProp = createSymbol(SymbolFlags.Property, name); - literalProp.type = anyType; - if (t.symbol) { - literalProp.declarations = t.symbol.declarations; - literalProp.valueDeclaration = t.symbol.valueDeclaration; - } - members.set(name, literalProp); - }); - const indexInfo = type.flags & TypeFlags.String ? createIndexInfo(emptyObjectType, /*isReadonly*/ false) : undefined; - return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined); - } - - /** - * Infer a suitable input type for a homomorphic mapped type { [P in keyof T]: X }. We construct - * an object type with the same set of properties as the source type, where the type of each - * property is computed by inferring from the source property type to X for the type - * variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). - */ - function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined { - const key = source.id + "," + target.id + "," + constraint.id; - if (reverseMappedCache.has(key)) { - return reverseMappedCache.get(key); - } - reverseMappedCache.set(key, undefined); - const type = createReverseMappedType(source, target, constraint); - reverseMappedCache.set(key, type); - return type; - } - - // We consider a type to be partially inferable if it isn't marked non-inferable or if it is - // an object literal type with at least one property of an inferable type. For example, an object - // literal { a: 123, b: x => true } is marked non-inferable because it contains a context sensitive - // arrow function, but is considered partially inferable because property 'a' has an inferable type. - function isPartiallyInferableType(type: Type): boolean { - return !(getObjectFlags(type) & ObjectFlags.NonInferrableType) || - isObjectLiteralType(type) && some(getPropertiesOfType(type), prop => isPartiallyInferableType(getTypeOfSymbol(prop))); - } - - function createReverseMappedType(source: Type, target: MappedType, constraint: IndexType) { - // We consider a source type reverse mappable if it has a string index signature or if - // it has one or more properties and is of a partially inferable type. - if (!(getIndexInfoOfType(source, IndexKind.String) || getPropertiesOfType(source).length !== 0 && isPartiallyInferableType(source))) { - return undefined; - } - // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been - // applied to the element type(s). - if (isArrayType(source)) { - return createArrayType(inferReverseMappedType(getTypeArguments(source)[0], target, constraint), isReadonlyArrayType(source)); - } - if (isTupleType(source)) { - const elementTypes = map(getTypeArguments(source), t => inferReverseMappedType(t, target, constraint)); - const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ? - getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength; - return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames); - } - // For all other object types we infer a new object type where the reverse mapping has been - // applied to the type of each property. - const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType; - reversed.source = source; - reversed.mappedType = target; - reversed.constraintType = constraint; - return reversed; - } - - function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) { - return inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType); - } - - function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type { - const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)); - const templateType = getTemplateTypeFromMappedType(target); - const inference = createInferenceInfo(typeParameter); - inferTypes([inference], sourceType, templateType); - return getTypeFromInference(inference) || unknownType; - } - - function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): IterableIterator { - const properties = getPropertiesOfType(target); - for (const targetProp of properties) { - // TODO: remove this when we support static private identifier fields and find other solutions to get privateNamesAndStaticFields test to pass - if (isStaticPrivateIdentifierProperty(targetProp)) { - continue; - } - if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial)) { - const sourceProp = getPropertyOfType(source, targetProp.escapedName); - if (!sourceProp) { - yield targetProp; - } - else if (matchDiscriminantProperties) { - const targetType = getTypeOfSymbol(targetProp); - if (targetType.flags & TypeFlags.Unit) { - const sourceType = getTypeOfSymbol(sourceProp); - if (!(sourceType.flags & TypeFlags.Any || getRegularTypeOfLiteralType(sourceType) === getRegularTypeOfLiteralType(targetType))) { - yield targetProp; - } - } - } - } - } - } - - function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): Symbol | undefined { - const result = getUnmatchedProperties(source, target, requireOptionalProperties, matchDiscriminantProperties).next(); - if (!result.done) return result.value; - } - - function tupleTypesDefinitelyUnrelated(source: TupleTypeReference, target: TupleTypeReference) { - return target.target.minLength > source.target.minLength || - !getRestTypeOfTupleType(target) && (!!getRestTypeOfTupleType(source) || getLengthOfTupleType(target) < getLengthOfTupleType(source)); - } - - function typesDefinitelyUnrelated(source: Type, target: Type) { - // Two tuple types with incompatible arities are definitely unrelated. - // Two object types that each have a property that is unmatched in the other are definitely unrelated. - return isTupleType(source) && isTupleType(target) && tupleTypesDefinitelyUnrelated(source, target) || - !!getUnmatchedProperty(source, target, /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ true) && - !!getUnmatchedProperty(target, source, /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ true); - } - - function getTypeFromInference(inference: InferenceInfo) { - return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : - inference.contraCandidates ? getIntersectionType(inference.contraCandidates) : - undefined; - } - - function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { - let symbolStack: Symbol[]; - let visited: Map; - let bivariant = false; - let propagationType: Type; - let inferencePriority = InferencePriority.MaxValue; - let allowComplexConstraintInference = true; - inferFromTypes(originalSource, originalTarget); - - function inferFromTypes(source: Type, target: Type): void { - if (!couldContainTypeVariables(target)) { - return; - } - if (source === wildcardType) { - // We are inferring from an 'any' type. We want to infer this type for every type parameter - // referenced in the target type, so we record it as the propagation type and infer from the - // target to itself. Then, as we find candidates we substitute the propagation type. - const savePropagationType = propagationType; - propagationType = source; - inferFromTypes(target, target); - propagationType = savePropagationType; - return; - } - if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { - // Source and target are types originating in the same generic type alias declaration. - // Simply infer from source type arguments to target type arguments. - inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol)); - return; - } - if (source === target && source.flags & TypeFlags.UnionOrIntersection) { - // When source and target are the same union or intersection type, just relate each constituent - // type to itself. - for (const t of (source).types) { - inferFromTypes(t, t); - } - return; - } - if (target.flags & TypeFlags.Union) { - // First, infer between identically matching source and target constituents and remove the - // matching types. - const [tempSources, tempTargets] = inferFromMatchingTypes(source.flags & TypeFlags.Union ? (source).types : [source], (target).types, isTypeOrBaseIdenticalTo); - // Next, infer between closely matching source and target constituents and remove - // the matching types. Types closely match when they are instantiations of the same - // object type or instantiations of the same type alias. - const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy); - if (targets.length === 0) { - return; - } - target = getUnionType(targets); - if (sources.length === 0) { - // All source constituents have been matched and there is nothing further to infer from. - // However, simply making no inferences is undesirable because it could ultimately mean - // inferring a type parameter constraint. Instead, make a lower priority inference from - // the full source to whatever remains in the target. For example, when inferring from - // string to 'string | T', make a lower priority inference of string for T. - inferWithPriority(source, target, InferencePriority.NakedTypeVariable); - return; - } - source = getUnionType(sources); - } - else if (target.flags & TypeFlags.Intersection && some((target).types, - t => !!getInferenceInfoForType(t) || (isGenericMappedType(t) && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) || neverType)))) { - // We reduce intersection types only when they contain naked type parameters. For example, when - // inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and - // infer { extra: any } for T. But when inferring to 'string[] & Iterable' we want to keep the - // string[] on the source side and infer string for T. - // Likewise, we consider a homomorphic mapped type constrainted to the target type parameter as similar to a "naked type variable" - // in such scenarios. - if (!(source.flags & TypeFlags.Union)) { - // Infer between identically matching source and target constituents and remove the matching types. - const [sources, targets] = inferFromMatchingTypes(source.flags & TypeFlags.Intersection ? (source).types : [source], (target).types, isTypeIdenticalTo); - if (sources.length === 0 || targets.length === 0) { - return; - } - source = getIntersectionType(sources); - target = getIntersectionType(targets); - } - } - else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) { - target = getActualTypeVariable(target); - } - if (target.flags & TypeFlags.TypeVariable) { - // If target is a type parameter, make an inference, unless the source type contains - // the anyFunctionType (the wildcard type that's used to avoid contextually typing functions). - // Because the anyFunctionType is internal, it should not be exposed to the user by adding - // it as an inference candidate. Hopefully, a better candidate will come along that does - // not contain anyFunctionType when we come back to this argument for its second round - // of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard - // when constructing types from type parameters that had no inference candidates). - if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType || source === silentNeverType || - (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) { - return; - } - const inference = getInferenceInfoForType(target); - if (inference) { - if (!inference.isFixed) { - if (inference.priority === undefined || priority < inference.priority) { - inference.candidates = undefined; - inference.contraCandidates = undefined; - inference.topLevel = true; - inference.priority = priority; - } - if (priority === inference.priority) { - const candidate = propagationType || source; - // We make contravariant inferences only if we are in a pure contravariant position, - // i.e. only if we have not descended into a bivariant position. - if (contravariant && !bivariant) { - if (!contains(inference.contraCandidates, candidate)) { - inference.contraCandidates = append(inference.contraCandidates, candidate); - clearCachedInferences(inferences); - } - } - else if (!contains(inference.candidates, candidate)) { - inference.candidates = append(inference.candidates, candidate); - clearCachedInferences(inferences); - } - } - if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && inference.topLevel && !isTypeParameterAtTopLevel(originalTarget, target)) { - inference.topLevel = false; - clearCachedInferences(inferences); - } - } - inferencePriority = Math.min(inferencePriority, priority); - return; - } - else { - // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine - const simplified = getSimplifiedType(target, /*writing*/ false); - if (simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); - } - else if (target.flags & TypeFlags.IndexedAccess) { - const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false); - // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider - // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. - if (indexType.flags & TypeFlags.Instantiable) { - const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false); - if (simplified && simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); - } - } - } - } - } - if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( - (source).target === (target).target || isArrayType(source) && isArrayType(target)) && - !((source).node && (target).node)) { - // If source and target are references to the same generic type, infer from type arguments - inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target)); - } - else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) { - contravariant = !contravariant; - inferFromTypes((source).type, (target).type); - contravariant = !contravariant; - } - else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) { - const empty = createEmptyObjectTypeFromStringLiteral(source); - contravariant = !contravariant; - inferWithPriority(empty, (target as IndexType).type, InferencePriority.LiteralKeyof); - contravariant = !contravariant; - } - else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) { - inferFromTypes((source).objectType, (target).objectType); - inferFromTypes((source).indexType, (target).indexType); - } - else if (source.flags & TypeFlags.Conditional && target.flags & TypeFlags.Conditional) { - inferFromTypes((source).checkType, (target).checkType); - inferFromTypes((source).extendsType, (target).extendsType); - inferFromTypes(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target)); - inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target)); - } - else if (target.flags & TypeFlags.Conditional) { - const savePriority = priority; - priority |= contravariant ? InferencePriority.ContravariantConditional : 0; - const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)]; - inferToMultipleTypes(source, targetTypes, target.flags); - priority = savePriority; - } - else if (target.flags & TypeFlags.UnionOrIntersection) { - inferToMultipleTypes(source, (target).types, target.flags); - } - else if (source.flags & TypeFlags.Union) { - // Source is a union or intersection type, infer from each constituent type - const sourceTypes = (source).types; - for (const sourceType of sourceTypes) { - inferFromTypes(sourceType, target); - } - } - else { - if (!(priority & InferencePriority.NoConstraints && source.flags & (TypeFlags.Intersection | TypeFlags.Instantiable))) { - const apparentSource = getApparentType(source); - // getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type. - // If that occurs and it doesn't simplify to an object or intersection, we'll need to restart `inferFromTypes` - // with the simplified source. - if (apparentSource !== source && allowComplexConstraintInference && !(apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection))) { - // TODO: The `allowComplexConstraintInference` flag is a hack! This forbids inference from complex constraints within constraints! - // This isn't required algorithmically, but rather is used to lower the memory burden caused by performing inference - // that is _too good_ in projects with complicated constraints (eg, fp-ts). In such cases, if we did not limit ourselves - // here, we might produce more valid inferences for types, causing us to do more checks and perform more instantiations - // (in addition to the extra stack depth here) which, in turn, can push the already close process over its limit. - // TL;DR: If we ever become generally more memory efficient (or our resource budget ever increases), we should just - // remove this `allowComplexConstraintInference` flag. - allowComplexConstraintInference = false; - return inferFromTypes(apparentSource, target); - } - source = apparentSource; - } - if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) { - invokeOnce(source, target, inferFromObjectTypes); - } - } - } - - function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) { - const savePriority = priority; - priority |= newPriority; - inferFromTypes(source, target); - priority = savePriority; - } - - function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) { - const key = source.id + "," + target.id; - const status = visited && visited.get(key); - if (status !== undefined) { - inferencePriority = Math.min(inferencePriority, status); - return; - } - (visited || (visited = createMap())).set(key, InferencePriority.Circularity); - const saveInferencePriority = inferencePriority; - inferencePriority = InferencePriority.MaxValue; - action(source, target); - visited.set(key, inferencePriority); - inferencePriority = Math.min(inferencePriority, saveInferencePriority); - } - - function inferFromMatchingTypes(sources: Type[], targets: Type[], matches: (s: Type, t: Type) => boolean): [Type[], Type[]] { - let matchedSources: Type[] | undefined; - let matchedTargets: Type[] | undefined; - for (const t of targets) { - for (const s of sources) { - if (matches(s, t)) { - inferFromTypes(s, t); - matchedSources = appendIfUnique(matchedSources, s); - matchedTargets = appendIfUnique(matchedTargets, t); - } - } - } - return [ - matchedSources ? filter(sources, t => !contains(matchedSources, t)) : sources, - matchedTargets ? filter(targets, t => !contains(matchedTargets, t)) : targets, - ]; - } - - function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) { - const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; - for (let i = 0; i < count; i++) { - if (i < variances.length && (variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Contravariant) { - inferFromContravariantTypes(sourceTypes[i], targetTypes[i]); - } - else { - inferFromTypes(sourceTypes[i], targetTypes[i]); - } - } - } - - function inferFromContravariantTypes(source: Type, target: Type) { - if (strictFunctionTypes || priority & InferencePriority.AlwaysStrict) { - contravariant = !contravariant; - inferFromTypes(source, target); - contravariant = !contravariant; - } - else { - inferFromTypes(source, target); - } - } - - function getInferenceInfoForType(type: Type) { - if (type.flags & TypeFlags.TypeVariable) { - for (const inference of inferences) { - if (type === inference.typeParameter) { - return inference; - } - } - } - return undefined; - } - - function getSingleTypeVariableFromIntersectionTypes(types: Type[]) { - let typeVariable: Type | undefined; - for (const type of types) { - const t = type.flags & TypeFlags.Intersection && find((type).types, t => !!getInferenceInfoForType(t)); - if (!t || typeVariable && t !== typeVariable) { - return undefined; - } - typeVariable = t; - } - return typeVariable; - } - - function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) { - let typeVariableCount = 0; - if (targetFlags & TypeFlags.Union) { - let nakedTypeVariable: Type | undefined; - const sources = source.flags & TypeFlags.Union ? (source).types : [source]; - const matched = new Array(sources.length); - let inferenceCircularity = false; - // First infer to types that are not naked type variables. For each source type we - // track whether inferences were made from that particular type to some target with - // equal priority (i.e. of equal quality) to what we would infer for a naked type - // parameter. - for (const t of targets) { - if (getInferenceInfoForType(t)) { - nakedTypeVariable = t; - typeVariableCount++; - } - else { - for (let i = 0; i < sources.length; i++) { - const saveInferencePriority = inferencePriority; - inferencePriority = InferencePriority.MaxValue; - inferFromTypes(sources[i], t); - if (inferencePriority === priority) matched[i] = true; - inferenceCircularity = inferenceCircularity || inferencePriority === InferencePriority.Circularity; - inferencePriority = Math.min(inferencePriority, saveInferencePriority); - } - } - } - if (typeVariableCount === 0) { - // If every target is an intersection of types containing a single naked type variable, - // make a lower priority inference to that type variable. This handles inferring from - // 'A | B' to 'T & (X | Y)' where we want to infer 'A | B' for T. - const intersectionTypeVariable = getSingleTypeVariableFromIntersectionTypes(targets); - if (intersectionTypeVariable) { - inferWithPriority(source, intersectionTypeVariable, InferencePriority.NakedTypeVariable); - } - return; - } - // If the target has a single naked type variable and no inference circularities were - // encountered above (meaning we explored the types fully), create a union of the source - // types from which no inferences have been made so far and infer from that union to the - // naked type variable. - if (typeVariableCount === 1 && !inferenceCircularity) { - const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s); - if (unmatched.length) { - inferFromTypes(getUnionType(unmatched), nakedTypeVariable!); - return; - } - } - } - else { - // We infer from types that are not naked type variables first so that inferences we - // make from nested naked type variables and given slightly higher priority by virtue - // of being first in the candidates array. - for (const t of targets) { - if (getInferenceInfoForType(t)) { - typeVariableCount++; - } - else { - inferFromTypes(source, t); - } - } - } - // Inferences directly to naked type variables are given lower priority as they are - // less specific. For example, when inferring from Promise to T | Promise, - // we want to infer string for T, not Promise | string. For intersection types - // we only infer to single naked type variables. - if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) { - for (const t of targets) { - if (getInferenceInfoForType(t)) { - inferWithPriority(source, t, InferencePriority.NakedTypeVariable); - } - } - } - } - - function inferToMappedType(source: Type, target: MappedType, constraintType: Type): boolean { - if (constraintType.flags & TypeFlags.Union) { - let result = false; - for (const type of (constraintType as UnionType).types) { - result = inferToMappedType(source, target, type) || result; - } - return result; - } - if (constraintType.flags & TypeFlags.Index) { - // We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X }, - // where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source - // type and then make a secondary inference from that type to T. We make a secondary inference - // such that direct inferences to T get priority over inferences to Partial, for example. - const inference = getInferenceInfoForType((constraintType).type); - if (inference && !inference.isFixed) { - const inferredType = inferTypeForHomomorphicMappedType(source, target, constraintType); - if (inferredType) { - // We assign a lower priority to inferences made from types containing non-inferrable - // types because we may only have a partial result (i.e. we may have failed to make - // reverse inferences for some properties). - inferWithPriority(inferredType, inference.typeParameter, - getObjectFlags(source) & ObjectFlags.NonInferrableType ? - InferencePriority.PartialHomomorphicMappedType : - InferencePriority.HomomorphicMappedType); - } - } - return true; - } - if (constraintType.flags & TypeFlags.TypeParameter) { - // We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type - // parameter. First infer from 'keyof S' to K. - inferWithPriority(getIndexType(source), constraintType, InferencePriority.MappedTypeConstraint); - // If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X }, - // where K extends keyof T, we make the same inferences as for a homomorphic mapped type - // { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a - // Pick. - const extendedConstraint = getConstraintOfType(constraintType); - if (extendedConstraint && inferToMappedType(source, target, extendedConstraint)) { - return true; - } - // If no inferences can be made to K's constraint, infer from a union of the property types - // in the source to the template type X. - const propTypes = map(getPropertiesOfType(source), getTypeOfSymbol); - const stringIndexType = getIndexTypeOfType(source, IndexKind.String); - const numberIndexInfo = getNonEnumNumberIndexInfo(source); - const numberIndexType = numberIndexInfo && numberIndexInfo.type; - inferFromTypes(getUnionType(append(append(propTypes, stringIndexType), numberIndexType)), getTemplateTypeFromMappedType(target)); - return true; - } - return false; - } - - function inferFromObjectTypes(source: Type, target: Type) { - // If we are already processing another target type with the same associated symbol (such as - // an instantiation of the same generic type), we do not explore this target as it would yield - // no further inferences. We exclude the static side of classes from this check since it shares - // its symbol with the instance side which would lead to false positives. - const isNonConstructorObject = target.flags & TypeFlags.Object && - !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class); - const symbol = isNonConstructorObject ? target.symbol : undefined; - if (symbol) { - if (contains(symbolStack, symbol)) { - inferencePriority = InferencePriority.Circularity; - return; - } - (symbolStack || (symbolStack = [])).push(symbol); - inferFromObjectTypesWorker(source, target); - symbolStack.pop(); - } - else { - inferFromObjectTypesWorker(source, target); - } - } - - function inferFromObjectTypesWorker(source: Type, target: Type) { - if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( - (source).target === (target).target || isArrayType(source) && isArrayType(target))) { - // If source and target are references to the same generic type, infer from type arguments - inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target)); - return; - } - if (isGenericMappedType(source) && isGenericMappedType(target)) { - // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer - // from S to T and from X to Y. - inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target)); - inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); - } - if (getObjectFlags(target) & ObjectFlags.Mapped) { - const constraintType = getConstraintTypeFromMappedType(target); - if (inferToMappedType(source, target, constraintType)) { - return; - } - } - // Infer from the members of source and target only if the two types are possibly related - if (!typesDefinitelyUnrelated(source, target)) { - inferFromProperties(source, target); - inferFromSignatures(source, target, SignatureKind.Call); - inferFromSignatures(source, target, SignatureKind.Construct); - inferFromIndexTypes(source, target); - } - } - - function inferFromProperties(source: Type, target: Type) { - if (isArrayType(source) || isTupleType(source)) { - if (isTupleType(target)) { - const sourceLength = isTupleType(source) ? getLengthOfTupleType(source) : 0; - const targetLength = getLengthOfTupleType(target); - const sourceRestType = isTupleType(source) ? getRestTypeOfTupleType(source) : getElementTypeOfArrayType(source); - const targetRestType = getRestTypeOfTupleType(target); - const fixedLength = targetLength < sourceLength || sourceRestType ? targetLength : sourceLength; - for (let i = 0; i < fixedLength; i++) { - inferFromTypes(i < sourceLength ? getTypeArguments(source)[i] : sourceRestType!, getTypeArguments(target)[i]); - } - if (targetRestType) { - const types = fixedLength < sourceLength ? getTypeArguments(source).slice(fixedLength, sourceLength) : []; - if (sourceRestType) { - types.push(sourceRestType); - } - if (types.length) { - inferFromTypes(getUnionType(types), targetRestType); - } - } - return; - } - if (isArrayType(target)) { - inferFromIndexTypes(source, target); - return; - } - } - const properties = getPropertiesOfObjectType(target); - for (const targetProp of properties) { - const sourceProp = getPropertyOfType(source, targetProp.escapedName); - if (sourceProp) { - inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp)); - } - } - } - - function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) { - const sourceSignatures = getSignaturesOfType(source, kind); - const targetSignatures = getSignaturesOfType(target, kind); - const sourceLen = sourceSignatures.length; - const targetLen = targetSignatures.length; - const len = sourceLen < targetLen ? sourceLen : targetLen; - const skipParameters = !!(getObjectFlags(source) & ObjectFlags.NonInferrableType); - for (let i = 0; i < len; i++) { - inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]), skipParameters); - } - } - - function inferFromSignature(source: Signature, target: Signature, skipParameters: boolean) { - if (!skipParameters) { - const saveBivariant = bivariant; - const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; - // Once we descend into a bivariant signature we remain bivariant for all nested inferences - bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor; - applyToParameterTypes(source, target, inferFromContravariantTypes); - bivariant = saveBivariant; - } - applyToReturnTypes(source, target, inferFromTypes); - } - - function inferFromIndexTypes(source: Type, target: Type) { - const targetStringIndexType = getIndexTypeOfType(target, IndexKind.String); - if (targetStringIndexType) { - const sourceIndexType = getIndexTypeOfType(source, IndexKind.String) || - getImplicitIndexTypeOfType(source, IndexKind.String); - if (sourceIndexType) { - inferFromTypes(sourceIndexType, targetStringIndexType); - } - } - const targetNumberIndexType = getIndexTypeOfType(target, IndexKind.Number); - if (targetNumberIndexType) { - const sourceIndexType = getIndexTypeOfType(source, IndexKind.Number) || - getIndexTypeOfType(source, IndexKind.String) || - getImplicitIndexTypeOfType(source, IndexKind.Number); - if (sourceIndexType) { - inferFromTypes(sourceIndexType, targetNumberIndexType); - } - } - } - } - - function isTypeOrBaseIdenticalTo(s: Type, t: Type) { - return isTypeIdenticalTo(s, t) || !!(s.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t); - } - - function isTypeCloselyMatchedBy(s: Type, t: Type) { - return !!(s.flags & TypeFlags.Object && t.flags & TypeFlags.Object && s.symbol && s.symbol === t.symbol || - s.aliasSymbol && s.aliasTypeArguments && s.aliasSymbol === t.aliasSymbol); - } - - function hasPrimitiveConstraint(type: TypeParameter): boolean { - const constraint = getConstraintOfTypeParameter(type); - return !!constraint && maybeTypeOfKind(constraint.flags & TypeFlags.Conditional ? getDefaultConstraintOfConditionalType(constraint as ConditionalType) : constraint, TypeFlags.Primitive | TypeFlags.Index); - } - - function isObjectLiteralType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.ObjectLiteral); - } - - function isObjectOrArrayLiteralType(type: Type) { - return !!(getObjectFlags(type) & (ObjectFlags.ObjectLiteral | ObjectFlags.ArrayLiteral)); - } - - function unionObjectAndArrayLiteralCandidates(candidates: Type[]): Type[] { - if (candidates.length > 1) { - const objectLiterals = filter(candidates, isObjectOrArrayLiteralType); - if (objectLiterals.length) { - const literalsType = getUnionType(objectLiterals, UnionReduction.Subtype); - return concatenate(filter(candidates, t => !isObjectOrArrayLiteralType(t)), [literalsType]); - } - } - return candidates; - } - - function getContravariantInference(inference: InferenceInfo) { - return inference.priority! & InferencePriority.PriorityImpliesCombination ? getIntersectionType(inference.contraCandidates!) : getCommonSubtype(inference.contraCandidates!); - } - - function getCovariantInference(inference: InferenceInfo, signature: Signature) { - // Extract all object and array literal types and replace them with a single widened and normalized type. - const candidates = unionObjectAndArrayLiteralCandidates(inference.candidates!); - // We widen inferred literal types if - // all inferences were made to top-level occurrences of the type parameter, and - // the type parameter has no constraint or its constraint includes no primitive or literal types, and - // the type parameter was fixed during inference or does not occur at top-level in the return type. - const primitiveConstraint = hasPrimitiveConstraint(inference.typeParameter); - const widenLiteralTypes = !primitiveConstraint && inference.topLevel && - (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); - const baseCandidates = primitiveConstraint ? sameMap(candidates, getRegularTypeOfLiteralType) : - widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : - candidates; - // If all inferences were made from a position that implies a combined result, infer a union type. - // Otherwise, infer a common supertype. - const unwidenedType = inference.priority! & InferencePriority.PriorityImpliesCombination ? - getUnionType(baseCandidates, UnionReduction.Subtype) : - getCommonSupertype(baseCandidates); - return getWidenedType(unwidenedType); - } - - function getInferredType(context: InferenceContext, index: number): Type { - const inference = context.inferences[index]; - if (!inference.inferredType) { - let inferredType: Type | undefined; - const signature = context.signature; - if (signature) { - const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined; - if (inference.contraCandidates) { - const inferredContravariantType = getContravariantInference(inference); - // If we have both co- and contra-variant inferences, we prefer the contra-variant inference - // unless the co-variant inference is a subtype and not 'never'. - inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) && - isTypeSubtypeOf(inferredCovariantType, inferredContravariantType) ? - inferredCovariantType : inferredContravariantType; - } - else if (inferredCovariantType) { - inferredType = inferredCovariantType; - } - else if (context.flags & InferenceFlags.NoDefault) { - // We use silentNeverType as the wildcard that signals no inferences. - inferredType = silentNeverType; - } - else { - // Infer either the default or the empty object type when no inferences were - // made. It is important to remember that in this case, inference still - // succeeds, meaning there is no error for not having inference candidates. An - // inference error only occurs when there are *conflicting* candidates, i.e. - // candidates with no common supertype. - const defaultType = getDefaultFromTypeParameter(inference.typeParameter); - if (defaultType) { - // Instantiate the default type. Any forward reference to a type - // parameter should be instantiated to the empty object type. - inferredType = instantiateType(defaultType, combineTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper)); - } - } - } - else { - inferredType = getTypeFromInference(inference); - } - - inference.inferredType = inferredType || getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault)); - - const constraint = getConstraintOfTypeParameter(inference.typeParameter); - if (constraint) { - const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper); - if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { - inference.inferredType = inferredType = instantiatedConstraint; - } - } - } - - return inference.inferredType; - } - - function getDefaultTypeArgumentType(isInJavaScriptFile: boolean): Type { - return isInJavaScriptFile ? anyType : unknownType; - } - - function getInferredTypes(context: InferenceContext): Type[] { - const result: Type[] = []; - for (let i = 0; i < context.inferences.length; i++) { - result.push(getInferredType(context, i)); - } - return result; - } - - // EXPRESSION TYPE CHECKING - - function getCannotFindNameDiagnosticForName(node: Identifier): DiagnosticMessage { - switch (node.escapedText) { - case "document": - case "console": - return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_include_dom; - case "$": - return compilerOptions.types - ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig - : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery; - case "describe": - case "suite": - case "it": - case "test": - return compilerOptions.types - ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig - : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha; - case "process": - case "require": - case "Buffer": - case "module": - return compilerOptions.types - ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig - : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode; - case "Map": - case "Set": - case "Promise": - case "Symbol": - case "WeakMap": - case "WeakSet": - case "Iterator": - case "AsyncIterator": - return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later; - default: - if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { - return Diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer; - } - else { - return Diagnostics.Cannot_find_name_0; - } - } - } - - function getResolvedSymbol(node: Identifier): Symbol { - const links = getNodeLinks(node); - if (!links.resolvedSymbol) { - links.resolvedSymbol = !nodeIsMissing(node) && - resolveName( - node, - node.escapedText, - SymbolFlags.Value | SymbolFlags.ExportValue, - getCannotFindNameDiagnosticForName(node), - node, - !isWriteOnlyAccess(node), - /*excludeGlobals*/ false, - Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol; - } - return links.resolvedSymbol; - } - - function isInTypeQuery(node: Node): boolean { - // TypeScript 1.0 spec (April 2014): 3.6.3 - // A type query consists of the keyword typeof followed by an expression. - // The expression is restricted to a single identifier or a sequence of identifiers separated by periods - return !!findAncestor( - node, - n => n.kind === SyntaxKind.TypeQuery ? true : n.kind === SyntaxKind.Identifier || n.kind === SyntaxKind.QualifiedName ? false : "quit"); - } - - // Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers - // separated by dots). The key consists of the id of the symbol referenced by the - // leftmost identifier followed by zero or more property names separated by dots. - // The result is undefined if the reference isn't a dotted name. We prefix nodes - // occurring in an apparent type position with '@' because the control flow type - // of such nodes may be based on the apparent type instead of the declared type. - function getFlowCacheKey(node: Node, declaredType: Type, initialType: Type, flowContainer: Node | undefined): string | undefined { - switch (node.kind) { - case SyntaxKind.Identifier: - const symbol = getResolvedSymbol(node); - return symbol !== unknownSymbol ? `${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}|${isConstraintPosition(node) ? "@" : ""}${getSymbolId(symbol)}` : undefined; - case SyntaxKind.ThisKeyword: - return "0"; - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - return getFlowCacheKey((node).expression, declaredType, initialType, flowContainer); - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - const propName = getAccessedPropertyName(node); - if (propName !== undefined) { - const key = getFlowCacheKey((node).expression, declaredType, initialType, flowContainer); - return key && key + "." + propName; - } - } - return undefined; - } - - function isMatchingReference(source: Node, target: Node): boolean { - switch (target.kind) { - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.NonNullExpression: - return isMatchingReference(source, (target as NonNullExpression | ParenthesizedExpression).expression); - } - switch (source.kind) { - case SyntaxKind.Identifier: - return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || - (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && - getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source)) === getSymbolOfNode(target); - case SyntaxKind.ThisKeyword: - return target.kind === SyntaxKind.ThisKeyword; - case SyntaxKind.SuperKeyword: - return target.kind === SyntaxKind.SuperKeyword; - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - return isMatchingReference((source as NonNullExpression | ParenthesizedExpression).expression, target); - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return isAccessExpression(target) && - getAccessedPropertyName(source) === getAccessedPropertyName(target) && - isMatchingReference((source).expression, target.expression); - } - return false; - } - - function getAccessedPropertyName(access: AccessExpression): __String | undefined { - return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText : - isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) : - undefined; - } - - function containsMatchingReference(source: Node, target: Node) { - while (isAccessExpression(source)) { - source = source.expression; - if (isMatchingReference(source, target)) { - return true; - } - } - return false; - } - - function optionalChainContainsReference(source: Node, target: Node) { - while (isOptionalChain(source)) { - source = source.expression; - if (isMatchingReference(source, target)) { - return true; - } - } - return false; - } - - // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared - // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property - // a possible discriminant if its type differs in the constituents of containing union type, and if every - // choice is a unit type or a union of unit types. - function containsMatchingReferenceDiscriminant(source: Node, target: Node) { - let name; - return isAccessExpression(target) && - containsMatchingReference(source, target.expression) && - (name = getAccessedPropertyName(target)) !== undefined && - isDiscriminantProperty(getDeclaredTypeOfReference(target.expression), name); - } - - function getDeclaredTypeOfReference(expr: Node): Type | undefined { - if (expr.kind === SyntaxKind.Identifier) { - return getTypeOfSymbol(getResolvedSymbol(expr)); - } - if (isAccessExpression(expr)) { - const type = getDeclaredTypeOfReference(expr.expression); - if (type) { - const propName = getAccessedPropertyName(expr); - return propName !== undefined ? getTypeOfPropertyOfType(type, propName) : undefined; - } - } - return undefined; - } - - function isDiscriminantProperty(type: Type | undefined, name: __String) { - if (type && type.flags & TypeFlags.Union) { - const prop = getUnionOrIntersectionProperty(type, name); - if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { - if ((prop).isDiscriminantProperty === undefined) { - (prop).isDiscriminantProperty = - ((prop).checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant && - !maybeTypeOfKind(getTypeOfSymbol(prop), TypeFlags.Instantiable); - } - return !!(prop).isDiscriminantProperty; - } - } - return false; - } - - function isSyntheticThisPropertyAccess(expr: Node) { - return isAccessExpression(expr) && expr.expression.kind === SyntaxKind.ThisKeyword && !!(expr.expression.flags & NodeFlags.Synthesized); - } - - function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { - let result: Symbol[] | undefined; - for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.escapedName)) { - if (result) { - result.push(sourceProperty); - continue; - } - result = [sourceProperty]; - } - } - return result; - } - - function isOrContainsMatchingReference(source: Node, target: Node) { - return isMatchingReference(source, target) || containsMatchingReference(source, target); - } - - function hasMatchingArgument(callExpression: CallExpression, reference: Node) { - if (callExpression.arguments) { - for (const argument of callExpression.arguments) { - if (isOrContainsMatchingReference(reference, argument)) { - return true; - } - } - } - if (callExpression.expression.kind === SyntaxKind.PropertyAccessExpression && - isOrContainsMatchingReference(reference, (callExpression.expression).expression)) { - return true; - } - return false; - } - - function getFlowNodeId(flow: FlowNode): number { - if (!flow.id || flow.id < 0) { - flow.id = nextFlowId; - nextFlowId++; - } - return flow.id; - } - - function typeMaybeAssignableTo(source: Type, target: Type) { - if (!(source.flags & TypeFlags.Union)) { - return isTypeAssignableTo(source, target); - } - for (const t of (source).types) { - if (isTypeAssignableTo(t, target)) { - return true; - } - } - return false; - } - - // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. - // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, - // we remove type string. - function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) { - if (declaredType !== assignedType) { - if (assignedType.flags & TypeFlags.Never) { - return assignedType; - } - let reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); - if (assignedType.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(assignedType)) { - reducedType = mapType(reducedType, getFreshTypeOfLiteralType); // Ensure that if the assignment is a fresh type, that we narrow to fresh types - } - // Our crude heuristic produces an invalid result in some cases: see GH#26130. - // For now, when that happens, we give up and don't narrow at all. (This also - // means we'll never narrow for erroneous assignments where the assigned type - // is not assignable to the declared type.) - if (isTypeAssignableTo(assignedType, reducedType)) { - return reducedType; - } - } - return declaredType; - } - - function getTypeFactsOfTypes(types: Type[]): TypeFacts { - let result: TypeFacts = TypeFacts.None; - for (const t of types) { - result |= getTypeFacts(t); - } - return result; - } - - function isFunctionObjectType(type: ObjectType): boolean { - // We do a quick check for a "bind" property before performing the more expensive subtype - // check. This gives us a quicker out in the common case where an object type is not a function. - const resolved = resolveStructuredTypeMembers(type); - return !!(resolved.callSignatures.length || resolved.constructSignatures.length || - resolved.members.get("bind" as __String) && isTypeSubtypeOf(type, globalFunctionType)); - } - - function getTypeFacts(type: Type): TypeFacts { - const flags = type.flags; - if (flags & TypeFlags.String) { - return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts; - } - if (flags & TypeFlags.StringLiteral) { - const isEmpty = (type).value === ""; - return strictNullChecks ? - isEmpty ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts : - isEmpty ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts; - } - if (flags & (TypeFlags.Number | TypeFlags.Enum)) { - return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts; - } - if (flags & TypeFlags.NumberLiteral) { - const isZero = (type).value === 0; - return strictNullChecks ? - isZero ? TypeFacts.ZeroNumberStrictFacts : TypeFacts.NonZeroNumberStrictFacts : - isZero ? TypeFacts.ZeroNumberFacts : TypeFacts.NonZeroNumberFacts; - } - if (flags & TypeFlags.BigInt) { - return strictNullChecks ? TypeFacts.BigIntStrictFacts : TypeFacts.BigIntFacts; - } - if (flags & TypeFlags.BigIntLiteral) { - const isZero = isZeroBigInt(type); - return strictNullChecks ? - isZero ? TypeFacts.ZeroBigIntStrictFacts : TypeFacts.NonZeroBigIntStrictFacts : - isZero ? TypeFacts.ZeroBigIntFacts : TypeFacts.NonZeroBigIntFacts; - } - if (flags & TypeFlags.Boolean) { - return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts; - } - if (flags & TypeFlags.BooleanLike) { - return strictNullChecks ? - (type === falseType || type === regularFalseType) ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts : - (type === falseType || type === regularFalseType) ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; - } - if (flags & TypeFlags.Object) { - return getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type) ? - strictNullChecks ? TypeFacts.EmptyObjectStrictFacts : TypeFacts.EmptyObjectFacts : - isFunctionObjectType(type) ? - strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : - strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; - } - if (flags & (TypeFlags.Void | TypeFlags.Undefined)) { - return TypeFacts.UndefinedFacts; - } - if (flags & TypeFlags.Null) { - return TypeFacts.NullFacts; - } - if (flags & TypeFlags.ESSymbolLike) { - return strictNullChecks ? TypeFacts.SymbolStrictFacts : TypeFacts.SymbolFacts; - } - if (flags & TypeFlags.NonPrimitive) { - return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; - } - if (flags & TypeFlags.Instantiable) { - return getTypeFacts(getBaseConstraintOfType(type) || unknownType); - } - if (flags & TypeFlags.UnionOrIntersection) { - return getTypeFactsOfTypes((type).types); - } - return TypeFacts.All; - } - - function getTypeWithFacts(type: Type, include: TypeFacts) { - return filterType(type, t => (getTypeFacts(t) & include) !== 0); - } - - function getTypeWithDefault(type: Type, defaultExpression: Expression) { - if (defaultExpression) { - const defaultType = getTypeOfExpression(defaultExpression); - return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]); - } - return type; - } - - function getTypeOfDestructuredProperty(type: Type, name: PropertyName) { - const nameType = getLiteralTypeFromPropertyName(name); - if (!isTypeUsableAsPropertyName(nameType)) return errorType; - const text = getPropertyNameFromType(nameType); - return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) || - isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) || - getIndexTypeOfType(type, IndexKind.String) || - errorType; - } - - function getTypeOfDestructuredArrayElement(type: Type, index: number) { - return everyType(type, isTupleLikeType) && getTupleElementType(type, index) || - checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined) || - errorType; - } - - function getTypeOfDestructuredSpreadExpression(type: Type) { - return createArrayType(checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined) || errorType); - } - - function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { - const isDestructuringDefaultAssignment = - node.parent.kind === SyntaxKind.ArrayLiteralExpression && isDestructuringAssignmentTarget(node.parent) || - node.parent.kind === SyntaxKind.PropertyAssignment && isDestructuringAssignmentTarget(node.parent.parent); - return isDestructuringDefaultAssignment ? - getTypeWithDefault(getAssignedType(node), node.right) : - getTypeOfExpression(node.right); - } - - function isDestructuringAssignmentTarget(parent: Node) { - return parent.parent.kind === SyntaxKind.BinaryExpression && (parent.parent as BinaryExpression).left === parent || - parent.parent.kind === SyntaxKind.ForOfStatement && (parent.parent as ForOfStatement).initializer === parent; - } - - function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { - return getTypeOfDestructuredArrayElement(getAssignedType(node), node.elements.indexOf(element)); - } - - function getAssignedTypeOfSpreadExpression(node: SpreadElement): Type { - return getTypeOfDestructuredSpreadExpression(getAssignedType(node.parent)); - } - - function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { - return getTypeOfDestructuredProperty(getAssignedType(node.parent), node.name); - } - - function getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type { - return getTypeWithDefault(getAssignedTypeOfPropertyAssignment(node), node.objectAssignmentInitializer!); - } - - function getAssignedType(node: Expression): Type { - const { parent } = node; - switch (parent.kind) { - case SyntaxKind.ForInStatement: - return stringType; - case SyntaxKind.ForOfStatement: - return checkRightHandSideOfForOf((parent).expression, (parent).awaitModifier) || errorType; - case SyntaxKind.BinaryExpression: - return getAssignedTypeOfBinaryExpression(parent); - case SyntaxKind.DeleteExpression: - return undefinedType; - case SyntaxKind.ArrayLiteralExpression: - return getAssignedTypeOfArrayLiteralElement(parent, node); - case SyntaxKind.SpreadElement: - return getAssignedTypeOfSpreadExpression(parent); - case SyntaxKind.PropertyAssignment: - return getAssignedTypeOfPropertyAssignment(parent); - case SyntaxKind.ShorthandPropertyAssignment: - return getAssignedTypeOfShorthandPropertyAssignment(parent); - } - return errorType; - } - - function getInitialTypeOfBindingElement(node: BindingElement): Type { - const pattern = node.parent; - const parentType = getInitialType(pattern.parent); - const type = pattern.kind === SyntaxKind.ObjectBindingPattern ? - getTypeOfDestructuredProperty(parentType, node.propertyName || node.name) : - !node.dotDotDotToken ? - getTypeOfDestructuredArrayElement(parentType, pattern.elements.indexOf(node)) : - getTypeOfDestructuredSpreadExpression(parentType); - return getTypeWithDefault(type, node.initializer!); - } - - function getTypeOfInitializer(node: Expression) { - // Return the cached type if one is available. If the type of the variable was inferred - // from its initializer, we'll already have cached the type. Otherwise we compute it now - // without caching such that transient types are reflected. - const links = getNodeLinks(node); - return links.resolvedType || getTypeOfExpression(node); - } - - function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) { - if (node.initializer) { - return getTypeOfInitializer(node.initializer); - } - if (node.parent.parent.kind === SyntaxKind.ForInStatement) { - return stringType; - } - if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { - return checkRightHandSideOfForOf(node.parent.parent.expression, node.parent.parent.awaitModifier) || errorType; - } - return errorType; - } - - function getInitialType(node: VariableDeclaration | BindingElement) { - return node.kind === SyntaxKind.VariableDeclaration ? - getInitialTypeOfVariableDeclaration(node) : - getInitialTypeOfBindingElement(node); - } - - function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) { - return node.kind === SyntaxKind.VariableDeclaration && (node).initializer && - isEmptyArrayLiteral((node).initializer!) || - node.kind !== SyntaxKind.BindingElement && node.parent.kind === SyntaxKind.BinaryExpression && - isEmptyArrayLiteral((node.parent).right); - } - - function getReferenceCandidate(node: Expression): Expression { - switch (node.kind) { - case SyntaxKind.ParenthesizedExpression: - return getReferenceCandidate((node).expression); - case SyntaxKind.BinaryExpression: - switch ((node).operatorToken.kind) { - case SyntaxKind.EqualsToken: - return getReferenceCandidate((node).left); - case SyntaxKind.CommaToken: - return getReferenceCandidate((node).right); - } - } - return node; - } - - function getReferenceRoot(node: Node): Node { - const { parent } = node; - return parent.kind === SyntaxKind.ParenthesizedExpression || - parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken && (parent).left === node || - parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.CommaToken && (parent).right === node ? - getReferenceRoot(parent) : node; - } - - function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { - if (clause.kind === SyntaxKind.CaseClause) { - return getRegularTypeOfLiteralType(getTypeOfExpression(clause.expression)); - } - return neverType; - } - - function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] { - const links = getNodeLinks(switchStatement); - if (!links.switchTypes) { - links.switchTypes = []; - for (const clause of switchStatement.caseBlock.clauses) { - links.switchTypes.push(getTypeOfSwitchClause(clause)); - } - } - return links.switchTypes; - } - - // Get the types from all cases in a switch on `typeof`. An - // `undefined` element denotes an explicit `default` clause. - function getSwitchClauseTypeOfWitnesses(switchStatement: SwitchStatement): (string | undefined)[] { - const witnesses: (string | undefined)[] = []; - for (const clause of switchStatement.caseBlock.clauses) { - if (clause.kind === SyntaxKind.CaseClause) { - if (isStringLiteralLike(clause.expression)) { - witnesses.push(clause.expression.text); - continue; - } - return emptyArray; - } - witnesses.push(/*explicitDefaultStatement*/ undefined); - } - return witnesses; - } - - function eachTypeContainedIn(source: Type, types: Type[]) { - return source.flags & TypeFlags.Union ? !forEach((source).types, t => !contains(types, t)) : contains(types, source); - } - - function isTypeSubsetOf(source: Type, target: Type) { - return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target); - } - - function isTypeSubsetOfUnion(source: Type, target: UnionType) { - if (source.flags & TypeFlags.Union) { - for (const t of (source).types) { - if (!containsType(target.types, t)) { - return false; - } - } - return true; - } - if (source.flags & TypeFlags.EnumLiteral && getBaseTypeOfEnumLiteralType(source) === target) { - return true; - } - return containsType(target.types, source); - } - - function forEachType(type: Type, f: (t: Type) => T | undefined): T | undefined { - return type.flags & TypeFlags.Union ? forEach((type).types, f) : f(type); - } - - function everyType(type: Type, f: (t: Type) => boolean): boolean { - return type.flags & TypeFlags.Union ? every((type).types, f) : f(type); - } - - function filterType(type: Type, f: (t: Type) => boolean): Type { - if (type.flags & TypeFlags.Union) { - const types = (type).types; - const filtered = filter(types, f); - return filtered === types ? type : getUnionTypeFromSortedList(filtered, (type).objectFlags); - } - return f(type) ? type : neverType; - } - - function countTypes(type: Type) { - return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1; - } - - // Apply a mapping function to a type and return the resulting type. If the source type - // is a union type, the mapping function is applied to each constituent type and a union - // of the resulting types is returned. - function mapType(type: Type, mapper: (t: Type) => Type, noReductions?: boolean): Type; - function mapType(type: Type, mapper: (t: Type) => Type | undefined, noReductions?: boolean): Type | undefined; - function mapType(type: Type, mapper: (t: Type) => Type | undefined, noReductions?: boolean): Type | undefined { - if (type.flags & TypeFlags.Never) { - return type; - } - if (!(type.flags & TypeFlags.Union)) { - return mapper(type); - } - let mappedTypes: Type[] | undefined; - for (const t of (type).types) { - const mapped = mapper(t); - if (mapped) { - if (!mappedTypes) { - mappedTypes = [mapped]; - } - else { - mappedTypes.push(mapped); - } - } - } - return mappedTypes && getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal); - } - - function extractTypesOfKind(type: Type, kind: TypeFlags) { - return filterType(type, t => (t.flags & kind) !== 0); - } - - // Return a new type in which occurrences of the string and number primitive types in - // typeWithPrimitives have been replaced with occurrences of string literals and numeric - // literals in typeWithLiterals, respectively. - function replacePrimitivesWithLiterals(typeWithPrimitives: Type, typeWithLiterals: Type) { - if (isTypeSubsetOf(stringType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.StringLiteral) || - isTypeSubsetOf(numberType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.NumberLiteral) || - isTypeSubsetOf(bigintType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.BigIntLiteral)) { - return mapType(typeWithPrimitives, t => - t.flags & TypeFlags.String ? extractTypesOfKind(typeWithLiterals, TypeFlags.String | TypeFlags.StringLiteral) : - t.flags & TypeFlags.Number ? extractTypesOfKind(typeWithLiterals, TypeFlags.Number | TypeFlags.NumberLiteral) : - t.flags & TypeFlags.BigInt ? extractTypesOfKind(typeWithLiterals, TypeFlags.BigInt | TypeFlags.BigIntLiteral) : t); - } - return typeWithPrimitives; - } - - function isIncomplete(flowType: FlowType) { - return flowType.flags === 0; - } - - function getTypeFromFlowType(flowType: FlowType) { - return flowType.flags === 0 ? (flowType).type : flowType; - } - - function createFlowType(type: Type, incomplete: boolean): FlowType { - return incomplete ? { flags: 0, type } : type; - } - - // An evolving array type tracks the element types that have so far been seen in an - // 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving - // array types are ultimately converted into manifest array types (using getFinalArrayType) - // and never escape the getFlowTypeOfReference function. - function createEvolvingArrayType(elementType: Type): EvolvingArrayType { - const result = createObjectType(ObjectFlags.EvolvingArray); - result.elementType = elementType; - return result; - } - - function getEvolvingArrayType(elementType: Type): EvolvingArrayType { - return evolvingArrayTypes[elementType.id] || (evolvingArrayTypes[elementType.id] = createEvolvingArrayType(elementType)); - } - - // When adding evolving array element types we do not perform subtype reduction. Instead, - // we defer subtype reduction until the evolving array type is finalized into a manifest - // array type. - function addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType { - const elementType = getBaseTypeOfLiteralType(getContextFreeTypeOfExpression(node)); - return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType])); - } - - function createFinalArrayType(elementType: Type) { - return elementType.flags & TypeFlags.Never ? - autoArrayType : - createArrayType(elementType.flags & TypeFlags.Union ? - getUnionType((elementType).types, UnionReduction.Subtype) : - elementType); - } - - // We perform subtype reduction upon obtaining the final array type from an evolving array type. - function getFinalArrayType(evolvingArrayType: EvolvingArrayType): Type { - return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = createFinalArrayType(evolvingArrayType.elementType)); - } - - function finalizeEvolvingArrayType(type: Type): Type { - return getObjectFlags(type) & ObjectFlags.EvolvingArray ? getFinalArrayType(type) : type; - } - - function getElementTypeOfEvolvingArrayType(type: Type) { - return getObjectFlags(type) & ObjectFlags.EvolvingArray ? (type).elementType : neverType; - } - - function isEvolvingArrayTypeList(types: Type[]) { - let hasEvolvingArrayType = false; - for (const t of types) { - if (!(t.flags & TypeFlags.Never)) { - if (!(getObjectFlags(t) & ObjectFlags.EvolvingArray)) { - return false; - } - hasEvolvingArrayType = true; - } - } - return hasEvolvingArrayType; - } - - // At flow control branch or loop junctions, if the type along every antecedent code path - // is an evolving array type, we construct a combined evolving array type. Otherwise we - // finalize all evolving array types. - function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: UnionReduction) { - return isEvolvingArrayTypeList(types) ? - getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))) : - getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction); - } - - // Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or - // 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type. - function isEvolvingArrayOperationTarget(node: Node) { - const root = getReferenceRoot(node); - const parent = root.parent; - const isLengthPushOrUnshift = isPropertyAccessExpression(parent) && ( - parent.name.escapedText === "length" || - parent.parent.kind === SyntaxKind.CallExpression - && isIdentifier(parent.name) - && isPushOrUnshiftIdentifier(parent.name)); - const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression && - (parent).expression === root && - parent.parent.kind === SyntaxKind.BinaryExpression && - (parent.parent).operatorToken.kind === SyntaxKind.EqualsToken && - (parent.parent).left === parent && - !isAssignmentTarget(parent.parent) && - isTypeAssignableToKind(getTypeOfExpression((parent).argumentExpression), TypeFlags.NumberLike); - return isLengthPushOrUnshift || isElementAssignment; - } - - function isDeclarationWithExplicitTypeAnnotation(declaration: Declaration | undefined) { - return !!(declaration && ( - declaration.kind === SyntaxKind.VariableDeclaration || declaration.kind === SyntaxKind.Parameter || - declaration.kind === SyntaxKind.PropertyDeclaration || declaration.kind === SyntaxKind.PropertySignature) && - getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature)); - } - - function getExplicitTypeOfSymbol(symbol: Symbol, diagnostic?: Diagnostic) { - if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.ValueModule)) { - return getTypeOfSymbol(symbol); - } - if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { - if (isDeclarationWithExplicitTypeAnnotation(symbol.valueDeclaration)) { - return getTypeOfSymbol(symbol); - } - if (diagnostic && symbol.valueDeclaration) { - addRelatedInfo(diagnostic, createDiagnosticForNode(symbol.valueDeclaration, Diagnostics._0_is_declared_here, symbolToString(symbol))); - } - } - } - - // We require the dotted function name in an assertion expression to be comprised of identifiers - // that reference function, method, class or value module symbols; or variable, property or - // parameter symbols with declarations that have explicit type annotations. Such references are - // resolvable with no possibility of triggering circularities in control flow analysis. - function getTypeOfDottedName(node: Expression, diagnostic: Diagnostic | undefined): Type | undefined { - if (!(node.flags & NodeFlags.InWithStatement)) { - switch (node.kind) { - case SyntaxKind.Identifier: - const symbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(node)); - return getExplicitTypeOfSymbol(symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol, diagnostic); - case SyntaxKind.ThisKeyword: - return getExplicitThisType(node); - case SyntaxKind.PropertyAccessExpression: - const type = getTypeOfDottedName((node).expression, diagnostic); - const prop = type && getPropertyOfType(type, (node).name.escapedText); - return prop && getExplicitTypeOfSymbol(prop, diagnostic); - case SyntaxKind.ParenthesizedExpression: - return getTypeOfDottedName((node).expression, diagnostic); - } - } - } - - function getEffectsSignature(node: CallExpression) { - const links = getNodeLinks(node); - let signature = links.effectsSignature; - if (signature === undefined) { - // A call expression parented by an expression statement is a potential assertion. Other call - // expressions are potential type predicate function calls. In order to avoid triggering - // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call - // target expression of an assertion. - let funcType: Type | undefined; - if (node.parent.kind === SyntaxKind.ExpressionStatement) { - funcType = getTypeOfDottedName(node.expression, /*diagnostic*/ undefined); - } - else if (node.expression.kind !== SyntaxKind.SuperKeyword) { - if (isOptionalChain(node)) { - funcType = checkNonNullType( - getOptionalExpressionType(checkExpression(node.expression), node.expression), - node.expression - ); - } - else { - funcType = checkNonNullExpression(node.expression); - } - } - const signatures = getSignaturesOfType(funcType && getApparentType(funcType) || unknownType, SignatureKind.Call); - const candidate = signatures.length === 1 && !signatures[0].typeParameters ? signatures[0] : - some(signatures, hasTypePredicateOrNeverReturnType) ? getResolvedSignature(node) : - undefined; - signature = links.effectsSignature = candidate && hasTypePredicateOrNeverReturnType(candidate) ? candidate : unknownSignature; - } - return signature === unknownSignature ? undefined : signature; - } - - function hasTypePredicateOrNeverReturnType(signature: Signature) { - return !!(getTypePredicateOfSignature(signature) || - signature.declaration && (getReturnTypeFromAnnotation(signature.declaration) || unknownType).flags & TypeFlags.Never); - } - - function getTypePredicateArgument(predicate: TypePredicate, callExpression: CallExpression) { - if (predicate.kind === TypePredicateKind.Identifier || predicate.kind === TypePredicateKind.AssertsIdentifier) { - return callExpression.arguments[predicate.parameterIndex]; - } - const invokedExpression = skipParentheses(callExpression.expression); - return isAccessExpression(invokedExpression) ? skipParentheses(invokedExpression.expression) : undefined; - } - - function reportFlowControlError(node: Node) { - const block = findAncestor(node, isFunctionOrModuleBlock); - const sourceFile = getSourceFileOfNode(node); - const span = getSpanOfTokenAtPosition(sourceFile, block.statements.pos); - diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.The_containing_function_or_module_body_is_too_large_for_control_flow_analysis)); - } - - function isReachableFlowNode(flow: FlowNode) { - const result = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false); - lastFlowNode = flow; - lastFlowNodeReachable = result; - return result; - } - - function isUnlockedReachableFlowNode(flow: FlowNode) { - return !(flow.flags & FlowFlags.PreFinally && (flow).lock.locked) && isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false); - } - - function isFalseExpression(expr: Expression): boolean { - const node = skipParentheses(expr); - return node.kind === SyntaxKind.FalseKeyword || node.kind === SyntaxKind.BinaryExpression && ( - (node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken && (isFalseExpression((node).left) || isFalseExpression((node).right)) || - (node).operatorToken.kind === SyntaxKind.BarBarToken && isFalseExpression((node).left) && isFalseExpression((node).right)); - } - - function isReachableFlowNodeWorker(flow: FlowNode, noCacheCheck: boolean): boolean { - while (true) { - if (flow === lastFlowNode) { - return lastFlowNodeReachable; - } - const flags = flow.flags; - if (flags & FlowFlags.Shared) { - if (!noCacheCheck) { - const id = getFlowNodeId(flow); - const reachable = flowNodeReachable[id]; - return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ true)); - } - noCacheCheck = false; - } - if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.PreFinally)) { - flow = (flow).antecedent; - } - else if (flags & FlowFlags.Call) { - const signature = getEffectsSignature((flow).node); - if (signature) { - const predicate = getTypePredicateOfSignature(signature); - if (predicate && predicate.kind === TypePredicateKind.AssertsIdentifier) { - const predicateArgument = (flow).node.arguments[predicate.parameterIndex]; - if (predicateArgument && isFalseExpression(predicateArgument)) { - return false; - } - } - if (getReturnTypeOfSignature(signature).flags & TypeFlags.Never) { - return false; - } - } - flow = (flow).antecedent; - } - else if (flags & FlowFlags.BranchLabel) { - // A branching point is reachable if any branch is reachable. - return some((flow).antecedents, isUnlockedReachableFlowNode); - } - else if (flags & FlowFlags.LoopLabel) { - // A loop is reachable if the control flow path that leads to the top is reachable. - flow = (flow).antecedents![0]; - } - else if (flags & FlowFlags.SwitchClause) { - // The control flow path representing an unmatched value in a switch statement with - // no default clause is unreachable if the switch statement is exhaustive. - if ((flow).clauseStart === (flow).clauseEnd && isExhaustiveSwitchStatement((flow).switchStatement)) { - return false; - } - flow = (flow).antecedent; - } - else if (flags & FlowFlags.AfterFinally) { - // Cache is unreliable once we start locking nodes - lastFlowNode = undefined; - (flow).locked = true; - const result = isReachableFlowNodeWorker((flow).antecedent, /*skipCacheCheck*/ false); - (flow).locked = false; - return result; - } - else { - return !(flags & FlowFlags.Unreachable); - } - } - } - - function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) { - let key: string | undefined; - let keySet = false; - let flowDepth = 0; - if (flowAnalysisDisabled) { - return errorType; - } - if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) { - return declaredType; - } - flowInvocationCount++; - const sharedFlowStart = sharedFlowCount; - const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode)); - sharedFlowCount = sharedFlowStart; - // When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation, - // we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations - // on empty arrays are possible without implicit any errors and new element types can be inferred without - // type mismatch errors. - const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? autoArrayType : finalizeEvolvingArrayType(evolvedType); - if (resultType === unreachableNeverType|| reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { - return declaredType; - } - return resultType; - - function getOrSetCacheKey() { - if (keySet) { - return key; - } - keySet = true; - return key = getFlowCacheKey(reference, declaredType, initialType, flowContainer); - } - - function getTypeAtFlowNode(flow: FlowNode): FlowType { - if (flowDepth === 2000) { - // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error - // and disable further control flow analysis in the containing function or module body. - flowAnalysisDisabled = true; - reportFlowControlError(reference); - return errorType; - } - flowDepth++; - while (true) { - const flags = flow.flags; - if (flags & FlowFlags.Shared) { - // We cache results of flow type resolution for shared nodes that were previously visited in - // the same getFlowTypeOfReference invocation. A node is considered shared when it is the - // antecedent of more than one node. - for (let i = sharedFlowStart; i < sharedFlowCount; i++) { - if (sharedFlowNodes[i] === flow) { - flowDepth--; - return sharedFlowTypes[i]; - } - } - } - let type: FlowType | undefined; - if (flags & FlowFlags.AfterFinally) { - // block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement - (flow).locked = true; - type = getTypeAtFlowNode((flow).antecedent); - (flow).locked = false; - } - else if (flags & FlowFlags.PreFinally) { - // locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel - // so here just redirect to antecedent - flow = (flow).antecedent; - continue; - } - else if (flags & FlowFlags.Assignment) { - type = getTypeAtFlowAssignment(flow); - if (!type) { - flow = (flow).antecedent; - continue; - } - } - else if (flags & FlowFlags.Call) { - type = getTypeAtFlowCall(flow); - if (!type) { - flow = (flow).antecedent; - continue; - } - } - else if (flags & FlowFlags.Condition) { - type = getTypeAtFlowCondition(flow); - } - else if (flags & FlowFlags.SwitchClause) { - type = getTypeAtSwitchClause(flow); - } - else if (flags & FlowFlags.Label) { - if ((flow).antecedents!.length === 1) { - flow = (flow).antecedents![0]; - continue; - } - type = flags & FlowFlags.BranchLabel ? - getTypeAtFlowBranchLabel(flow) : - getTypeAtFlowLoopLabel(flow); - } - else if (flags & FlowFlags.ArrayMutation) { - type = getTypeAtFlowArrayMutation(flow); - if (!type) { - flow = (flow).antecedent; - continue; - } - } - else if (flags & FlowFlags.Start) { - // Check if we should continue with the control flow of the containing function. - const container = (flow).node; - if (container && container !== flowContainer && - reference.kind !== SyntaxKind.PropertyAccessExpression && - reference.kind !== SyntaxKind.ElementAccessExpression && - reference.kind !== SyntaxKind.ThisKeyword) { - flow = container.flowNode!; - continue; - } - // At the top of the flow we have the initial type. - type = initialType; - } - else { - // Unreachable code errors are reported in the binding phase. Here we - // simply return the non-auto declared type to reduce follow-on errors. - type = convertAutoToAny(declaredType); - } - if (flags & FlowFlags.Shared) { - // Record visited node and the associated type in the cache. - sharedFlowNodes[sharedFlowCount] = flow; - sharedFlowTypes[sharedFlowCount] = type; - sharedFlowCount++; - } - flowDepth--; - return type; - } - } - - function getInitialOrAssignedType(flow: FlowAssignment) { - const node = flow.node; - return getConstraintForLocation(node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ? - getInitialType(node) : - getAssignedType(node), reference); - } - - function getTypeAtFlowAssignment(flow: FlowAssignment) { - const node = flow.node; - // Assignments only narrow the computed type if the declared type is a union type. Thus, we - // only need to evaluate the assigned type if the declared type is a union type. - if (isMatchingReference(reference, node)) { - if (!isReachableFlowNode(flow)) { - return unreachableNeverType; - } - if (getAssignmentTargetKind(node) === AssignmentKind.Compound) { - const flowType = getTypeAtFlowNode(flow.antecedent); - return createFlowType(getBaseTypeOfLiteralType(getTypeFromFlowType(flowType)), isIncomplete(flowType)); - } - if (declaredType === autoType || declaredType === autoArrayType) { - if (isEmptyArrayAssignment(node)) { - return getEvolvingArrayType(neverType); - } - const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(flow)); - return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType; - } - if (declaredType.flags & TypeFlags.Union) { - return getAssignmentReducedType(declaredType, getInitialOrAssignedType(flow)); - } - return declaredType; - } - // We didn't have a direct match. However, if the reference is a dotted name, this - // may be an assignment to a left hand part of the reference. For example, for a - // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, - // return the declared type. - if (containsMatchingReference(reference, node)) { - if (!isReachableFlowNode(flow)) { - return unreachableNeverType; - } - // A matching dotted name might also be an expando property on a function *expression*, - // in which case we continue control flow analysis back to the function's declaration - if (isVariableDeclaration(node) && (isInJSFile(node) || isVarConst(node))) { - const init = getDeclaredExpandoInitializer(node); - if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) { - return getTypeAtFlowNode(flow.antecedent); - } - } - return declaredType; - } - // for (const _ in ref) acts as a nonnull on ref - if (isVariableDeclaration(node) && node.parent.parent.kind === SyntaxKind.ForInStatement && isMatchingReference(reference, node.parent.parent.expression)) { - return getNonNullableTypeIfNeeded(getTypeFromFlowType(getTypeAtFlowNode(flow.antecedent))); - } - // Assignment doesn't affect reference - return undefined; - } - - function narrowTypeByAssertion(type: Type, expr: Expression): Type { - const node = skipParentheses(expr); - if (node.kind === SyntaxKind.FalseKeyword) { - return unreachableNeverType; - } - if (node.kind === SyntaxKind.BinaryExpression) { - if ((node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { - return narrowTypeByAssertion(narrowTypeByAssertion(type, (node).left), (node).right); - } - if ((node).operatorToken.kind === SyntaxKind.BarBarToken) { - return getUnionType([narrowTypeByAssertion(type, (node).left), narrowTypeByAssertion(type, (node).right)]); - } - } - return narrowType(type, node, /*assumeTrue*/ true); - } - - function getTypeAtFlowCall(flow: FlowCall): FlowType | undefined { - const signature = getEffectsSignature(flow.node); - if (signature) { - const predicate = getTypePredicateOfSignature(signature); - if (predicate && (predicate.kind === TypePredicateKind.AssertsThis || predicate.kind === TypePredicateKind.AssertsIdentifier)) { - const flowType = getTypeAtFlowNode(flow.antecedent); - const type = getTypeFromFlowType(flowType); - const narrowedType = predicate.type ? narrowTypeByTypePredicate(type, predicate, flow.node, /*assumeTrue*/ true) : - predicate.kind === TypePredicateKind.AssertsIdentifier && predicate.parameterIndex >= 0 && predicate.parameterIndex < flow.node.arguments.length ? narrowTypeByAssertion(type, flow.node.arguments[predicate.parameterIndex]) : - type; - return narrowedType === type ? flowType : createFlowType(narrowedType, isIncomplete(flowType)); - } - if (getReturnTypeOfSignature(signature).flags & TypeFlags.Never) { - return unreachableNeverType; - } - } - return undefined; - } - - function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType | undefined { - if (declaredType === autoType || declaredType === autoArrayType) { - const node = flow.node; - const expr = node.kind === SyntaxKind.CallExpression ? - (node.expression).expression : - (node.left).expression; - if (isMatchingReference(reference, getReferenceCandidate(expr))) { - const flowType = getTypeAtFlowNode(flow.antecedent); - const type = getTypeFromFlowType(flowType); - if (getObjectFlags(type) & ObjectFlags.EvolvingArray) { - let evolvedType = type; - if (node.kind === SyntaxKind.CallExpression) { - for (const arg of node.arguments) { - evolvedType = addEvolvingArrayElementType(evolvedType, arg); - } - } - else { - // We must get the context free expression type so as to not recur in an uncached fashion on the LHS (which causes exponential blowup in compile time) - const indexType = getContextFreeTypeOfExpression((node.left).argumentExpression); - if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { - evolvedType = addEvolvingArrayElementType(evolvedType, node.right); - } - } - return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType)); - } - return flowType; - } - } - return undefined; - } - - function getTypeAtFlowCondition(flow: FlowCondition): FlowType { - const flowType = getTypeAtFlowNode(flow.antecedent); - const type = getTypeFromFlowType(flowType); - if (type.flags & TypeFlags.Never) { - return flowType; - } - // If we have an antecedent type (meaning we're reachable in some way), we first - // attempt to narrow the antecedent type. If that produces the never type, and if - // the antecedent type is incomplete (i.e. a transient type in a loop), then we - // take the type guard as an indication that control *could* reach here once we - // have the complete type. We proceed by switching to the silent never type which - // doesn't report errors when operators are applied to it. Note that this is the - // *only* place a silent never type is ever generated. - const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0; - const nonEvolvingType = finalizeEvolvingArrayType(type); - const narrowedType = narrowType(nonEvolvingType, flow.node, assumeTrue); - if (narrowedType === nonEvolvingType) { - return flowType; - } - const incomplete = isIncomplete(flowType); - const resultType = incomplete && narrowedType.flags & TypeFlags.Never ? silentNeverType : narrowedType; - return createFlowType(resultType, incomplete); - } - - function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType { - const expr = flow.switchStatement.expression; - const flowType = getTypeAtFlowNode(flow.antecedent); - let type = getTypeFromFlowType(flowType); - if (isMatchingReference(reference, expr)) { - type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); - } - else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) { - type = narrowBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); - } - else { - if (strictNullChecks) { - if (optionalChainContainsReference(expr, reference)) { - type = narrowTypeBySwitchOptionalChainContainment(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd, - t => !(t.flags & (TypeFlags.Undefined | TypeFlags.Never))); - } - else if (expr.kind === SyntaxKind.TypeOfExpression && optionalChainContainsReference((expr as TypeOfExpression).expression, reference)) { - type = narrowTypeBySwitchOptionalChainContainment(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd, - t => !(t.flags & TypeFlags.Never || t.flags & TypeFlags.StringLiteral && (t).value === "undefined")); - } - } - if (isMatchingReferenceDiscriminant(expr, type)) { - type = narrowTypeByDiscriminant(type, expr as AccessExpression, - t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); - } - else if (containsMatchingReferenceDiscriminant(reference, expr)) { - type = declaredType; - } - } - return createFlowType(type, isIncomplete(flowType)); - } - - function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { - const antecedentTypes: Type[] = []; - let subtypeReduction = false; - let seenIncomplete = false; - let bypassFlow: FlowSwitchClause | undefined; - for (const antecedent of flow.antecedents!) { - if (antecedent.flags & FlowFlags.PreFinally && (antecedent).lock.locked) { - // if flow correspond to branch from pre-try to finally and this branch is locked - this means that - // we initially have started following the flow outside the finally block. - // in this case we should ignore this branch. - continue; - } - if (!bypassFlow && antecedent.flags & FlowFlags.SwitchClause && (antecedent).clauseStart === (antecedent).clauseEnd) { - // The antecedent is the bypass branch of a potentially exhaustive switch statement. - bypassFlow = antecedent; - continue; - } - const flowType = getTypeAtFlowNode(antecedent); - const type = getTypeFromFlowType(flowType); - // If the type at a particular antecedent path is the declared type and the - // reference is known to always be assigned (i.e. when declared and initial types - // are the same), there is no reason to process more antecedents since the only - // possible outcome is subtypes that will be removed in the final union type anyway. - if (type === declaredType && declaredType === initialType) { - return type; - } - pushIfUnique(antecedentTypes, type); - // If an antecedent type is not a subset of the declared type, we need to perform - // subtype reduction. This happens when a "foreign" type is injected into the control - // flow using the instanceof operator or a user defined type predicate. - if (!isTypeSubsetOf(type, declaredType)) { - subtypeReduction = true; - } - if (isIncomplete(flowType)) { - seenIncomplete = true; - } - } - if (bypassFlow) { - const flowType = getTypeAtFlowNode(bypassFlow); - const type = getTypeFromFlowType(flowType); - // If the bypass flow contributes a type we haven't seen yet and the switch statement - // isn't exhaustive, process the bypass flow type. Since exhaustiveness checks increase - // the risk of circularities, we only want to perform them when they make a difference. - if (!contains(antecedentTypes, type) && !isExhaustiveSwitchStatement(bypassFlow.switchStatement)) { - if (type === declaredType && declaredType === initialType) { - return type; - } - antecedentTypes.push(type); - if (!isTypeSubsetOf(type, declaredType)) { - subtypeReduction = true; - } - if (isIncomplete(flowType)) { - seenIncomplete = true; - } - } - } - return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal), seenIncomplete); - } - - function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { - // If we have previously computed the control flow type for the reference at - // this flow loop junction, return the cached type. - const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); - const key = getOrSetCacheKey(); - if (!key) { - // No cache key is generated when binding patterns are in unnarrowable situations - return declaredType; - } - const cached = cache.get(key); - if (cached) { - return cached; - } - // If this flow loop junction and reference are already being processed, return - // the union of the types computed for each branch so far, marked as incomplete. - // It is possible to see an empty array in cases where loops are nested and the - // back edge of the outer loop reaches an inner loop that is already being analyzed. - // In such cases we restart the analysis of the inner loop, which will then see - // a non-empty in-process array for the outer loop and eventually terminate because - // the first antecedent of a loop junction is always the non-looping control flow - // path that leads to the top. - for (let i = flowLoopStart; i < flowLoopCount; i++) { - if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key && flowLoopTypes[i].length) { - return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], UnionReduction.Literal), /*incomplete*/ true); - } - } - // Add the flow loop junction and reference to the in-process stack and analyze - // each antecedent code path. - const antecedentTypes: Type[] = []; - let subtypeReduction = false; - let firstAntecedentType: FlowType | undefined; - for (const antecedent of flow.antecedents!) { - let flowType; - if (!firstAntecedentType) { - // The first antecedent of a loop junction is always the non-looping control - // flow path that leads to the top. - flowType = firstAntecedentType = getTypeAtFlowNode(antecedent); - } - else { - // All but the first antecedent are the looping control flow paths that lead - // back to the loop junction. We track these on the flow loop stack. - flowLoopNodes[flowLoopCount] = flow; - flowLoopKeys[flowLoopCount] = key; - flowLoopTypes[flowLoopCount] = antecedentTypes; - flowLoopCount++; - const saveFlowTypeCache = flowTypeCache; - flowTypeCache = undefined; - flowType = getTypeAtFlowNode(antecedent); - flowTypeCache = saveFlowTypeCache; - flowLoopCount--; - // If we see a value appear in the cache it is a sign that control flow analysis - // was restarted and completed by checkExpressionCached. We can simply pick up - // the resulting type and bail out. - const cached = cache.get(key); - if (cached) { - return cached; - } - } - const type = getTypeFromFlowType(flowType); - pushIfUnique(antecedentTypes, type); - // If an antecedent type is not a subset of the declared type, we need to perform - // subtype reduction. This happens when a "foreign" type is injected into the control - // flow using the instanceof operator or a user defined type predicate. - if (!isTypeSubsetOf(type, declaredType)) { - subtypeReduction = true; - } - // If the type at a particular antecedent path is the declared type there is no - // reason to process more antecedents since the only possible outcome is subtypes - // that will be removed in the final union type anyway. - if (type === declaredType) { - break; - } - } - // The result is incomplete if the first antecedent (the non-looping control flow path) - // is incomplete. - const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal); - if (isIncomplete(firstAntecedentType!)) { - return createFlowType(result, /*incomplete*/ true); - } - cache.set(key, result); - return result; - } - - function isMatchingReferenceDiscriminant(expr: Expression, computedType: Type) { - if (!(computedType.flags & TypeFlags.Union) || !isAccessExpression(expr)) { - return false; - } - const name = getAccessedPropertyName(expr); - if (name === undefined) { - return false; - } - return isMatchingReference(reference, expr.expression) && isDiscriminantProperty(computedType, name); - } - - function narrowTypeByDiscriminant(type: Type, access: AccessExpression, narrowType: (t: Type) => Type): Type { - const propName = getAccessedPropertyName(access); - if (propName === undefined) { - return type; - } - const propType = getTypeOfPropertyOfType(type, propName); - if (!propType) { - return type; - } - const narrowedPropType = narrowType(propType); - return filterType(type, t => { - const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName); - return !(discriminantType.flags & TypeFlags.Never) && isTypeComparableTo(discriminantType, narrowedPropType); - }); - } - - function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { - if (isMatchingReference(reference, expr)) { - return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); - } - if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) { - type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); - } - if (isMatchingReferenceDiscriminant(expr, declaredType)) { - return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); - } - if (containsMatchingReferenceDiscriminant(reference, expr)) { - return declaredType; - } - return type; - } - - function isTypePresencePossible(type: Type, propName: __String, assumeTrue: boolean) { - if (getIndexInfoOfType(type, IndexKind.String)) { - return true; - } - const prop = getPropertyOfType(type, propName); - if (prop) { - return prop.flags & SymbolFlags.Optional ? true : assumeTrue; - } - return !assumeTrue; - } - - function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { - if (type.flags & (TypeFlags.Union | TypeFlags.Object) || isThisTypeParameter(type)) { - const propName = escapeLeadingUnderscores(literal.text); - return filterType(type, t => isTypePresencePossible(t, propName, assumeTrue)); - } - return type; - } - - function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - switch (expr.operatorToken.kind) { - case SyntaxKind.EqualsToken: - return narrowTypeByTruthiness(narrowType(type, expr.right, assumeTrue), expr.left, assumeTrue); - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - const operator = expr.operatorToken.kind; - const left = getReferenceCandidate(expr.left); - const right = getReferenceCandidate(expr.right); - if (left.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(right)) { - return narrowTypeByTypeof(type, left, operator, right, assumeTrue); - } - if (right.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(left)) { - return narrowTypeByTypeof(type, right, operator, left, assumeTrue); - } - if (isMatchingReference(reference, left)) { - return narrowTypeByEquality(type, operator, right, assumeTrue); - } - if (isMatchingReference(reference, right)) { - return narrowTypeByEquality(type, operator, left, assumeTrue); - } - if (strictNullChecks) { - if (optionalChainContainsReference(left, reference)) { - type = narrowTypeByOptionalChainContainment(type, operator, right, assumeTrue); - } - else if (optionalChainContainsReference(right, reference)) { - type = narrowTypeByOptionalChainContainment(type, operator, left, assumeTrue); - } - } - if (isMatchingReferenceDiscriminant(left, declaredType)) { - return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); - } - if (isMatchingReferenceDiscriminant(right, declaredType)) { - return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); - } - if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { - return declaredType; - } - break; - case SyntaxKind.InstanceOfKeyword: - return narrowTypeByInstanceof(type, expr, assumeTrue); - case SyntaxKind.InKeyword: - const target = getReferenceCandidate(expr.right); - if (isStringLiteralLike(expr.left) && isMatchingReference(reference, target)) { - return narrowByInKeyword(type, expr.left, assumeTrue); - } - break; - case SyntaxKind.CommaToken: - return narrowType(type, expr.right, assumeTrue); - } - return type; - } - - function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { - // We are in a branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from - // the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the - // operator is !== and the type of value is undefined. - const effectiveTrue = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken ? assumeTrue : !assumeTrue; - const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; - const valueNonNullish = !(getTypeFacts(getTypeOfExpression(value)) & (doubleEquals ? TypeFacts.EQUndefinedOrNull : TypeFacts.EQUndefined)); - return effectiveTrue === valueNonNullish ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; - } - - function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { - if (type.flags & TypeFlags.Any) { - return type; - } - if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { - assumeTrue = !assumeTrue; - } - const valueType = getTypeOfExpression(value); - if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) { - if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) { - return valueType; - } - if (valueType.flags & TypeFlags.Object) { - return nonPrimitiveType; - } - return type; - } - if (valueType.flags & TypeFlags.Nullable) { - if (!strictNullChecks) { - return type; - } - const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; - const facts = doubleEquals ? - assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : - valueType.flags & TypeFlags.Null ? - assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : - assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; - return getTypeWithFacts(type, facts); - } - if (type.flags & TypeFlags.NotUnionOrUnit) { - return type; - } - if (assumeTrue) { - const narrowedType = filterType(type, filterFn); - return narrowedType.flags & TypeFlags.Never ? type : replacePrimitivesWithLiterals(narrowedType, valueType); - } - if (isUnitType(valueType)) { - const regularType = getRegularTypeOfLiteralType(valueType); - return filterType(type, t => getRegularTypeOfLiteralType(t) !== regularType); - } - return type; - } - - function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type { - // We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands - if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { - assumeTrue = !assumeTrue; - } - const target = getReferenceCandidate(typeOfExpr.expression); - if (!isMatchingReference(reference, target)) { - if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) { - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); - } - // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the - // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, target)) { - return declaredType; - } - return type; - } - if (type.flags & TypeFlags.Any && literal.text === "function") { - return type; - } - const facts = assumeTrue ? - typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : - typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; - return getTypeWithFacts(assumeTrue ? mapType(type, narrowTypeForTypeof) : type, facts); - - function narrowTypeForTypeof(type: Type) { - if (type.flags & TypeFlags.Unknown && literal.text === "object") { - return getUnionType([nonPrimitiveType, nullType]); - } - // We narrow a non-union type to an exact primitive type if the non-union type - // is a supertype of that primitive type. For example, type 'any' can be narrowed - // to one of the primitive types. - const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text); - if (targetType) { - if (isTypeSubtypeOf(type, targetType)) { - return type; - } - if (isTypeSubtypeOf(targetType, type)) { - return targetType; - } - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type) || anyType; - if (isTypeSubtypeOf(targetType, constraint)) { - return getIntersectionType([type, targetType]); - } - } - } - return type; - } - } - - function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) { - const everyClauseChecks = clauseStart !== clauseEnd && every(getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd), clauseCheck); - return everyClauseChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; - } - - function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) { - // We only narrow if all case expressions specify - // values with unit types, except for the case where - // `type` is unknown. In this instance we map object - // types to the nonPrimitive type and narrow with that. - const switchTypes = getSwitchClauseTypes(switchStatement); - if (!switchTypes.length) { - return type; - } - const clauseTypes = switchTypes.slice(clauseStart, clauseEnd); - const hasDefaultClause = clauseStart === clauseEnd || contains(clauseTypes, neverType); - if ((type.flags & TypeFlags.Unknown) && !hasDefaultClause) { - let groundClauseTypes: Type[] | undefined; - for (let i = 0; i < clauseTypes.length; i += 1) { - const t = clauseTypes[i]; - if (t.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) { - if (groundClauseTypes !== undefined) { - groundClauseTypes.push(t); - } - } - else if (t.flags & TypeFlags.Object) { - if (groundClauseTypes === undefined) { - groundClauseTypes = clauseTypes.slice(0, i); - } - groundClauseTypes.push(nonPrimitiveType); - } - else { - return type; - } - } - return getUnionType(groundClauseTypes === undefined ? clauseTypes : groundClauseTypes); - } - const discriminantType = getUnionType(clauseTypes); - const caseType = - discriminantType.flags & TypeFlags.Never ? neverType : - replacePrimitivesWithLiterals(filterType(type, t => areTypesComparable(discriminantType, t)), discriminantType); - if (!hasDefaultClause) { - return caseType; - } - const defaultType = filterType(type, t => !(isUnitType(t) && contains(switchTypes, getRegularTypeOfLiteralType(t)))); - return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]); - } - - function getImpliedTypeFromTypeofCase(type: Type, text: string) { - switch (text) { - case "function": - return type.flags & TypeFlags.Any ? type : globalFunctionType; - case "object": - return type.flags & TypeFlags.Unknown ? getUnionType([nonPrimitiveType, nullType]) : type; - default: - return typeofTypesByName.get(text) || type; - } - } - - function narrowTypeForTypeofSwitch(candidate: Type) { - return (type: Type) => { - if (isTypeSubtypeOf(candidate, type)) { - return candidate; - } - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type) || anyType; - if (isTypeSubtypeOf(candidate, constraint)) { - return getIntersectionType([type, candidate]); - } - } - return type; - }; - } - - function narrowBySwitchOnTypeOf(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type { - const switchWitnesses = getSwitchClauseTypeOfWitnesses(switchStatement); - if (!switchWitnesses.length) { - return type; - } - // Equal start and end denotes implicit fallthrough; undefined marks explicit default clause - const defaultCaseLocation = findIndex(switchWitnesses, elem => elem === undefined); - const hasDefaultClause = clauseStart === clauseEnd || (defaultCaseLocation >= clauseStart && defaultCaseLocation < clauseEnd); - let clauseWitnesses: string[]; - let switchFacts: TypeFacts; - if (defaultCaseLocation > -1) { - // We no longer need the undefined denoting an - // explicit default case. Remove the undefined and - // fix-up clauseStart and clauseEnd. This means - // that we don't have to worry about undefined - // in the witness array. - const witnesses = switchWitnesses.filter(witness => witness !== undefined); - // The adjusted clause start and end after removing the `default` statement. - const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart; - const fixedClauseEnd = defaultCaseLocation < clauseEnd ? clauseEnd - 1 : clauseEnd; - clauseWitnesses = witnesses.slice(fixedClauseStart, fixedClauseEnd); - switchFacts = getFactsFromTypeofSwitch(fixedClauseStart, fixedClauseEnd, witnesses, hasDefaultClause); - } - else { - clauseWitnesses = switchWitnesses.slice(clauseStart, clauseEnd); - switchFacts = getFactsFromTypeofSwitch(clauseStart, clauseEnd, switchWitnesses, hasDefaultClause); - } - if (hasDefaultClause) { - return filterType(type, t => (getTypeFacts(t) & switchFacts) === switchFacts); - } - /* - The implied type is the raw type suggested by a - value being caught in this clause. - - When the clause contains a default case we ignore - the implied type and try to narrow using any facts - we can learn: see `switchFacts`. - - Example: - switch (typeof x) { - case 'number': - case 'string': break; - default: break; - case 'number': - case 'boolean': break - } - - In the first clause (case `number` and `string`) the - implied type is number | string. - - In the default clause we de not compute an implied type. - - In the third clause (case `number` and `boolean`) - the naive implied type is number | boolean, however - we use the type facts to narrow the implied type to - boolean. We know that number cannot be selected - because it is caught in the first clause. - */ - let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofCase(type, text))), switchFacts); - if (impliedType.flags & TypeFlags.Union) { - impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOrType(type)); - } - return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts); - } - - function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - const left = getReferenceCandidate(expr.left); - if (!isMatchingReference(reference, left)) { - if (assumeTrue && strictNullChecks && optionalChainContainsReference(left, reference)) { - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); - } - // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the - // narrowed type of 'y' to its declared type. We do this because preceding 'x.y' - // references might reference a different 'y' property. However, we make an exception - // for property accesses where x is a synthetic 'this' expression, indicating that we - // were called from isPropertyInitializedInConstructor. Without this exception, - // initializations of 'this' properties that occur before a 'this instanceof XXX' - // check would not be considered. - if (containsMatchingReference(reference, left) && !isSyntheticThisPropertyAccess(reference)) { - return declaredType; - } - return type; - } - - // Check that right operand is a function type with a prototype property - const rightType = getTypeOfExpression(expr.right); - if (!isTypeDerivedFrom(rightType, globalFunctionType)) { - return type; - } - - let targetType: Type | undefined; - const prototypeProperty = getPropertyOfType(rightType, "prototype" as __String); - if (prototypeProperty) { - // Target type is type of the prototype property - const prototypePropertyType = getTypeOfSymbol(prototypeProperty); - if (!isTypeAny(prototypePropertyType)) { - targetType = prototypePropertyType; - } - } - - // Don't narrow from 'any' if the target type is exactly 'Object' or 'Function' - if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) { - return type; - } - - if (!targetType) { - const constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct); - targetType = constructSignatures.length ? - getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature)))) : - emptyObjectType; - } - - return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); - } - - function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) { - if (!assumeTrue) { - return filterType(type, t => !isRelated(t, candidate)); - } - // If the current type is a union type, remove all constituents that couldn't be instances of - // the candidate type. If one or more constituents remain, return a union of those. - if (type.flags & TypeFlags.Union) { - const assignableType = filterType(type, t => isRelated(t, candidate)); - if (!(assignableType.flags & TypeFlags.Never)) { - return assignableType; - } - } - // If the candidate type is a subtype of the target type, narrow to the candidate type. - // Otherwise, if the target type is assignable to the candidate type, keep the target type. - // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate - // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the - // two types. - return isTypeSubtypeOf(candidate, type) ? candidate : - isTypeAssignableTo(type, candidate) ? type : - isTypeAssignableTo(candidate, type) ? candidate : - getIntersectionType([type, candidate]); - } - - function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { - if (hasMatchingArgument(callExpression, reference)) { - const signature = assumeTrue || !isCallChain(callExpression) ? getEffectsSignature(callExpression) : undefined; - const predicate = signature && getTypePredicateOfSignature(signature); - if (predicate && (predicate.kind === TypePredicateKind.This || predicate.kind === TypePredicateKind.Identifier)) { - return narrowTypeByTypePredicate(type, predicate, callExpression, assumeTrue); - } - } - return type; - } - - function narrowTypeByTypePredicate(type: Type, predicate: TypePredicate, callExpression: CallExpression, assumeTrue: boolean): Type { - // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' - if (predicate.type && !(isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType))) { - const predicateArgument = getTypePredicateArgument(predicate, callExpression); - if (predicateArgument) { - if (isMatchingReference(reference, predicateArgument)) { - return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf); - } - if (strictNullChecks && assumeTrue && optionalChainContainsReference(predicateArgument, reference) && - !(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) { - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); - } - if (containsMatchingReference(reference, predicateArgument)) { - return declaredType; - } - } - } - return type; - } - - // Narrow the given type based on the given expression having the assumed boolean value. The returned type - // will be a subtype or the same type as the argument. - function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type { - // for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a` - if (isExpressionOfOptionalChainRoot(expr) || - isBinaryExpression(expr.parent) && expr.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken && expr.parent.left === expr) { - return narrowTypeByOptionality(type, expr, assumeTrue); - } - switch (expr.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.ThisKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return narrowTypeByTruthiness(type, expr, assumeTrue); - case SyntaxKind.CallExpression: - return narrowTypeByCallExpression(type, expr, assumeTrue); - case SyntaxKind.ParenthesizedExpression: - return narrowType(type, (expr).expression, assumeTrue); - case SyntaxKind.BinaryExpression: - return narrowTypeByBinaryExpression(type, expr, assumeTrue); - case SyntaxKind.PrefixUnaryExpression: - if ((expr).operator === SyntaxKind.ExclamationToken) { - return narrowType(type, (expr).operand, !assumeTrue); - } - break; - } - return type; - } - - function narrowTypeByOptionality(type: Type, expr: Expression, assumePresent: boolean): Type { - if (isMatchingReference(reference, expr)) { - return getTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull); - } - if (isMatchingReferenceDiscriminant(expr, declaredType)) { - return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull)); - } - if (containsMatchingReferenceDiscriminant(reference, expr)) { - return declaredType; - } - return type; - } - } - - function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { - symbol = symbol.exportSymbol || symbol; - - // If we have an identifier or a property access at the given location, if the location is - // an dotted name expression, and if the location is not an assignment target, obtain the type - // of the expression (which will reflect control flow analysis). If the expression indeed - // resolved to the given symbol, return the narrowed type. - if (location.kind === SyntaxKind.Identifier) { - if (isRightSideOfQualifiedNameOrPropertyAccess(location)) { - location = location.parent; - } - if (isExpressionNode(location) && !isAssignmentTarget(location)) { - const type = getTypeOfExpression(location); - if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location).resolvedSymbol) === symbol) { - return type; - } - } - } - // The location isn't a reference to the given symbol, meaning we're being asked - // a hypothetical question of what type the symbol would have if there was a reference - // to it at the given location. Since we have no control flow information for the - // hypothetical reference (control flow information is created and attached by the - // binder), we simply return the declared type of the symbol. - return getTypeOfSymbol(symbol); - } - - function getControlFlowContainer(node: Node): Node { - return findAncestor(node.parent, node => - isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) || - node.kind === SyntaxKind.ModuleBlock || - node.kind === SyntaxKind.SourceFile || - node.kind === SyntaxKind.PropertyDeclaration)!; - } - - // Check if a parameter is assigned anywhere within its declaring function. - function isParameterAssigned(symbol: Symbol) { - const func = getRootDeclaration(symbol.valueDeclaration).parent; - const links = getNodeLinks(func); - if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) { - links.flags |= NodeCheckFlags.AssignmentsMarked; - if (!hasParentWithAssignmentsMarked(func)) { - markParameterAssignments(func); - } - } - return symbol.isAssigned || false; - } - - function hasParentWithAssignmentsMarked(node: Node) { - return !!findAncestor(node.parent, node => isFunctionLike(node) && !!(getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked)); - } - - function markParameterAssignments(node: Node) { - if (node.kind === SyntaxKind.Identifier) { - if (isAssignmentTarget(node)) { - const symbol = getResolvedSymbol(node); - if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) { - symbol.isAssigned = true; - } - } - } - else { - forEachChild(node, markParameterAssignments); - } - } - - function isConstVariable(symbol: Symbol) { - return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && getTypeOfSymbol(symbol) !== autoArrayType; - } - - /** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */ - function removeOptionalityFromDeclaredType(declaredType: Type, declaration: VariableLikeDeclaration): Type { - const annotationIncludesUndefined = strictNullChecks && - declaration.kind === SyntaxKind.Parameter && - declaration.initializer && - getFalsyFlags(declaredType) & TypeFlags.Undefined && - !(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined); - return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType; - } - - function isConstraintPosition(node: Node) { - const parent = node.parent; - return parent.kind === SyntaxKind.PropertyAccessExpression || - parent.kind === SyntaxKind.CallExpression && (parent).expression === node || - parent.kind === SyntaxKind.ElementAccessExpression && (parent).expression === node || - parent.kind === SyntaxKind.BindingElement && (parent).name === node && !!(parent).initializer; - } - - function typeHasNullableConstraint(type: Type) { - return type.flags & TypeFlags.InstantiableNonPrimitive && maybeTypeOfKind(getBaseConstraintOfType(type) || unknownType, TypeFlags.Nullable); - } - - function getConstraintForLocation(type: Type, node: Node): Type; - function getConstraintForLocation(type: Type | undefined, node: Node): Type | undefined; - function getConstraintForLocation(type: Type, node: Node): Type | undefined { - // When a node is the left hand expression of a property access, element access, or call expression, - // and the type of the node includes type variables with constraints that are nullable, we fetch the - // apparent type of the node *before* performing control flow analysis such that narrowings apply to - // the constraint type. - if (type && isConstraintPosition(node) && forEachType(type, typeHasNullableConstraint)) { - return mapType(getWidenedType(type), getBaseConstraintOrType); - } - return type; - } - - function isExportOrExportExpression(location: Node) { - return !!findAncestor(location, e => e.parent && isExportAssignment(e.parent) && e.parent.expression === e && isEntityNameExpression(e)); - } - - function markAliasReferenced(symbol: Symbol, location: Node) { - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) { - markAliasSymbolAsReferenced(symbol); - } - } - - function checkIdentifier(node: Identifier): Type { - const symbol = getResolvedSymbol(node); - if (symbol === unknownSymbol) { - return errorType; - } - - // As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects. - // Although in down-level emit of arrow function, we emit it using function expression which means that - // arguments objects will be bound to the inner object; emitting arrow function natively in ES6, arguments objects - // will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior. - // To avoid that we will give an error to users if they use arguments objects in arrow function so that they - // can explicitly bound arguments objects - if (symbol === argumentsSymbol) { - const container = getContainingFunction(node)!; - if (languageVersion < ScriptTarget.ES2015) { - if (container.kind === SyntaxKind.ArrowFunction) { - error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression); - } - else if (hasModifier(container, ModifierFlags.Async)) { - error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_async_function_or_method_in_ES3_and_ES5_Consider_using_a_standard_function_or_method); - } - } - - getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments; - return getTypeOfSymbol(symbol); - } - - // We should only mark aliases as referenced if there isn't a local value declaration - // for the symbol. Also, don't mark any property access expression LHS - checkPropertyAccessExpression will handle that - if (!(node.parent && isPropertyAccessExpression(node.parent) && node.parent.expression === node)) { - markAliasReferenced(symbol, node); - } - - const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); - let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration; - - if (localOrExportSymbol.flags & SymbolFlags.Class) { - // Due to the emit for class decorators, any reference to the class from inside of the class body - // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind - // behavior of class names in ES6. - if (declaration.kind === SyntaxKind.ClassDeclaration - && nodeIsDecorated(declaration as ClassDeclaration)) { - let container = getContainingClass(node); - while (container !== undefined) { - if (container === declaration && container.name !== node) { - getNodeLinks(declaration).flags |= NodeCheckFlags.ClassWithConstructorReference; - getNodeLinks(node).flags |= NodeCheckFlags.ConstructorReferenceInClass; - break; - } - - container = getContainingClass(container); - } - } - else if (declaration.kind === SyntaxKind.ClassExpression) { - // When we emit a class expression with static members that contain a reference - // to the constructor in the initializer, we will need to substitute that - // binding with an alias as the class name is not in scope. - let container = getThisContainer(node, /*includeArrowFunctions*/ false); - while (container.kind !== SyntaxKind.SourceFile) { - if (container.parent === declaration) { - if (container.kind === SyntaxKind.PropertyDeclaration && hasModifier(container, ModifierFlags.Static)) { - getNodeLinks(declaration).flags |= NodeCheckFlags.ClassWithConstructorReference; - getNodeLinks(node).flags |= NodeCheckFlags.ConstructorReferenceInClass; - } - break; - } - - container = getThisContainer(container, /*includeArrowFunctions*/ false); - } - } - } - - checkNestedBlockScopedBinding(node, symbol); - - const type = getConstraintForLocation(getTypeOfSymbol(localOrExportSymbol), node); - const assignmentKind = getAssignmentTargetKind(node); - - if (assignmentKind) { - if (!(localOrExportSymbol.flags & SymbolFlags.Variable) && - !(isInJSFile(node) && localOrExportSymbol.flags & SymbolFlags.ValueModule)) { - error(node, Diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable, symbolToString(symbol)); - return errorType; - } - if (isReadonlySymbol(localOrExportSymbol)) { - if (localOrExportSymbol.flags & SymbolFlags.Variable) { - error(node, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant, symbolToString(symbol)); - } - else { - error(node, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, symbolToString(symbol)); - } - return errorType; - } - } - - const isAlias = localOrExportSymbol.flags & SymbolFlags.Alias; - - // We only narrow variables and parameters occurring in a non-assignment position. For all other - // entities we simply return the declared type. - if (localOrExportSymbol.flags & SymbolFlags.Variable) { - if (assignmentKind === AssignmentKind.Definite) { - return type; - } - } - else if (isAlias) { - declaration = find(symbol.declarations, isSomeImportDeclaration); - } - else { - return type; - } - - if (!declaration) { - return type; - } - - // The declaration container is the innermost function that encloses the declaration of the variable - // or parameter. The flow container is the innermost function starting with which we analyze the control - // flow graph to determine the control flow based type. - const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; - const declarationContainer = getControlFlowContainer(declaration); - let flowContainer = getControlFlowContainer(node); - const isOuterVariable = flowContainer !== declarationContainer; - const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent); - const isModuleExports = symbol.flags & SymbolFlags.ModuleExports; - // When the control flow originates in a function expression or arrow function and we are referencing - // a const variable or parameter from an outer function, we extend the origin of the control flow - // analysis to include the immediately enclosing function. - while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression || - flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer)) && - (isConstVariable(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) { - flowContainer = getControlFlowContainer(flowContainer); - } - // We only look for uninitialized variables in strict null checking mode, and only when we can analyze - // the entire control flow graph from the variable's declaration (i.e. when the flow container and - // declaration container are the same). - const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isBindingElement(declaration) || - type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 || - isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || - node.parent.kind === SyntaxKind.NonNullExpression || - declaration.kind === SyntaxKind.VariableDeclaration && (declaration).exclamationToken || - declaration.flags & NodeFlags.Ambient; - const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) : - type === autoType || type === autoArrayType ? undefinedType : - getOptionalType(type); - const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized); - // A variable is considered uninitialized when it is possible to analyze the entire control flow graph - // from declaration to use, and when the variable's declared type doesn't include undefined but the - // control flow based type does include undefined. - if (!isEvolvingArrayOperationTarget(node) && (type === autoType || type === autoArrayType)) { - if (flowType === autoType || flowType === autoArrayType) { - if (noImplicitAny) { - error(getNameOfDeclaration(declaration), Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(flowType)); - error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(flowType)); - } - return convertAutoToAny(flowType); - } - } - else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { - const diag = error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); - - // See GH:32846 - if the user is using a variable whose type is () => T1 | ... | undefined - // they may have meant to specify the type as (() => T1 | ...) | undefined - // This is assumed if: the type is a FunctionType, the return type is a Union, the last constituent of - // the union is `undefined` - if (type.symbol && type.symbol.declarations.length === 1 && isFunctionTypeNode(type.symbol.declarations[0])) { - const funcTypeNode = type.symbol.declarations[0]; - const returnType = getReturnTypeFromAnnotation(funcTypeNode); - if (returnType && returnType.flags & TypeFlags.Union) { - const unionTypes = (funcTypeNode.type).types; - if (unionTypes && unionTypes[unionTypes.length - 1].kind === SyntaxKind.UndefinedKeyword) { - const parenedFuncType = getMutableClone(funcTypeNode); - // Highlight to the end of the second to last constituent of the union - parenedFuncType.end = unionTypes[unionTypes.length - 2].end; - addRelatedInfo(diag, createDiagnosticForNode(parenedFuncType, Diagnostics.Did_you_mean_to_parenthesize_this_function_type)); - } - } - } - - // Return the declared type to reduce follow-on errors - return type; - } - return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType; - } - - function isInsideFunction(node: Node, threshold: Node): boolean { - return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n)); - } - - function getPartOfForStatementContainingNode(node: Node, container: ForStatement) { - return findAncestor(node, n => n === container ? "quit" : n === container.initializer || n === container.condition || n === container.incrementor || n === container.statement); - } - - function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void { - if (languageVersion >= ScriptTarget.ES2015 || - (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 || - isSourceFile(symbol.valueDeclaration) || - symbol.valueDeclaration.parent.kind === SyntaxKind.CatchClause) { - return; - } - - // 1. walk from the use site up to the declaration and check - // if there is anything function like between declaration and use-site (is binding/class is captured in function). - // 2. walk from the declaration up to the boundary of lexical environment and check - // if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement) - - const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); - const usedInFunction = isInsideFunction(node.parent, container); - let current = container; - - let containedInIterationStatement = false; - while (current && !nodeStartsNewLexicalEnvironment(current)) { - if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { - containedInIterationStatement = true; - break; - } - current = current.parent; - } - - if (containedInIterationStatement) { - if (usedInFunction) { - // mark iteration statement as containing block-scoped binding captured in some function - let capturesBlockScopeBindingInLoopBody = true; - if (isForStatement(container)) { - const varDeclList = getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList); - if (varDeclList && varDeclList.parent === container) { - const part = getPartOfForStatementContainingNode(node.parent, container); - if (part) { - const links = getNodeLinks(part); - links.flags |= NodeCheckFlags.ContainsCapturedBlockScopeBinding; - - const capturedBindings = links.capturedBlockScopeBindings || (links.capturedBlockScopeBindings = []); - pushIfUnique(capturedBindings, symbol); - - if (part === container.initializer) { - capturesBlockScopeBindingInLoopBody = false; // Initializer is outside of loop body - } - } - } - } - if (capturesBlockScopeBindingInLoopBody) { - getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding; - } - } - - // mark variables that are declared in loop initializer and reassigned inside the body of ForStatement. - // if body of ForStatement will be converted to function then we'll need a extra machinery to propagate reassigned values back. - if (isForStatement(container)) { - const varDeclList = getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList); - if (varDeclList && varDeclList.parent === container && isAssignedInBodyOfForStatement(node, container)) { - getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.NeedsLoopOutParameter; - } - } - - // set 'declared inside loop' bit on the block-scoped binding - getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; - } - - if (usedInFunction) { - getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.CapturedBlockScopedBinding; - } - } - - function isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement) { - const links = getNodeLinks(node); - return !!links && contains(links.capturedBlockScopeBindings, getSymbolOfNode(decl)); - } - - function isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean { - // skip parenthesized nodes - let current: Node = node; - while (current.parent.kind === SyntaxKind.ParenthesizedExpression) { - current = current.parent; - } - - // check if node is used as LHS in some assignment expression - let isAssigned = false; - if (isAssignmentTarget(current)) { - isAssigned = true; - } - else if ((current.parent.kind === SyntaxKind.PrefixUnaryExpression || current.parent.kind === SyntaxKind.PostfixUnaryExpression)) { - const expr = current.parent; - isAssigned = expr.operator === SyntaxKind.PlusPlusToken || expr.operator === SyntaxKind.MinusMinusToken; - } - - if (!isAssigned) { - return false; - } - - // at this point we know that node is the target of assignment - // now check that modification happens inside the statement part of the ForStatement - return !!findAncestor(current, n => n === container ? "quit" : n === container.statement); - } - - function captureLexicalThis(node: Node, container: Node): void { - getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis; - if (container.kind === SyntaxKind.PropertyDeclaration || container.kind === SyntaxKind.Constructor) { - const classNode = container.parent; - getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis; - } - else { - getNodeLinks(container).flags |= NodeCheckFlags.CaptureThis; - } - } - - function findFirstSuperCall(n: Node): SuperCall | undefined { - if (isSuperCall(n)) { - return n; - } - else if (isFunctionLike(n)) { - return undefined; - } - return forEachChild(n, findFirstSuperCall); - } - - /** - * Return a cached result if super-statement is already found. - * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor - * - * @param constructor constructor-function to look for super statement - */ - function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined { - const links = getNodeLinks(constructor); - - // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result - if (links.hasSuperCall === undefined) { - links.superCall = findFirstSuperCall(constructor.body!); - links.hasSuperCall = links.superCall ? true : false; - } - return links.superCall!; - } - - /** - * Check if the given class-declaration extends null then return true. - * Otherwise, return false - * @param classDecl a class declaration to check if it extends null - */ - function classDeclarationExtendsNull(classDecl: ClassDeclaration): boolean { - const classSymbol = getSymbolOfNode(classDecl); - const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); - const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType); - - return baseConstructorType === nullWideningType; - } - - function checkThisBeforeSuper(node: Node, container: Node, diagnosticMessage: DiagnosticMessage) { - const containingClassDecl = container.parent; - const baseTypeNode = getClassExtendsHeritageElement(containingClassDecl); - - // If a containing class does not have extends clause or the class extends null - // skip checking whether super statement is called before "this" accessing. - if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) { - const superCall = getSuperCallInConstructor(container); - - // We should give an error in the following cases: - // - No super-call - // - "this" is accessing before super-call. - // i.e super(this) - // this.x; super(); - // We want to make sure that super-call is done before accessing "this" so that - // "this" is not accessed as a parameter of the super-call. - if (!superCall || superCall.end > node.pos) { - // In ES6, super inside constructor of class-declaration has to precede "this" accessing - error(node, diagnosticMessage); - } - } - } - - function checkThisExpression(node: Node): Type { - // Stop at the first arrow function so that we can - // tell whether 'this' needs to be captured. - let container = getThisContainer(node, /* includeArrowFunctions */ true); - let capturedByArrowFunction = false; - - if (container.kind === SyntaxKind.Constructor) { - checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class); - } - - // Now skip arrow functions to get the "real" owner of 'this'. - if (container.kind === SyntaxKind.ArrowFunction) { - container = getThisContainer(container, /* includeArrowFunctions */ false); - capturedByArrowFunction = true; - } - - switch (container.kind) { - case SyntaxKind.ModuleDeclaration: - error(node, Diagnostics.this_cannot_be_referenced_in_a_module_or_namespace_body); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - break; - case SyntaxKind.EnumDeclaration: - error(node, Diagnostics.this_cannot_be_referenced_in_current_location); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - break; - case SyntaxKind.Constructor: - if (isInConstructorArgumentInitializer(node, container)) { - error(node, Diagnostics.this_cannot_be_referenced_in_constructor_arguments); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - } - break; - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - if (hasModifier(container, ModifierFlags.Static)) { - error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - } - break; - case SyntaxKind.ComputedPropertyName: - error(node, Diagnostics.this_cannot_be_referenced_in_a_computed_property_name); - break; - } - - // When targeting es6, mark that we'll need to capture `this` in its lexically bound scope. - if (capturedByArrowFunction && languageVersion < ScriptTarget.ES2015) { - captureLexicalThis(node, container); - } - - const type = tryGetThisTypeAt(node, /*includeGlobalThis*/ true, container); - if (noImplicitThis) { - const globalThisType = getTypeOfSymbol(globalThisSymbol); - if (type === globalThisType && capturedByArrowFunction) { - error(node, Diagnostics.The_containing_arrow_function_captures_the_global_value_of_this); - } - else if (!type) { - // With noImplicitThis, functions may not reference 'this' if it has type 'any' - const diag = error(node, Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation); - if (!isSourceFile(container)) { - const outsideThis = tryGetThisTypeAt(container); - if (outsideThis && outsideThis !== globalThisType) { - addRelatedInfo(diag, createDiagnosticForNode(container, Diagnostics.An_outer_value_of_this_is_shadowed_by_this_container)); - } - } - } - } - return type || anyType; - } - - function tryGetThisTypeAt(node: Node, includeGlobalThis = true, container = getThisContainer(node, /*includeArrowFunctions*/ false)): Type | undefined { - const isInJS = isInJSFile(node); - if (isFunctionLike(container) && - (!isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container))) { - // Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated. - // If this is a function in a JS file, it might be a class method. - const className = getClassNameFromPrototypeMethod(container); - if (isInJS && className) { - const classSymbol = checkExpression(className).symbol; - if (classSymbol && classSymbol.members && (classSymbol.flags & SymbolFlags.Function)) { - const classType = (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType).thisType!; - return getFlowTypeOfReference(node, classType); - } - } - // Check if it's a constructor definition, can be either a variable decl or function decl - // i.e. - // * /** @constructor */ function [name]() { ... } - // * /** @constructor */ var x = function() { ... } - else if (isInJS && - (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.FunctionDeclaration) && - getJSDocClassTag(container)) { - const classType = (getDeclaredTypeOfSymbol(getMergedSymbol(container.symbol)) as InterfaceType).thisType!; - return getFlowTypeOfReference(node, classType); - } - - const thisType = getThisTypeOfDeclaration(container) || getContextualThisParameterType(container); - if (thisType) { - return getFlowTypeOfReference(node, thisType); - } - } - - if (isClassLike(container.parent)) { - const symbol = getSymbolOfNode(container.parent); - const type = hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!; - return getFlowTypeOfReference(node, type); - } - - if (isInJS) { - const type = getTypeForThisExpressionFromJSDoc(container); - if (type && type !== errorType) { - return getFlowTypeOfReference(node, type); - } - } - if (isSourceFile(container)) { - // look up in the source file's locals or exports - if (container.commonJsModuleIndicator) { - const fileSymbol = getSymbolOfNode(container); - return fileSymbol && getTypeOfSymbol(fileSymbol); - } - else if (includeGlobalThis) { - return getTypeOfSymbol(globalThisSymbol); - } - } - } - - function getExplicitThisType(node: Expression) { - const container = getThisContainer(node, /*includeArrowFunctions*/ false); - if (isFunctionLike(container)) { - const signature = getSignatureFromDeclaration(container); - if (signature.thisParameter) { - return getExplicitTypeOfSymbol(signature.thisParameter); - } - } - if (isClassLike(container.parent)) { - const symbol = getSymbolOfNode(container.parent); - return hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!; - } - } - - function getClassNameFromPrototypeMethod(container: Node) { - // Check if it's the RHS of a x.prototype.y = function [name]() { .... } - if (container.kind === SyntaxKind.FunctionExpression && - isBinaryExpression(container.parent) && - getAssignmentDeclarationKind(container.parent) === AssignmentDeclarationKind.PrototypeProperty) { - // Get the 'x' of 'x.prototype.y = container' - return ((container.parent // x.prototype.y = container - .left as PropertyAccessExpression) // x.prototype.y - .expression as PropertyAccessExpression) // x.prototype - .expression; // x - } - // x.prototype = { method() { } } - else if (container.kind === SyntaxKind.MethodDeclaration && - container.parent.kind === SyntaxKind.ObjectLiteralExpression && - isBinaryExpression(container.parent.parent) && - getAssignmentDeclarationKind(container.parent.parent) === AssignmentDeclarationKind.Prototype) { - return (container.parent.parent.left as PropertyAccessExpression).expression; - } - // x.prototype = { method: function() { } } - else if (container.kind === SyntaxKind.FunctionExpression && - container.parent.kind === SyntaxKind.PropertyAssignment && - container.parent.parent.kind === SyntaxKind.ObjectLiteralExpression && - isBinaryExpression(container.parent.parent.parent) && - getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.Prototype) { - return (container.parent.parent.parent.left as PropertyAccessExpression).expression; - } - // Object.defineProperty(x, "method", { value: function() { } }); - // Object.defineProperty(x, "method", { set: (x: () => void) => void }); - // Object.defineProperty(x, "method", { get: () => function() { }) }); - else if (container.kind === SyntaxKind.FunctionExpression && - isPropertyAssignment(container.parent) && - isIdentifier(container.parent.name) && - (container.parent.name.escapedText === "value" || container.parent.name.escapedText === "get" || container.parent.name.escapedText === "set") && - isObjectLiteralExpression(container.parent.parent) && - isCallExpression(container.parent.parent.parent) && - container.parent.parent.parent.arguments[2] === container.parent.parent && - getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) { - return (container.parent.parent.parent.arguments[0] as PropertyAccessExpression).expression; - } - // Object.defineProperty(x, "method", { value() { } }); - // Object.defineProperty(x, "method", { set(x: () => void) {} }); - // Object.defineProperty(x, "method", { get() { return () => {} } }); - else if (isMethodDeclaration(container) && - isIdentifier(container.name) && - (container.name.escapedText === "value" || container.name.escapedText === "get" || container.name.escapedText === "set") && - isObjectLiteralExpression(container.parent) && - isCallExpression(container.parent.parent) && - container.parent.parent.arguments[2] === container.parent && - getAssignmentDeclarationKind(container.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) { - return (container.parent.parent.arguments[0] as PropertyAccessExpression).expression; - } - } - - function getTypeForThisExpressionFromJSDoc(node: Node) { - const jsdocType = getJSDocType(node); - if (jsdocType && jsdocType.kind === SyntaxKind.JSDocFunctionType) { - const jsDocFunctionType = jsdocType; - if (jsDocFunctionType.parameters.length > 0 && - jsDocFunctionType.parameters[0].name && - (jsDocFunctionType.parameters[0].name as Identifier).escapedText === InternalSymbolName.This) { - return getTypeFromTypeNode(jsDocFunctionType.parameters[0].type!); - } - } - const thisTag = getJSDocThisTag(node); - if (thisTag && thisTag.typeExpression) { - return getTypeFromTypeNode(thisTag.typeExpression); - } - } - - function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean { - return !!findAncestor(node, n => isFunctionLikeDeclaration(n) ? "quit" : n.kind === SyntaxKind.Parameter && n.parent === constructorDecl); - } - - function checkSuperExpression(node: Node): Type { - const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (node.parent).expression === node; - - let container = getSuperContainer(node, /*stopOnFunctions*/ true); - let needToCaptureLexicalThis = false; - - // adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting - if (!isCallExpression) { - while (container && container.kind === SyntaxKind.ArrowFunction) { - container = getSuperContainer(container, /*stopOnFunctions*/ true); - needToCaptureLexicalThis = languageVersion < ScriptTarget.ES2015; - } - } - - const canUseSuperExpression = isLegalUsageOfSuperExpression(container); - let nodeCheckFlag: NodeCheckFlags = 0; - - if (!canUseSuperExpression) { - // issue more specific error if super is used in computed property name - // class A { foo() { return "1" }} - // class B { - // [super.foo()]() {} - // } - const current = findAncestor(node, n => n === container ? "quit" : n.kind === SyntaxKind.ComputedPropertyName); - if (current && current.kind === SyntaxKind.ComputedPropertyName) { - error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name); - } - else if (isCallExpression) { - error(node, Diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors); - } - else if (!container || !container.parent || !(isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression)) { - error(node, Diagnostics.super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions); - } - else { - error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class); - } - return errorType; - } - - if (!isCallExpression && container.kind === SyntaxKind.Constructor) { - checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class); - } - - if (hasModifier(container, ModifierFlags.Static) || isCallExpression) { - nodeCheckFlag = NodeCheckFlags.SuperStatic; - } - else { - nodeCheckFlag = NodeCheckFlags.SuperInstance; - } - - getNodeLinks(node).flags |= nodeCheckFlag; - - // Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference. - // This is due to the fact that we emit the body of an async function inside of a generator function. As generator - // functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper - // uses an arrow function, which is permitted to reference `super`. - // - // There are two primary ways we can access `super` from within an async method. The first is getting the value of a property - // or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value - // of a property or indexed access, either as part of an assignment expression or destructuring assignment. - // - // The simplest case is reading a value, in which case we will emit something like the following: - // - // // ts - // ... - // async asyncMethod() { - // let x = await super.asyncMethod(); - // return x; - // } - // ... - // - // // js - // ... - // asyncMethod() { - // const _super = Object.create(null, { - // asyncMethod: { get: () => super.asyncMethod }, - // }); - // return __awaiter(this, arguments, Promise, function *() { - // let x = yield _super.asyncMethod.call(this); - // return x; - // }); - // } - // ... - // - // The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases - // are legal in ES6, but also likely less frequent, we only emit setters if there is an assignment: - // - // // ts - // ... - // async asyncMethod(ar: Promise) { - // [super.a, super.b] = await ar; - // } - // ... - // - // // js - // ... - // asyncMethod(ar) { - // const _super = Object.create(null, { - // a: { get: () => super.a, set: (v) => super.a = v }, - // b: { get: () => super.b, set: (v) => super.b = v } - // }; - // return __awaiter(this, arguments, Promise, function *() { - // [_super.a, _super.b] = yield ar; - // }); - // } - // ... - // - // Creating an object that has getter and setters instead of just an accessor function is required for destructuring assignments - // as a call expression cannot be used as the target of a destructuring assignment while a property access can. - // - // For element access expressions (`super[x]`), we emit a generic helper that forwards the element access in both situations. - if (container.kind === SyntaxKind.MethodDeclaration && hasModifier(container, ModifierFlags.Async)) { - if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) { - getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; - } - else { - getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper; - } - } - - if (needToCaptureLexicalThis) { - // call expressions are allowed only in constructors so they should always capture correct 'this' - // super property access expressions can also appear in arrow functions - - // in this case they should also use correct lexical this - captureLexicalThis(node.parent, container); - } - - if (container.parent.kind === SyntaxKind.ObjectLiteralExpression) { - if (languageVersion < ScriptTarget.ES2015) { - error(node, Diagnostics.super_is_only_allowed_in_members_of_object_literal_expressions_when_option_target_is_ES2015_or_higher); - return errorType; - } - else { - // for object literal assume that type of 'super' is 'any' - return anyType; - } - } - - // at this point the only legal case for parent is ClassLikeDeclaration - const classLikeDeclaration = container.parent; - if (!getClassExtendsHeritageElement(classLikeDeclaration)) { - error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class); - return errorType; - } - - const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(classLikeDeclaration)); - const baseClassType = classType && getBaseTypes(classType)[0]; - if (!baseClassType) { - return errorType; - } - - if (container.kind === SyntaxKind.Constructor && isInConstructorArgumentInitializer(node, container)) { - // issue custom error message for super property access in constructor arguments (to be aligned with old compiler) - error(node, Diagnostics.super_cannot_be_referenced_in_constructor_arguments); - return errorType; - } - - return nodeCheckFlag === NodeCheckFlags.SuperStatic - ? getBaseConstructorTypeOfClass(classType) - : getTypeWithThisArgument(baseClassType, classType.thisType); - - function isLegalUsageOfSuperExpression(container: Node): boolean { - if (!container) { - return false; - } - - if (isCallExpression) { - // TS 1.0 SPEC (April 2014): 4.8.1 - // Super calls are only permitted in constructors of derived classes - return container.kind === SyntaxKind.Constructor; - } - else { - // TS 1.0 SPEC (April 2014) - // 'super' property access is allowed - // - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance - // - In a static member function or static member accessor - - // topmost container must be something that is directly nested in the class declaration\object literal expression - if (isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression) { - if (hasModifier(container, ModifierFlags.Static)) { - return container.kind === SyntaxKind.MethodDeclaration || - container.kind === SyntaxKind.MethodSignature || - container.kind === SyntaxKind.GetAccessor || - container.kind === SyntaxKind.SetAccessor; - } - else { - return container.kind === SyntaxKind.MethodDeclaration || - container.kind === SyntaxKind.MethodSignature || - container.kind === SyntaxKind.GetAccessor || - container.kind === SyntaxKind.SetAccessor || - container.kind === SyntaxKind.PropertyDeclaration || - container.kind === SyntaxKind.PropertySignature || - container.kind === SyntaxKind.Constructor; - } - } - } - - return false; - } - } - - function getContainingObjectLiteral(func: SignatureDeclaration): ObjectLiteralExpression | undefined { - return (func.kind === SyntaxKind.MethodDeclaration || - func.kind === SyntaxKind.GetAccessor || - func.kind === SyntaxKind.SetAccessor) && func.parent.kind === SyntaxKind.ObjectLiteralExpression ? func.parent : - func.kind === SyntaxKind.FunctionExpression && func.parent.kind === SyntaxKind.PropertyAssignment ? func.parent.parent : - undefined; - } - - function getThisTypeArgument(type: Type): Type | undefined { - return getObjectFlags(type) & ObjectFlags.Reference && (type).target === globalThisType ? getTypeArguments(type)[0] : undefined; - } - - function getThisTypeFromContextualType(type: Type): Type | undefined { - return mapType(type, t => { - return t.flags & TypeFlags.Intersection ? forEach((t).types, getThisTypeArgument) : getThisTypeArgument(t); - }); - } - - function getContextualThisParameterType(func: SignatureDeclaration): Type | undefined { - if (func.kind === SyntaxKind.ArrowFunction) { - return undefined; - } - if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - const contextualSignature = getContextualSignature(func); - if (contextualSignature) { - const thisParameter = contextualSignature.thisParameter; - if (thisParameter) { - return getTypeOfSymbol(thisParameter); - } - } - } - const inJs = isInJSFile(func); - if (noImplicitThis || inJs) { - const containingLiteral = getContainingObjectLiteral(func); - if (containingLiteral) { - // We have an object literal method. Check if the containing object literal has a contextual type - // that includes a ThisType. If so, T is the contextual type for 'this'. We continue looking in - // any directly enclosing object literals. - const contextualType = getApparentTypeOfContextualType(containingLiteral); - let literal = containingLiteral; - let type = contextualType; - while (type) { - const thisType = getThisTypeFromContextualType(type); - if (thisType) { - return instantiateType(thisType, getMapperFromContext(getInferenceContext(containingLiteral))); - } - if (literal.parent.kind !== SyntaxKind.PropertyAssignment) { - break; - } - literal = literal.parent.parent; - type = getApparentTypeOfContextualType(literal); - } - // There was no contextual ThisType for the containing object literal, so the contextual type - // for 'this' is the non-null form of the contextual type for the containing object literal or - // the type of the object literal itself. - return getWidenedType(contextualType ? getNonNullableType(contextualType) : checkExpressionCached(containingLiteral)); - } - // In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the - // contextual type for 'this' is 'obj'. - const parent = func.parent; - if (parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { - const target = (parent).left; - if (isAccessExpression(target)) { - const { expression } = target; - // Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }` - if (inJs && isIdentifier(expression)) { - const sourceFile = getSourceFileOfNode(parent); - if (sourceFile.commonJsModuleIndicator && getResolvedSymbol(expression) === sourceFile.symbol) { - return undefined; - } - } - - return getWidenedType(checkExpressionCached(expression)); - } - } - } - return undefined; - } - - // Return contextual type of parameter or undefined if no contextual type is available - function getContextuallyTypedParameterType(parameter: ParameterDeclaration, forCache: boolean): Type | undefined { - const func = parameter.parent; - if (!isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - return undefined; - } - const iife = getImmediatelyInvokedFunctionExpression(func); - if (iife && iife.arguments) { - const args = getEffectiveCallArguments(iife); - const indexOfParameter = func.parameters.indexOf(parameter); - if (parameter.dotDotDotToken) { - return getSpreadArgumentType(args, indexOfParameter, args.length, anyType, /*context*/ undefined); - } - const links = getNodeLinks(iife); - const cached = links.resolvedSignature; - links.resolvedSignature = anySignature; - const type = indexOfParameter < args.length ? - getWidenedLiteralType(checkExpression(args[indexOfParameter])) : - parameter.initializer ? undefined : undefinedWideningType; - links.resolvedSignature = cached; - return type; - } - let contextualSignature = getContextualSignature(func); - if (contextualSignature) { - if (forCache) { - // Calling the below guarantees the types are primed and assigned in the same way - // as when the parameter is reached via `checkFunctionExpressionOrObjectLiteralMethod`. - // This should prevent any uninstantiated inference variables in the contextual signature - // from leaking, and should lock in cached parameter types via `assignContextualParameterTypes` - // which we will then immediately use the results of below. - contextuallyCheckFunctionExpressionOrObjectLiteralMethod(func); - const type = getTypeOfSymbol(getMergedSymbol(func.symbol)); - if (isTypeAny(type)) { - return type; - } - contextualSignature = getSignaturesOfType(type, SignatureKind.Call)[0]; - } - const index = func.parameters.indexOf(parameter) - (getThisParameter(func) ? 1 : 0); - return parameter.dotDotDotToken && lastOrUndefined(func.parameters) === parameter ? - getRestTypeAtPosition(contextualSignature, index) : - tryGetTypeAtPosition(contextualSignature, index); - } - } - - function getContextualTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type | undefined { - const typeNode = getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - switch (declaration.kind) { - case SyntaxKind.Parameter: - return getContextuallyTypedParameterType(declaration, /*forCache*/ false); - case SyntaxKind.BindingElement: - return getContextualTypeForBindingElement(declaration); - // By default, do nothing and return undefined - only parameters and binding elements have context implied by a parent - } - } - - function getContextualTypeForBindingElement(declaration: BindingElement): Type | undefined { - const parentDeclaration = declaration.parent.parent; - const name = declaration.propertyName || declaration.name; - const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration); - if (parentType && !isBindingPattern(name) && !isComputedNonLiteralName(name)) { - const nameType = getLiteralTypeFromPropertyName(name); - if (isTypeUsableAsPropertyName(nameType)) { - const text = getPropertyNameFromType(nameType); - return getTypeOfPropertyOfType(parentType, text); - } - } - } - - // In a variable, parameter or property declaration with a type annotation, - // the contextual type of an initializer expression is the type of the variable, parameter or property. - // Otherwise, in a parameter declaration of a contextually typed function expression, - // the contextual type of an initializer expression is the contextual type of the parameter. - // Otherwise, in a variable or parameter declaration with a binding pattern name, - // the contextual type of an initializer expression is the type implied by the binding pattern. - // Otherwise, in a binding pattern inside a variable or parameter declaration, - // the contextual type of an initializer expression is the type annotation of the containing declaration, if present. - function getContextualTypeForInitializerExpression(node: Expression): Type | undefined { - const declaration = node.parent; - if (hasInitializer(declaration) && node === declaration.initializer) { - const result = getContextualTypeForVariableLikeDeclaration(declaration); - if (result) { - return result; - } - if (isBindingPattern(declaration.name)) { // This is less a contextual type and more an implied shape - in some cases, this may be undesirable - return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false); - } - } - return undefined; - } - - function getContextualTypeForReturnExpression(node: Expression): Type | undefined { - const func = getContainingFunction(node); - if (func) { - const functionFlags = getFunctionFlags(func); - if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function - return undefined; - } - - const contextualReturnType = getContextualReturnType(func); - if (contextualReturnType) { - if (functionFlags & FunctionFlags.Async) { // Async function - const contextualAwaitedType = getAwaitedTypeOfPromise(contextualReturnType); - return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]); - } - return contextualReturnType; // Regular function - } - } - return undefined; - } - - function getContextualTypeForAwaitOperand(node: AwaitExpression): Type | undefined { - const contextualType = getContextualType(node); - if (contextualType) { - const contextualAwaitedType = getAwaitedType(contextualType); - return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]); - } - return undefined; - } - - function getContextualTypeForYieldOperand(node: YieldExpression): Type | undefined { - const func = getContainingFunction(node); - if (func) { - const functionFlags = getFunctionFlags(func); - const contextualReturnType = getContextualReturnType(func); - if (contextualReturnType) { - return node.asteriskToken - ? contextualReturnType - : getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0); - } - } - - return undefined; - } - - function isInParameterInitializerBeforeContainingFunction(node: Node) { - let inBindingInitializer = false; - while (node.parent && !isFunctionLike(node.parent)) { - if (isParameter(node.parent) && (inBindingInitializer || node.parent.initializer === node)) { - return true; - } - if (isBindingElement(node.parent) && node.parent.initializer === node) { - inBindingInitializer = true; - } - - node = node.parent; - } - - return false; - } - - function getContextualIterationType(kind: IterationTypeKind, functionDecl: SignatureDeclaration): Type | undefined { - const isAsync = !!(getFunctionFlags(functionDecl) & FunctionFlags.Async); - const contextualReturnType = getContextualReturnType(functionDecl); - if (contextualReturnType) { - return getIterationTypeOfGeneratorFunctionReturnType(kind, contextualReturnType, isAsync) - || undefined; - } - - return undefined; - } - - function getContextualReturnType(functionDecl: SignatureDeclaration): Type | undefined { - // If the containing function has a return type annotation, is a constructor, or is a get accessor whose - // corresponding set accessor has a type annotation, return statements in the function are contextually typed - const returnType = getReturnTypeFromAnnotation(functionDecl); - if (returnType) { - return returnType; - } - // Otherwise, if the containing function is contextually typed by a function type with exactly one call signature - // and that call signature is non-generic, return statements are contextually typed by the return type of the signature - const signature = getContextualSignatureForFunctionLikeDeclaration(functionDecl); - if (signature && !isResolvingReturnTypeOfSignature(signature)) { - return getReturnTypeOfSignature(signature); - } - return undefined; - } - - // In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter. - function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression, contextFlags?: ContextFlags): Type | undefined { - const args = getEffectiveCallArguments(callTarget); - const argIndex = args.indexOf(arg); // -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression - return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex, contextFlags); - } - - function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type { - // If we're already in the process of resolving the given signature, don't resolve again as - // that could cause infinite recursion. Instead, return anySignature. - let signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget); - if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) { - signature = getBaseSignature(signature.target); - } - - if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) { - return getEffectiveFirstArgumentForJsxSignature(signature, callTarget); - } - return getTypeAtPosition(signature, argIndex); - } - - function getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) { - if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) { - return getContextualTypeForArgument(template.parent, substitutionExpression); - } - - return undefined; - } - - function getContextualTypeForBinaryOperand(node: Expression, contextFlags?: ContextFlags): Type | undefined { - const binaryExpression = node.parent; - const { left, operatorToken, right } = binaryExpression; - switch (operatorToken.kind) { - case SyntaxKind.EqualsToken: - if (node !== right) { - return undefined; - } - const contextSensitive = getIsContextSensitiveAssignmentOrContextType(binaryExpression); - if (!contextSensitive) { - return undefined; - } - return contextSensitive === true ? getTypeOfExpression(left) : contextSensitive; - case SyntaxKind.BarBarToken: - case SyntaxKind.QuestionQuestionToken: - // When an || expression has a contextual type, the operands are contextually typed by that type, except - // when that type originates in a binding pattern, the right operand is contextually typed by the type of - // the left operand. When an || expression has no contextual type, the right operand is contextually typed - // by the type of the left operand, except for the special case of Javascript declarations of the form - // `namespace.prop = namespace.prop || {}`. - const type = getContextualType(binaryExpression, contextFlags); - return node === right && (type && type.pattern || !type && !isDefaultedExpandoInitializer(binaryExpression)) ? - getTypeOfExpression(left) : type; - case SyntaxKind.AmpersandAmpersandToken: - case SyntaxKind.CommaToken: - return node === right ? getContextualType(binaryExpression, contextFlags) : undefined; - default: - return undefined; - } - } - - // In an assignment expression, the right operand is contextually typed by the type of the left operand. - // Don't do this for assignment declarations unless there is a type tag on the assignment, to avoid circularity from checking the right operand. - function getIsContextSensitiveAssignmentOrContextType(binaryExpression: BinaryExpression): boolean | Type { - const kind = getAssignmentDeclarationKind(binaryExpression); - switch (kind) { - case AssignmentDeclarationKind.None: - return true; - case AssignmentDeclarationKind.Property: - case AssignmentDeclarationKind.ExportsProperty: - case AssignmentDeclarationKind.Prototype: - case AssignmentDeclarationKind.PrototypeProperty: - // If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration. - // See `bindStaticPropertyAssignment` in `binder.ts`. - if (!binaryExpression.left.symbol) { - return true; - } - else { - const decl = binaryExpression.left.symbol.valueDeclaration; - if (!decl) { - return false; - } - const lhs = cast(binaryExpression.left, isAccessExpression); - const overallAnnotation = getEffectiveTypeAnnotationNode(decl); - if (overallAnnotation) { - return getTypeFromTypeNode(overallAnnotation); - } - else if (isIdentifier(lhs.expression)) { - const id = lhs.expression; - const parentSymbol = resolveName(id, id.escapedText, SymbolFlags.Value, undefined, id.escapedText, /*isUse*/ true); - if (parentSymbol) { - const annotated = getEffectiveTypeAnnotationNode(parentSymbol.valueDeclaration); - if (annotated) { - const nameStr = getElementOrPropertyAccessName(lhs); - if (nameStr !== undefined) { - const type = getTypeOfPropertyOfContextualType(getTypeFromTypeNode(annotated), nameStr); - return type || false; - } - } - return false; - } - } - return !isInJSFile(decl); - } - case AssignmentDeclarationKind.ModuleExports: - case AssignmentDeclarationKind.ThisProperty: - if (!binaryExpression.symbol) return true; - if (binaryExpression.symbol.valueDeclaration) { - const annotated = getEffectiveTypeAnnotationNode(binaryExpression.symbol.valueDeclaration); - if (annotated) { - const type = getTypeFromTypeNode(annotated); - if (type) { - return type; - } - } - } - if (kind === AssignmentDeclarationKind.ModuleExports) return false; - const thisAccess = cast(binaryExpression.left, isAccessExpression); - if (!isObjectLiteralMethod(getThisContainer(thisAccess.expression, /*includeArrowFunctions*/ false))) { - return false; - } - const thisType = checkThisExpression(thisAccess.expression); - const nameStr = getElementOrPropertyAccessName(thisAccess); - return nameStr !== undefined && thisType && getTypeOfPropertyOfContextualType(thisType, nameStr) || false; - case AssignmentDeclarationKind.ObjectDefinePropertyValue: - case AssignmentDeclarationKind.ObjectDefinePropertyExports: - case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: - return Debug.fail("Does not apply"); - default: - return Debug.assertNever(kind); - } - } - - function getTypeOfPropertyOfContextualType(type: Type, name: __String) { - return mapType(type, t => { - if (isGenericMappedType(t)) { - const constraint = getConstraintTypeFromMappedType(t); - const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint; - const propertyNameType = getLiteralType(unescapeLeadingUnderscores(name)); - if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) { - return substituteIndexedMappedType(t, propertyNameType); - } - } - else if (t.flags & TypeFlags.StructuredType) { - const prop = getPropertyOfType(t, name); - if (prop) { - return getTypeOfSymbol(prop); - } - if (isTupleType(t)) { - const restType = getRestTypeOfTupleType(t); - if (restType && isNumericLiteralName(name) && +name >= 0) { - return restType; - } - } - return isNumericLiteralName(name) && getIndexTypeOfContextualType(t, IndexKind.Number) || - getIndexTypeOfContextualType(t, IndexKind.String); - } - return undefined; - }, /*noReductions*/ true); - } - - function getIndexTypeOfContextualType(type: Type, kind: IndexKind) { - return mapType(type, t => getIndexTypeOfStructuredType(t, kind), /*noReductions*/ true); - } - - // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of - // the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one - // exists. Otherwise, it is the type of the string index signature in T, if one exists. - function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration, contextFlags?: ContextFlags): Type | undefined { - Debug.assert(isObjectLiteralMethod(node)); - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return undefined; - } - return getContextualTypeForObjectLiteralElement(node, contextFlags); - } - - function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike, contextFlags?: ContextFlags) { - const objectLiteral = element.parent; - const type = getApparentTypeOfContextualType(objectLiteral, contextFlags); - if (type) { - if (!hasNonBindableDynamicName(element)) { - // For a (non-symbol) computed property, there is no reason to look up the name - // in the type. It will just be "__computed", which does not appear in any - // SymbolTable. - const symbolName = getSymbolOfNode(element).escapedName; - const propertyType = getTypeOfPropertyOfContextualType(type, symbolName); - if (propertyType) { - return propertyType; - } - } - return isNumericName(element.name!) && getIndexTypeOfContextualType(type, IndexKind.Number) || - getIndexTypeOfContextualType(type, IndexKind.String); - } - return undefined; - } - - // In an array literal contextually typed by a type T, the contextual type of an element expression at index N is - // the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature, - // it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated - // type of T. - function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined { - return arrayContextualType && ( - getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) - || getIteratedTypeOrElementType(IterationUse.Element, arrayContextualType, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false)); - } - - // In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. - function getContextualTypeForConditionalOperand(node: Expression, contextFlags?: ContextFlags): Type | undefined { - const conditional = node.parent; - return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional, contextFlags) : undefined; - } - - function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) { - const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName); - // JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty) - const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - if (!(attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "")) { - return undefined; - } - const realChildren = getSemanticJsxChildren(node.children); - const childIndex = realChildren.indexOf(child); - const childFieldType = getTypeOfPropertyOfContextualType(attributesType, jsxChildrenPropertyName); - return childFieldType && (realChildren.length === 1 ? childFieldType : mapType(childFieldType, t => { - if (isArrayLikeType(t)) { - return getIndexedAccessType(t, getLiteralType(childIndex)); - } - else { - return t; - } - }, /*noReductions*/ true)); - } - - function getContextualTypeForJsxExpression(node: JsxExpression): Type | undefined { - const exprParent = node.parent; - return isJsxAttributeLike(exprParent) - ? getContextualType(node) - : isJsxElement(exprParent) - ? getContextualTypeForChildJsxExpression(exprParent, node) - : undefined; - } - - function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined { - // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type - // which is a type of the parameter of the signature we are trying out. - // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName - if (isJsxAttribute(attribute)) { - const attributesType = getApparentTypeOfContextualType(attribute.parent); - if (!attributesType || isTypeAny(attributesType)) { - return undefined; - } - return getTypeOfPropertyOfContextualType(attributesType, attribute.name.escapedText); - } - else { - return getContextualType(attribute.parent); - } - } - - // Return true if the given expression is possibly a discriminant value. We limit the kinds of - // expressions we check to those that don't depend on their contextual type in order not to cause - // recursive (and possibly infinite) invocations of getContextualType. - function isPossiblyDiscriminantValue(node: Expression): boolean { - switch (node.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.Identifier: - case SyntaxKind.UndefinedKeyword: - return true; - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ParenthesizedExpression: - return isPossiblyDiscriminantValue((node).expression); - case SyntaxKind.JsxExpression: - return !(node as JsxExpression).expression || isPossiblyDiscriminantValue((node as JsxExpression).expression!); - } - return false; - } - - function discriminateContextualTypeByObjectMembers(node: ObjectLiteralExpression, contextualType: UnionType) { - return discriminateTypeByDiscriminableItems(contextualType, - map( - filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.PropertyAssignment && isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName)), - prop => ([() => checkExpression((prop as PropertyAssignment).initializer), prop.symbol.escapedName] as [() => Type, __String]) - ), - isTypeAssignableTo, - contextualType - ); - } - - function discriminateContextualTypeByJSXAttributes(node: JsxAttributes, contextualType: UnionType) { - return discriminateTypeByDiscriminableItems(contextualType, - map( - filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))), - prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => checkExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String]) - ), - isTypeAssignableTo, - contextualType - ); - } - - // Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily - // be "pushed" onto a node using the contextualType property. - function getApparentTypeOfContextualType(node: Expression | MethodDeclaration, contextFlags?: ContextFlags): Type | undefined { - const contextualType = isObjectLiteralMethod(node) ? - getContextualTypeForObjectLiteralMethod(node, contextFlags) : - getContextualType(node, contextFlags); - const instantiatedType = instantiateContextualType(contextualType, node, contextFlags); - if (instantiatedType && !(contextFlags && contextFlags & ContextFlags.NoConstraints && instantiatedType.flags & TypeFlags.TypeVariable)) { - const apparentType = mapType(instantiatedType, getApparentType, /*noReductions*/ true); - if (apparentType.flags & TypeFlags.Union) { - if (isObjectLiteralExpression(node)) { - return discriminateContextualTypeByObjectMembers(node, apparentType as UnionType); - } - else if (isJsxAttributes(node)) { - return discriminateContextualTypeByJSXAttributes(node, apparentType as UnionType); - } - } - return apparentType; - } - } - - // If the given contextual type contains instantiable types and if a mapper representing - // return type inferences is available, instantiate those types using that mapper. - function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags?: ContextFlags): Type | undefined { - if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) { - const inferenceContext = getInferenceContext(node); - // If no inferences have been made, nothing is gained from instantiating as type parameters - // would just be replaced with their defaults similar to the apparent type. - if (inferenceContext && some(inferenceContext.inferences, hasInferenceCandidates)) { - // For contextual signatures we incorporate all inferences made so far, e.g. from return - // types as well as arguments to the left in a function call. - if (contextFlags && contextFlags & ContextFlags.Signature) { - return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); - } - // For other purposes (e.g. determining whether to produce literal types) we only - // incorporate inferences made from the return type in a function call. - if (inferenceContext.returnMapper) { - return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper); - } - } - } - return contextualType; - } - - // This function is similar to instantiateType, except that (a) it only instantiates types that - // are classified as instantiable (i.e. it doesn't instantiate object types), and (b) it performs - // no reductions on instantiated union types. - function instantiateInstantiableTypes(type: Type, mapper: TypeMapper): Type { - if (type.flags & TypeFlags.Instantiable) { - return instantiateType(type, mapper); - } - if (type.flags & TypeFlags.Union) { - return getUnionType(map((type).types, t => instantiateInstantiableTypes(t, mapper)), UnionReduction.None); - } - if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map((type).types, t => instantiateInstantiableTypes(t, mapper))); - } - return type; - } - - /** - * Whoa! Do you really want to use this function? - * - * Unless you're trying to get the *non-apparent* type for a - * value-literal type or you're authoring relevant portions of this algorithm, - * you probably meant to use 'getApparentTypeOfContextualType'. - * Otherwise this may not be very useful. - * - * In cases where you *are* working on this function, you should understand - * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'. - * - * - Use 'getContextualType' when you are simply going to propagate the result to the expression. - * - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type. - * - * @param node the expression whose contextual type will be returned. - * @returns the contextual type of an expression. - */ - function getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined { - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return undefined; - } - if (node.contextualType) { - return node.contextualType; - } - const { parent } = node; - switch (parent.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.BindingElement: - return getContextualTypeForInitializerExpression(node); - case SyntaxKind.ArrowFunction: - case SyntaxKind.ReturnStatement: - return getContextualTypeForReturnExpression(node); - case SyntaxKind.YieldExpression: - return getContextualTypeForYieldOperand(parent); - case SyntaxKind.AwaitExpression: - return getContextualTypeForAwaitOperand(parent); - case SyntaxKind.CallExpression: - if ((parent).expression.kind === SyntaxKind.ImportKeyword) { - return stringType; - } - /* falls through */ - case SyntaxKind.NewExpression: - return getContextualTypeForArgument(parent, node, contextFlags); - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return isConstTypeReference((parent).type) ? undefined : getTypeFromTypeNode((parent).type); - case SyntaxKind.BinaryExpression: - return getContextualTypeForBinaryOperand(node, contextFlags); - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - return getContextualTypeForObjectLiteralElement(parent, contextFlags); - case SyntaxKind.SpreadAssignment: - return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression, contextFlags); - case SyntaxKind.ArrayLiteralExpression: { - const arrayLiteral = parent; - const type = getApparentTypeOfContextualType(arrayLiteral, contextFlags); - return getContextualTypeForElementExpression(type, indexOfNode(arrayLiteral.elements, node)); - } - case SyntaxKind.ConditionalExpression: - return getContextualTypeForConditionalOperand(node, contextFlags); - case SyntaxKind.TemplateSpan: - Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression); - return getContextualTypeForSubstitutionExpression(parent.parent, node); - case SyntaxKind.ParenthesizedExpression: { - // Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast. - const tag = isInJSFile(parent) ? getJSDocTypeTag(parent) : undefined; - return tag ? getTypeFromTypeNode(tag.typeExpression.type) : getContextualType(parent, contextFlags); - } - case SyntaxKind.JsxExpression: - return getContextualTypeForJsxExpression(parent); - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxSpreadAttribute: - return getContextualTypeForJsxAttribute(parent); - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSelfClosingElement: - return getContextualJsxElementAttributesType(parent, contextFlags); - } - return undefined; - } - - function getInferenceContext(node: Node) { - const ancestor = findAncestor(node, n => !!n.inferenceContext); - return ancestor && ancestor.inferenceContext!; - } - - function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags?: ContextFlags) { - if (isJsxOpeningElement(node) && node.parent.contextualType && contextFlags !== ContextFlags.BaseConstraint) { - // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit - // _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type - // (as below) instead! - return node.parent.contextualType; - } - return getContextualTypeForArgumentAtIndex(node, 0, contextFlags); - } - - function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) { - return getJsxReferenceKind(node) !== JsxReferenceKind.Component - ? getJsxPropsTypeFromCallSignature(signature, node) - : getJsxPropsTypeFromClassType(signature, node); - } - - function getJsxPropsTypeFromCallSignature(sig: Signature, context: JsxOpeningLikeElement) { - let propsType = getTypeOfFirstParameterOfSignatureWithFallback(sig, unknownType); - propsType = getJsxManagedAttributesFromLocatedAttributes(context, getJsxNamespaceAt(context), propsType); - const intrinsicAttribs = getJsxType(JsxNames.IntrinsicAttributes, context); - if (intrinsicAttribs !== errorType) { - propsType = intersectTypes(intrinsicAttribs, propsType); - } - return propsType; - } - - function getJsxPropsTypeForSignatureFromMember(sig: Signature, forcedLookupLocation: __String) { - if (sig.unionSignatures) { - // JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input - // instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature, - // get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur - // for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input. - // The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane. - const results: Type[] = []; - for (const signature of sig.unionSignatures) { - const instance = getReturnTypeOfSignature(signature); - if (isTypeAny(instance)) { - return instance; - } - const propType = getTypeOfPropertyOfType(instance, forcedLookupLocation); - if (!propType) { - return; - } - results.push(propType); - } - return getIntersectionType(results); - } - const instanceType = getReturnTypeOfSignature(sig); - return isTypeAny(instanceType) ? instanceType : getTypeOfPropertyOfType(instanceType, forcedLookupLocation); - } - - function getStaticTypeOfReferencedJsxConstructor(context: JsxOpeningLikeElement) { - if (isJsxIntrinsicIdentifier(context.tagName)) { - const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context); - const fakeSignature = createSignatureForJSXIntrinsic(context, result); - return getOrCreateTypeFromSignature(fakeSignature); - } - const tagType = checkExpressionCached(context.tagName); - if (tagType.flags & TypeFlags.StringLiteral) { - const result = getIntrinsicAttributesTypeFromStringLiteralType(tagType as StringLiteralType, context); - if (!result) { - return errorType; - } - const fakeSignature = createSignatureForJSXIntrinsic(context, result); - return getOrCreateTypeFromSignature(fakeSignature); - } - return tagType; - } - - function getJsxManagedAttributesFromLocatedAttributes(context: JsxOpeningLikeElement, ns: Symbol, attributesType: Type) { - const managedSym = getJsxLibraryManagedAttributes(ns); - if (managedSym) { - const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); - const ctorType = getStaticTypeOfReferencedJsxConstructor(context); - if (length((declaredManagedType as GenericType).typeParameters) >= 2) { - const args = fillMissingTypeArguments([ctorType, attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context)); - return createTypeReference((declaredManagedType as GenericType), args); - } - else if (length(declaredManagedType.aliasTypeArguments) >= 2) { - const args = fillMissingTypeArguments([ctorType, attributesType], declaredManagedType.aliasTypeArguments, 2, isInJSFile(context)); - return getTypeAliasInstantiation(declaredManagedType.aliasSymbol!, args); - } - } - return attributesType; - } - - function getJsxPropsTypeFromClassType(sig: Signature, context: JsxOpeningLikeElement) { - const ns = getJsxNamespaceAt(context); - const forcedLookupLocation = getJsxElementPropertiesName(ns); - let attributesType = forcedLookupLocation === undefined - // If there is no type ElementAttributesProperty, return the type of the first parameter of the signature, which should be the props type - ? getTypeOfFirstParameterOfSignatureWithFallback(sig, unknownType) - : forcedLookupLocation === "" - // If there is no e.g. 'props' member in ElementAttributesProperty, use the element class type instead - ? getReturnTypeOfSignature(sig) - // Otherwise get the type of the property on the signature return type - : getJsxPropsTypeForSignatureFromMember(sig, forcedLookupLocation); - - if (!attributesType) { - // There is no property named 'props' on this instance type - if (!!forcedLookupLocation && !!length(context.attributes.properties)) { - error(context, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(forcedLookupLocation)); - } - return unknownType; - } - - attributesType = getJsxManagedAttributesFromLocatedAttributes(context, ns, attributesType); - - if (isTypeAny(attributesType)) { - // Props is of type 'any' or unknown - return attributesType; - } - else { - // Normal case -- add in IntrinsicClassElements and IntrinsicElements - let apparentAttributesType = attributesType; - const intrinsicClassAttribs = getJsxType(JsxNames.IntrinsicClassAttributes, context); - if (intrinsicClassAttribs !== errorType) { - const typeParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol); - const hostClassType = getReturnTypeOfSignature(sig); - apparentAttributesType = intersectTypes( - typeParams - ? createTypeReference(intrinsicClassAttribs, fillMissingTypeArguments([hostClassType], typeParams, getMinTypeArgumentCount(typeParams), isInJSFile(context))) - : intrinsicClassAttribs, - apparentAttributesType - ); - } - - const intrinsicAttribs = getJsxType(JsxNames.IntrinsicAttributes, context); - if (intrinsicAttribs !== errorType) { - apparentAttributesType = intersectTypes(intrinsicAttribs, apparentAttributesType); - } - - return apparentAttributesType; - } - } - - // If the given type is an object or union type with a single signature, and if that signature has at - // least as many parameters as the given function, return the signature. Otherwise return undefined. - function getContextualCallSignature(type: Type, node: SignatureDeclaration): Signature | undefined { - const signatures = getSignaturesOfType(type, SignatureKind.Call); - if (signatures.length === 1) { - const signature = signatures[0]; - if (!isAritySmaller(signature, node)) { - return signature; - } - } - } - - /** If the contextual signature has fewer parameters than the function expression, do not use it */ - function isAritySmaller(signature: Signature, target: SignatureDeclaration) { - let targetParameterCount = 0; - for (; targetParameterCount < target.parameters.length; targetParameterCount++) { - const param = target.parameters[targetParameterCount]; - if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) { - break; - } - } - if (target.parameters.length && parameterIsThisKeyword(target.parameters[0])) { - targetParameterCount--; - } - return !hasEffectiveRestParameter(signature) && getParameterCount(signature) < targetParameterCount; - } - - function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction { - return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction; - } - - function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature | undefined { - // Only function expressions, arrow functions, and object literal methods are contextually typed. - return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node) - ? getContextualSignature(node) - : undefined; - } - - // Return the contextual signature for a given expression node. A contextual type provides a - // contextual signature if it has a single call signature and if that call signature is non-generic. - // If the contextual type is a union type, get the signature from each type possible and if they are - // all identical ignoring their return type, the result is same signature but with return type as - // union type of return types from these signatures - function getContextualSignature(node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature | undefined { - Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - const typeTagSignature = getSignatureOfTypeTag(node); - if (typeTagSignature) { - return typeTagSignature; - } - const type = getApparentTypeOfContextualType(node, ContextFlags.Signature); - if (!type) { - return undefined; - } - if (!(type.flags & TypeFlags.Union)) { - return getContextualCallSignature(type, node); - } - let signatureList: Signature[] | undefined; - const types = (type).types; - for (const current of types) { - const signature = getContextualCallSignature(current, node); - if (signature) { - if (!signatureList) { - // This signature will contribute to contextual union signature - signatureList = [signature]; - } - else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) { - // Signatures aren't identical, do not use - return undefined; - } - else { - // Use this signature for contextual union signature - signatureList.push(signature); - } - } - } - // Result is union of signatures collected (return type is union of return types of this signature set) - if (signatureList) { - return signatureList.length === 1 ? signatureList[0] : createUnionSignature(signatureList[0], signatureList); - } - } - - function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type { - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers(node, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArrays); - } - - const arrayOrIterableType = checkExpression(node.expression, checkMode); - return checkIteratedTypeOrElementType(IterationUse.Spread, arrayOrIterableType, undefinedType, node.expression); - } - - function hasDefaultValue(node: BindingElement | Expression): boolean { - return (node.kind === SyntaxKind.BindingElement && !!(node).initializer) || - (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.EqualsToken); - } - - function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, forceTuple: boolean | undefined): Type { - const elements = node.elements; - const elementCount = elements.length; - let hasNonEndingSpreadElement = false; - const elementTypes: Type[] = []; - const inDestructuringPattern = isAssignmentTarget(node); - const contextualType = getApparentTypeOfContextualType(node); - const inConstContext = isConstContext(node); - for (let index = 0; index < elementCount; index++) { - const e = elements[index]; - if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElement) { - // Given the following situation: - // var c: {}; - // [...c] = ["", 0]; - // - // c is represented in the tree as a spread element in an array literal. - // But c really functions as a rest element, and its purpose is to provide - // a contextual type for the right hand side of the assignment. Therefore, - // instead of calling checkExpression on "...c", which will give an error - // if c is not iterable/array-like, we need to act as if we are trying to - // get the contextual element type from it. So we do something similar to - // getContextualTypeForElementExpression, which will crucially not error - // if there is no index type / iterated type. - const restArrayType = checkExpression((e).expression, checkMode, forceTuple); - const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) || - getIteratedTypeOrElementType(IterationUse.Destructuring, restArrayType, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false); - if (restElementType) { - elementTypes.push(restElementType); - } - } - else { - const elementContextualType = getContextualTypeForElementExpression(contextualType, index); - const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple); - elementTypes.push(type); - } - if (index < elementCount - 1 && e.kind === SyntaxKind.SpreadElement) { - hasNonEndingSpreadElement = true; - } - } - if (!hasNonEndingSpreadElement) { - const hasRestElement = elementCount > 0 && elements[elementCount - 1].kind === SyntaxKind.SpreadElement; - const minLength = elementCount - (hasRestElement ? 1 : 0); - // If array literal is actually a destructuring pattern, mark it as an implied type. We do this such - // that we get the same behavior for "var [x, y] = []" and "[x, y] = []". - let tupleResult; - if (inDestructuringPattern && minLength > 0) { - const type = cloneTypeReference(createTupleType(elementTypes, minLength, hasRestElement)); - type.pattern = node; - return type; - } - else if (tupleResult = getArrayLiteralTupleTypeIfApplicable(elementTypes, contextualType, hasRestElement, elementCount, inConstContext)) { - return createArrayLiteralType(tupleResult); - } - else if (forceTuple) { - return createArrayLiteralType(createTupleType(elementTypes, minLength, hasRestElement)); - } - } - return createArrayLiteralType(createArrayType(elementTypes.length ? - getUnionType(elementTypes, UnionReduction.Subtype) : - strictNullChecks ? implicitNeverType : undefinedWideningType, inConstContext)); - } - - function createArrayLiteralType(type: ObjectType) { - if (!(getObjectFlags(type) & ObjectFlags.Reference)) { - return type; - } - let literalType = (type).literalType; - if (!literalType) { - literalType = (type).literalType = cloneTypeReference(type); - literalType.objectFlags |= ObjectFlags.ArrayLiteral | ObjectFlags.ContainsObjectOrArrayLiteral; - } - return literalType; - } - - function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length, readonly = false) { - // Infer a tuple type when the contextual type is or contains a tuple-like type - if (readonly || (contextualType && forEachType(contextualType, isTupleLikeType))) { - return createTupleType(elementTypes, elementCount - (hasRestElement ? 1 : 0), hasRestElement, readonly); - } - } - - function isNumericName(name: DeclarationName): boolean { - switch (name.kind) { - case SyntaxKind.ComputedPropertyName: - return isNumericComputedName(name); - case SyntaxKind.Identifier: - return isNumericLiteralName(name.escapedText); - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - return isNumericLiteralName(name.text); - default: - return false; - } - } - - function isNumericComputedName(name: ComputedPropertyName): boolean { - // It seems odd to consider an expression of type Any to result in a numeric name, - // but this behavior is consistent with checkIndexedAccess - return isTypeAssignableToKind(checkComputedPropertyName(name), TypeFlags.NumberLike); - } - - function isInfinityOrNaNString(name: string | __String): boolean { - return name === "Infinity" || name === "-Infinity" || name === "NaN"; - } - - function isNumericLiteralName(name: string | __String) { - // The intent of numeric names is that - // - they are names with text in a numeric form, and that - // - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit', - // acquired by applying the abstract 'ToNumber' operation on the name's text. - // - // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name. - // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold. - // - // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)' - // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'. - // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names - // because their 'ToString' representation is not equal to their original text. - // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1. - // - // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'. - // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation. - // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number. - // - // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional. - // This is desired behavior, because when indexing with them as numeric entities, you are indexing - // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively. - return (+name).toString() === name; - } - - function checkComputedPropertyName(node: ComputedPropertyName): Type { - const links = getNodeLinks(node.expression); - if (!links.resolvedType) { - links.resolvedType = checkExpression(node.expression); - // This will allow types number, string, symbol or any. It will also allow enums, the unknown - // type, and any union of these types (like string | number). - if (links.resolvedType.flags & TypeFlags.Nullable || - !isTypeAssignableToKind(links.resolvedType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) && - !isTypeAssignableTo(links.resolvedType, stringNumberSymbolType)) { - error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any); - } - else { - checkThatExpressionIsProperSymbolReference(node.expression, links.resolvedType, /*reportError*/ true); - } - } - - return links.resolvedType; - } - - function getObjectLiteralIndexInfo(node: ObjectLiteralExpression, offset: number, properties: Symbol[], kind: IndexKind): IndexInfo { - const propTypes: Type[] = []; - for (let i = 0; i < properties.length; i++) { - if (kind === IndexKind.String || isNumericName(node.properties[i + offset].name!)) { - propTypes.push(getTypeOfSymbol(properties[i])); - } - } - const unionType = propTypes.length ? getUnionType(propTypes, UnionReduction.Subtype) : undefinedType; - return createIndexInfo(unionType, isConstContext(node)); - } - - function getImmediateAliasedSymbol(symbol: Symbol): Symbol | undefined { - Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here."); - const links = getSymbolLinks(symbol); - if (!links.immediateTarget) { - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - links.immediateTarget = getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true); - } - - return links.immediateTarget; - } - - function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type { - const inDestructuringPattern = isAssignmentTarget(node); - // Grammar checking - checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - - let propertiesTable: SymbolTable; - let propertiesArray: Symbol[] = []; - let spread: Type = emptyObjectType; - - const contextualType = getApparentTypeOfContextualType(node); - const contextualTypeHasPattern = contextualType && contextualType.pattern && - (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); - const inConstContext = isConstContext(node); - const checkFlags = inConstContext ? CheckFlags.Readonly : 0; - const isInJavascript = isInJSFile(node) && !isInJsonFile(node); - const enumTag = getJSDocEnumTag(node); - const isJSObjectLiteral = !contextualType && isInJavascript && !enumTag; - let objectFlags: ObjectFlags = freshObjectLiteralFlag; - let patternWithComputedProperties = false; - let hasComputedStringProperty = false; - let hasComputedNumberProperty = false; - propertiesTable = createSymbolTable(); - - let offset = 0; - for (let i = 0; i < node.properties.length; i++) { - const memberDecl = node.properties[i]; - let member = getSymbolOfNode(memberDecl); - const computedNameType = memberDecl.name && memberDecl.name.kind === SyntaxKind.ComputedPropertyName && !isWellKnownSymbolSyntactically(memberDecl.name.expression) ? - checkComputedPropertyName(memberDecl.name) : undefined; - if (memberDecl.kind === SyntaxKind.PropertyAssignment || - memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment || - isObjectLiteralMethod(memberDecl)) { - let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, checkMode) : - memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(memberDecl.name, checkMode) : - checkObjectLiteralMethod(memberDecl, checkMode); - if (isInJavascript) { - const jsDocType = getTypeForDeclarationFromJSDocComment(memberDecl); - if (jsDocType) { - checkTypeAssignableTo(type, jsDocType, memberDecl); - type = jsDocType; - } - else if (enumTag && enumTag.typeExpression) { - checkTypeAssignableTo(type, getTypeFromTypeNode(enumTag.typeExpression), memberDecl); - } - } - objectFlags |= getObjectFlags(type) & ObjectFlags.PropagatingFlags; - const nameType = computedNameType && isTypeUsableAsPropertyName(computedNameType) ? computedNameType : undefined; - const prop = nameType ? - createSymbol(SymbolFlags.Property | member.flags, getPropertyNameFromType(nameType), checkFlags | CheckFlags.Late) : - createSymbol(SymbolFlags.Property | member.flags, member.escapedName, checkFlags); - if (nameType) { - prop.nameType = nameType; - } - - if (inDestructuringPattern) { - // If object literal is an assignment pattern and if the assignment pattern specifies a default value - // for the property, make the property optional. - const isOptional = - (memberDecl.kind === SyntaxKind.PropertyAssignment && hasDefaultValue(memberDecl.initializer)) || - (memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment && memberDecl.objectAssignmentInitializer); - if (isOptional) { - prop.flags |= SymbolFlags.Optional; - } - } - else if (contextualTypeHasPattern && !(getObjectFlags(contextualType!) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { - // If object literal is contextually typed by the implied type of a binding pattern, and if the - // binding pattern specifies a default value for the property, make the property optional. - const impliedProp = getPropertyOfType(contextualType!, member.escapedName); - if (impliedProp) { - prop.flags |= impliedProp.flags & SymbolFlags.Optional; - } - - else if (!compilerOptions.suppressExcessPropertyErrors && !getIndexInfoOfType(contextualType!, IndexKind.String)) { - error(memberDecl.name, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - symbolToString(member), typeToString(contextualType!)); - } - } - - prop.declarations = member.declarations; - prop.parent = member.parent; - if (member.valueDeclaration) { - prop.valueDeclaration = member.valueDeclaration; - } - - prop.type = type; - prop.target = member; - member = prop; - } - else if (memberDecl.kind === SyntaxKind.SpreadAssignment) { - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign); - } - if (propertiesArray.length > 0) { - spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, objectFlags, inConstContext); - propertiesArray = []; - propertiesTable = createSymbolTable(); - hasComputedStringProperty = false; - hasComputedNumberProperty = false; - } - const type = checkExpression(memberDecl.expression); - if (!isValidSpreadType(type)) { - error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types); - return errorType; - } - spread = getSpreadType(spread, type, node.symbol, objectFlags, inConstContext); - offset = i + 1; - continue; - } - else { - // TypeScript 1.0 spec (April 2014) - // A get accessor declaration is processed in the same manner as - // an ordinary function declaration(section 6.1) with no parameters. - // A set accessor declaration is processed in the same manner - // as an ordinary function declaration with a single parameter and a Void return type. - Debug.assert(memberDecl.kind === SyntaxKind.GetAccessor || memberDecl.kind === SyntaxKind.SetAccessor); - checkNodeDeferred(memberDecl); - } - - if (computedNameType && !(computedNameType.flags & TypeFlags.StringOrNumberLiteralOrUnique)) { - if (isTypeAssignableTo(computedNameType, stringNumberSymbolType)) { - if (isTypeAssignableTo(computedNameType, numberType)) { - hasComputedNumberProperty = true; - } - else { - hasComputedStringProperty = true; - } - if (inDestructuringPattern) { - patternWithComputedProperties = true; - } - } - } - else { - propertiesTable.set(member.escapedName, member); - } - propertiesArray.push(member); - } - - // If object literal is contextually typed by the implied type of a binding pattern, augment the result - // type with those properties for which the binding pattern specifies a default value. - if (contextualTypeHasPattern) { - for (const prop of getPropertiesOfType(contextualType!)) { - if (!propertiesTable.get(prop.escapedName) && !(spread && getPropertyOfType(spread, prop.escapedName))) { - if (!(prop.flags & SymbolFlags.Optional)) { - error(prop.valueDeclaration || (prop).bindingElement, - Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); - } - propertiesTable.set(prop.escapedName, prop); - propertiesArray.push(prop); - } - } - } - - if (spread !== emptyObjectType) { - if (propertiesArray.length > 0) { - spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, objectFlags, inConstContext); - propertiesArray = []; - propertiesTable = createSymbolTable(); - hasComputedStringProperty = false; - hasComputedNumberProperty = false; - } - // remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site - return mapType(spread, t => t === emptyObjectType ? createObjectLiteralType() : t); - } - - return createObjectLiteralType(); - - function createObjectLiteralType() { - const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, offset, propertiesArray, IndexKind.String) : undefined; - const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, offset, propertiesArray, IndexKind.Number) : undefined; - const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); - result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral; - if (isJSObjectLiteral) { - result.objectFlags |= ObjectFlags.JSLiteral; - } - if (patternWithComputedProperties) { - result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; - } - if (inDestructuringPattern) { - result.pattern = node; - } - return result; - } - } - - function isValidSpreadType(type: Type): boolean { - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type); - if (constraint !== undefined) { - return isValidSpreadType(constraint); - } - } - return !!(type.flags & (TypeFlags.Any | TypeFlags.NonPrimitive | TypeFlags.Object | TypeFlags.InstantiableNonPrimitive) || - getFalsyFlags(type) & TypeFlags.DefinitelyFalsy && isValidSpreadType(removeDefinitelyFalsyTypes(type)) || - type.flags & TypeFlags.UnionOrIntersection && every((type).types, isValidSpreadType)); - } - - function checkJsxSelfClosingElementDeferred(node: JsxSelfClosingElement) { - checkJsxOpeningLikeElementOrOpeningFragment(node); - } - - function checkJsxSelfClosingElement(node: JsxSelfClosingElement, _checkMode: CheckMode | undefined): Type { - checkNodeDeferred(node); - return getJsxElementTypeAt(node) || anyType; - } - - function checkJsxElementDeferred(node: JsxElement) { - // Check attributes - checkJsxOpeningLikeElementOrOpeningFragment(node.openingElement); - - // Perform resolution on the closing tag so that rename/go to definition/etc work - if (isJsxIntrinsicIdentifier(node.closingElement.tagName)) { - getIntrinsicTagSymbol(node.closingElement); - } - else { - checkExpression(node.closingElement.tagName); - } - - checkJsxChildren(node); - } - - function checkJsxElement(node: JsxElement, _checkMode: CheckMode | undefined): Type { - checkNodeDeferred(node); - - return getJsxElementTypeAt(node) || anyType; - } - - function checkJsxFragment(node: JsxFragment): Type { - checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment); - - if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || getSourceFileOfNode(node).pragmas.has("jsx"))) { - error(node, compilerOptions.jsxFactory - ? Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory - : Diagnostics.JSX_fragment_is_not_supported_when_using_an_inline_JSX_factory_pragma); - } - - checkJsxChildren(node); - return getJsxElementTypeAt(node) || anyType; - } - - /** - * Returns true iff the JSX element name would be a valid JS identifier, ignoring restrictions about keywords not being identifiers - */ - function isUnhyphenatedJsxName(name: string | __String) { - // - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers - return !stringContains(name as string, "-"); - } - - /** - * Returns true iff React would emit this tag name as a string rather than an identifier or qualified name - */ - function isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression): boolean { - return tagName.kind === SyntaxKind.Identifier && isIntrinsicJsxName(tagName.escapedText); - } - - function checkJsxAttribute(node: JsxAttribute, checkMode?: CheckMode) { - return node.initializer - ? checkExpressionForMutableLocation(node.initializer, checkMode) - : trueType; // is sugar for - } - - /** - * Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element. - * - * @param openingLikeElement a JSX opening-like element - * @param filter a function to remove attributes that will not participate in checking whether attributes are assignable - * @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property. - * @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, - * which also calls getSpreadType. - */ - function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode | undefined) { - const attributes = openingLikeElement.attributes; - let attributesTable = createSymbolTable(); - let spread: Type = emptyJsxObjectType; - let hasSpreadAnyType = false; - let typeToIntersect: Type | undefined; - let explicitlySpecifyChildrenAttribute = false; - let objectFlags: ObjectFlags = ObjectFlags.JsxAttributes; - const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(openingLikeElement)); - - for (const attributeDecl of attributes.properties) { - const member = attributeDecl.symbol; - if (isJsxAttribute(attributeDecl)) { - const exprType = checkJsxAttribute(attributeDecl, checkMode); - objectFlags |= getObjectFlags(exprType) & ObjectFlags.PropagatingFlags; - - const attributeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.escapedName); - attributeSymbol.declarations = member.declarations; - attributeSymbol.parent = member.parent; - if (member.valueDeclaration) { - attributeSymbol.valueDeclaration = member.valueDeclaration; - } - attributeSymbol.type = exprType; - attributeSymbol.target = member; - attributesTable.set(attributeSymbol.escapedName, attributeSymbol); - if (attributeDecl.name.escapedText === jsxChildrenPropertyName) { - explicitlySpecifyChildrenAttribute = true; - } - } - else { - Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute); - if (attributesTable.size > 0) { - spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, objectFlags, /*readonly*/ false); - attributesTable = createSymbolTable(); - } - const exprType = checkExpressionCached(attributeDecl.expression, checkMode); - if (isTypeAny(exprType)) { - hasSpreadAnyType = true; - } - if (isValidSpreadType(exprType)) { - spread = getSpreadType(spread, exprType, attributes.symbol, objectFlags, /*readonly*/ false); - } - else { - typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType; - } - } - } - - if (!hasSpreadAnyType) { - if (attributesTable.size > 0) { - spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, objectFlags, /*readonly*/ false); - } - } - - // Handle children attribute - const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ? openingLikeElement.parent as JsxElement : undefined; - // We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement - if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) { - const childrenTypes: Type[] = checkJsxChildren(parent, checkMode); - - if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { - // Error if there is a attribute named "children" explicitly specified and children element. - // This is because children element will overwrite the value from attributes. - // Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread. - if (explicitlySpecifyChildrenAttribute) { - error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, unescapeLeadingUnderscores(jsxChildrenPropertyName)); - } - - const contextualType = getApparentTypeOfContextualType(openingLikeElement.attributes); - const childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); - // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process - const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName); - childrenPropSymbol.type = childrenTypes.length === 1 ? - childrenTypes[0] : - (getArrayLiteralTupleTypeIfApplicable(childrenTypes, childrenContextualType, /*hasRestElement*/ false) || createArrayType(getUnionType(childrenTypes))); - // Fake up a property declaration for the children - childrenPropSymbol.valueDeclaration = createPropertySignature(/*modifiers*/ undefined, unescapeLeadingUnderscores(jsxChildrenPropertyName), /*questionToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined); - childrenPropSymbol.valueDeclaration.parent = attributes; - childrenPropSymbol.valueDeclaration.symbol = childrenPropSymbol; - const childPropMap = createSymbolTable(); - childPropMap.set(jsxChildrenPropertyName, childrenPropSymbol); - spread = getSpreadType(spread, createAnonymousType(attributes.symbol, childPropMap, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined), - attributes.symbol, objectFlags, /*readonly*/ false); - - } - } - - if (hasSpreadAnyType) { - return anyType; - } - if (typeToIntersect && spread !== emptyJsxObjectType) { - return getIntersectionType([typeToIntersect, spread]); - } - return typeToIntersect || (spread === emptyJsxObjectType ? createJsxAttributesType() : spread); - - /** - * Create anonymous type from given attributes symbol table. - * @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable - * @param attributesTable a symbol table of attributes property - */ - function createJsxAttributesType() { - objectFlags |= freshObjectLiteralFlag; - const result = createAnonymousType(attributes.symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); - result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral; - return result; - } - } - - function checkJsxChildren(node: JsxElement | JsxFragment, checkMode?: CheckMode) { - const childrenTypes: Type[] = []; - for (const child of node.children) { - // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that - // because then type of children property will have constituent of string type. - if (child.kind === SyntaxKind.JsxText) { - if (!child.containsOnlyTriviaWhiteSpaces) { - childrenTypes.push(stringType); - } - } - else { - childrenTypes.push(checkExpressionForMutableLocation(child, checkMode)); - } - } - return childrenTypes; - } - - /** - * Check attributes property of opening-like element. This function is called during chooseOverload to get call signature of a JSX opening-like element. - * (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used) - * @param node a JSXAttributes to be resolved of its type - */ - function checkJsxAttributes(node: JsxAttributes, checkMode: CheckMode | undefined) { - return createJsxAttributesTypeFromAttributesProperty(node.parent, checkMode); - } - - function getJsxType(name: __String, location: Node | undefined) { - const namespace = getJsxNamespaceAt(location); - const exports = namespace && getExportsOfSymbol(namespace); - const typeSymbol = exports && getSymbol(exports, name, SymbolFlags.Type); - return typeSymbol ? getDeclaredTypeOfSymbol(typeSymbol) : errorType; - } - - /** - * Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic - * property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic - * string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). - * May also return unknownSymbol if both of these lookups fail. - */ - function getIntrinsicTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { - const links = getNodeLinks(node); - if (!links.resolvedSymbol) { - const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements, node); - if (intrinsicElementsType !== errorType) { - // Property case - if (!isIdentifier(node.tagName)) return Debug.fail(); - const intrinsicProp = getPropertyOfType(intrinsicElementsType, node.tagName.escapedText); - if (intrinsicProp) { - links.jsxFlags |= JsxFlags.IntrinsicNamedElement; - return links.resolvedSymbol = intrinsicProp; - } - - // Intrinsic string indexer case - const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String); - if (indexSignatureType) { - links.jsxFlags |= JsxFlags.IntrinsicIndexedElement; - return links.resolvedSymbol = intrinsicElementsType.symbol; - } - - // Wasn't found - error(node, Diagnostics.Property_0_does_not_exist_on_type_1, idText(node.tagName), "JSX." + JsxNames.IntrinsicElements); - return links.resolvedSymbol = unknownSymbol; - } - else { - if (noImplicitAny) { - error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, unescapeLeadingUnderscores(JsxNames.IntrinsicElements)); - } - return links.resolvedSymbol = unknownSymbol; - } - } - return links.resolvedSymbol; - } - - function getJsxNamespaceAt(location: Node | undefined): Symbol { - const links = location && getNodeLinks(location); - if (links && links.jsxNamespace) { - return links.jsxNamespace; - } - if (!links || links.jsxNamespace !== false) { - const namespaceName = getJsxNamespace(location); - const resolvedNamespace = resolveName(location, namespaceName, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined, namespaceName, /*isUse*/ false); - if (resolvedNamespace) { - const candidate = resolveSymbol(getSymbol(getExportsOfSymbol(resolveSymbol(resolvedNamespace)), JsxNames.JSX, SymbolFlags.Namespace)); - if (candidate) { - if (links) { - links.jsxNamespace = candidate; - } - return candidate; - } - if (links) { - links.jsxNamespace = false; - } - } - } - // JSX global fallback - return getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined)!; // TODO: GH#18217 - } - - /** - * Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer. - * Get a single property from that container if existed. Report an error if there are more than one property. - * - * @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer - * if other string is given or the container doesn't exist, return undefined. - */ - function getNameFromJsxElementAttributesContainer(nameOfAttribPropContainer: __String, jsxNamespace: Symbol): __String | undefined { - // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol] - const jsxElementAttribPropInterfaceSym = jsxNamespace && getSymbol(jsxNamespace.exports!, nameOfAttribPropContainer, SymbolFlags.Type); - // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [type] - const jsxElementAttribPropInterfaceType = jsxElementAttribPropInterfaceSym && getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym); - // The properties of JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute - const propertiesOfJsxElementAttribPropInterface = jsxElementAttribPropInterfaceType && getPropertiesOfType(jsxElementAttribPropInterfaceType); - if (propertiesOfJsxElementAttribPropInterface) { - // Element Attributes has zero properties, so the element attributes type will be the class instance type - if (propertiesOfJsxElementAttribPropInterface.length === 0) { - return "" as __String; - } - // Element Attributes has one property, so the element attributes type will be the type of the corresponding - // property of the class instance type - else if (propertiesOfJsxElementAttribPropInterface.length === 1) { - return propertiesOfJsxElementAttribPropInterface[0].escapedName; - } - else if (propertiesOfJsxElementAttribPropInterface.length > 1) { - // More than one property on ElementAttributesProperty is an error - error(jsxElementAttribPropInterfaceSym!.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, unescapeLeadingUnderscores(nameOfAttribPropContainer)); - } - } - return undefined; - } - - function getJsxLibraryManagedAttributes(jsxNamespace: Symbol) { - // JSX.LibraryManagedAttributes [symbol] - return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.LibraryManagedAttributes, SymbolFlags.Type); - } - - /// e.g. "props" for React.d.ts, - /// or 'undefined' if ElementAttributesProperty doesn't exist (which means all - /// non-intrinsic elements' attributes type is 'any'), - /// or '' if it has 0 properties (which means every - /// non-intrinsic elements' attributes type is the element instance type) - function getJsxElementPropertiesName(jsxNamespace: Symbol) { - return getNameFromJsxElementAttributesContainer(JsxNames.ElementAttributesPropertyNameContainer, jsxNamespace); - } - - function getJsxElementChildrenPropertyName(jsxNamespace: Symbol): __String | undefined { - return getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer, jsxNamespace); - } - - function getUninstantiatedJsxSignaturesOfType(elementType: Type, caller: JsxOpeningLikeElement): readonly Signature[] { - if (elementType.flags & TypeFlags.String) { - return [anySignature]; - } - else if (elementType.flags & TypeFlags.StringLiteral) { - const intrinsicType = getIntrinsicAttributesTypeFromStringLiteralType(elementType as StringLiteralType, caller); - if (!intrinsicType) { - error(caller, Diagnostics.Property_0_does_not_exist_on_type_1, (elementType as StringLiteralType).value, "JSX." + JsxNames.IntrinsicElements); - return emptyArray; - } - else { - const fakeSignature = createSignatureForJSXIntrinsic(caller, intrinsicType); - return [fakeSignature]; - } - } - const apparentElemType = getApparentType(elementType); - // Resolve the signatures, preferring constructor - let signatures = getSignaturesOfType(apparentElemType, SignatureKind.Construct); - if (signatures.length === 0) { - // No construct signatures, try call signatures - signatures = getSignaturesOfType(apparentElemType, SignatureKind.Call); - } - if (signatures.length === 0 && apparentElemType.flags & TypeFlags.Union) { - // If each member has some combination of new/call signatures; make a union signature list for those - signatures = getUnionSignatures(map((apparentElemType as UnionType).types, t => getUninstantiatedJsxSignaturesOfType(t, caller))); - } - return signatures; - } - - function getIntrinsicAttributesTypeFromStringLiteralType(type: StringLiteralType, location: Node): Type | undefined { - // If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type - // For example: - // var CustomTag: "h1" = "h1"; - // Hello World - const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements, location); - if (intrinsicElementsType !== errorType) { - const stringLiteralTypeName = type.value; - const intrinsicProp = getPropertyOfType(intrinsicElementsType, escapeLeadingUnderscores(stringLiteralTypeName)); - if (intrinsicProp) { - return getTypeOfSymbol(intrinsicProp); - } - const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String); - if (indexSignatureType) { - return indexSignatureType; - } - return undefined; - } - // If we need to report an error, we already done so here. So just return any to prevent any more error downstream - return anyType; - } - - function checkJsxReturnAssignableToAppropriateBound(refKind: JsxReferenceKind, elemInstanceType: Type, openingLikeElement: Node) { - if (refKind === JsxReferenceKind.Function) { - const sfcReturnConstraint = getJsxStatelessElementTypeAt(openingLikeElement); - if (sfcReturnConstraint) { - checkTypeRelatedTo(elemInstanceType, sfcReturnConstraint, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements); - } - } - else if (refKind === JsxReferenceKind.Component) { - const classConstraint = getJsxElementClassTypeAt(openingLikeElement); - if (classConstraint) { - // Issue an error if this return type isn't assignable to JSX.ElementClass or JSX.Element, failing that - checkTypeRelatedTo(elemInstanceType, classConstraint, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements); - } - } - else { // Mixed - const sfcReturnConstraint = getJsxStatelessElementTypeAt(openingLikeElement); - const classConstraint = getJsxElementClassTypeAt(openingLikeElement); - if (!sfcReturnConstraint || !classConstraint) { - return; - } - const combined = getUnionType([sfcReturnConstraint, classConstraint]); - checkTypeRelatedTo(elemInstanceType, combined, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements); - } - } - - /** - * Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name. - * The function is intended to be called from a function which has checked that the opening element is an intrinsic element. - * @param node an intrinsic JSX opening-like element - */ - function getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type { - Debug.assert(isJsxIntrinsicIdentifier(node.tagName)); - const links = getNodeLinks(node); - if (!links.resolvedJsxElementAttributesType) { - const symbol = getIntrinsicTagSymbol(node); - if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) { - return links.resolvedJsxElementAttributesType = getTypeOfSymbol(symbol); - } - else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) { - return links.resolvedJsxElementAttributesType = - getIndexTypeOfType(getDeclaredTypeOfSymbol(symbol), IndexKind.String)!; - } - else { - return links.resolvedJsxElementAttributesType = errorType; - } - } - return links.resolvedJsxElementAttributesType; - } - - function getJsxElementClassTypeAt(location: Node): Type | undefined { - const type = getJsxType(JsxNames.ElementClass, location); - if (type === errorType) return undefined; - return type; - } - - function getJsxElementTypeAt(location: Node): Type { - return getJsxType(JsxNames.Element, location); - } - - function getJsxStatelessElementTypeAt(location: Node): Type | undefined { - const jsxElementType = getJsxElementTypeAt(location); - if (jsxElementType) { - return getUnionType([jsxElementType, nullType]); - } - } - - /** - * Returns all the properties of the Jsx.IntrinsicElements interface - */ - function getJsxIntrinsicTagNamesAt(location: Node): Symbol[] { - const intrinsics = getJsxType(JsxNames.IntrinsicElements, location); - return intrinsics ? getPropertiesOfType(intrinsics) : emptyArray; - } - - function checkJsxPreconditions(errorNode: Node) { - // Preconditions for using JSX - if ((compilerOptions.jsx || JsxEmit.None) === JsxEmit.None) { - error(errorNode, Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided); - } - - if (getJsxElementTypeAt(errorNode) === undefined) { - if (noImplicitAny) { - error(errorNode, Diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist); - } - } - } - - function checkJsxOpeningLikeElementOrOpeningFragment(node: JsxOpeningLikeElement | JsxOpeningFragment) { - const isNodeOpeningLikeElement = isJsxOpeningLikeElement(node); - - if (isNodeOpeningLikeElement) { - checkGrammarJsxElement(node); - } - checkJsxPreconditions(node); - // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. - // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. - const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; - const reactNamespace = getJsxNamespace(node); - const reactLocation = isNodeOpeningLikeElement ? (node).tagName : node; - const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true); - if (reactSym) { - // Mark local symbol as referenced here because it might not have been marked - // if jsx emit was not react as there wont be error being emitted - reactSym.isReferenced = SymbolFlags.All; - - // If react symbol is alias, mark it as refereced - if (reactSym.flags & SymbolFlags.Alias) { - markAliasSymbolAsReferenced(reactSym); - } - } - - if (isNodeOpeningLikeElement) { - const sig = getResolvedSignature(node as JsxOpeningLikeElement); - checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(node as JsxOpeningLikeElement), getReturnTypeOfSignature(sig), node); - } - } - - /** - * Check if a property with the given name is known anywhere in the given type. In an object type, a property - * is considered known if - * 1. the object type is empty and the check is for assignability, or - * 2. if the object type has index signatures, or - * 3. if the property is actually declared in the object type - * (this means that 'toString', for example, is not usually a known property). - * 4. In a union or intersection type, - * a property is considered known if it is known in any constituent type. - * @param targetType a type to search a given name in - * @param name a property name to search - * @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType - */ - function isKnownProperty(targetType: Type, name: __String, isComparingJsxAttributes: boolean): boolean { - if (targetType.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(targetType as ObjectType); - if (resolved.stringIndexInfo || - resolved.numberIndexInfo && isNumericLiteralName(name) || - getPropertyOfObjectType(targetType, name) || - isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) { - // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known. - return true; - } - } - else if (targetType.flags & TypeFlags.UnionOrIntersection && isExcessPropertyCheckTarget(targetType)) { - for (const t of (targetType as UnionOrIntersectionType).types) { - if (isKnownProperty(t, name, isComparingJsxAttributes)) { - return true; - } - } - } - return false; - } - - function isExcessPropertyCheckTarget(type: Type): boolean { - return !!(type.flags & TypeFlags.Object && !(getObjectFlags(type) & ObjectFlags.ObjectLiteralPatternWithComputedProperties) || - type.flags & TypeFlags.NonPrimitive || - type.flags & TypeFlags.Union && some((type).types, isExcessPropertyCheckTarget) || - type.flags & TypeFlags.Intersection && every((type).types, isExcessPropertyCheckTarget)); - } - - function checkJsxExpression(node: JsxExpression, checkMode?: CheckMode) { - checkGrammarJsxExpression(node); - if (node.expression) { - const type = checkExpression(node.expression, checkMode); - if (node.dotDotDotToken && type !== anyType && !isArrayType(type)) { - error(node, Diagnostics.JSX_spread_child_must_be_an_array_type); - } - return type; - } - else { - return errorType; - } - } - - function getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags { - return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0; - } - - /** - * Return whether this symbol is a member of a prototype somewhere - * Note that this is not tracked well within the compiler, so the answer may be incorrect. - */ - function isPrototypeProperty(symbol: Symbol) { - if (symbol.flags & SymbolFlags.Method || getCheckFlags(symbol) & CheckFlags.SyntheticMethod) { - return true; - } - if (isInJSFile(symbol.valueDeclaration)) { - const parent = symbol.valueDeclaration.parent; - return parent && isBinaryExpression(parent) && - getAssignmentDeclarationKind(parent) === AssignmentDeclarationKind.PrototypeProperty; - } - } - - /** - * Check whether the requested property access is valid. - * Returns true if node is a valid property access, and false otherwise. - * @param node The node to be checked. - * @param isSuper True if the access is from `super.`. - * @param type The type of the object whose property is being accessed. (Not the type of the property.) - * @param prop The symbol for the property being accessed. - */ - function checkPropertyAccessibility( - node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement, - isSuper: boolean, type: Type, prop: Symbol): boolean { - const flags = getDeclarationModifierFlagsFromSymbol(prop); - const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right : node.kind === SyntaxKind.ImportType ? node : node.name; - - if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) { - // Synthetic property with private constituent property - error(errorNode, Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(prop), typeToString(type)); - return false; - } - - if (isSuper) { - // TS 1.0 spec (April 2014): 4.8.2 - // - In a constructor, instance member function, instance member accessor, or - // instance member variable initializer where this references a derived class instance, - // a super property access is permitted and must specify a public instance member function of the base class. - // - In a static member function or static member accessor - // where this references the constructor function object of a derived class, - // a super property access is permitted and must specify a public static member function of the base class. - if (languageVersion < ScriptTarget.ES2015) { - if (symbolHasNonMethodDeclaration(prop)) { - error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword); - return false; - } - } - if (flags & ModifierFlags.Abstract) { - // A method cannot be accessed in a super property access if the method is abstract. - // This error could mask a private property access error. But, a member - // cannot simultaneously be private and abstract, so this will trigger an - // additional error elsewhere. - error(errorNode, Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, symbolToString(prop), typeToString(getDeclaringClass(prop)!)); - return false; - } - } - - // Referencing abstract properties within their own constructors is not allowed - if ((flags & ModifierFlags.Abstract) && isThisProperty(node) && symbolHasNonMethodDeclaration(prop)) { - const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!); - if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(node)) { - error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!)); // TODO: GH#18217 - return false; - } - } - - if (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)) { - if (!getContainingClass(node)) { - error(errorNode, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - return false; - } - return true; - } - - // Public properties are otherwise accessible. - if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) { - return true; - } - - // Property is known to be private or protected at this point - - // Private property is accessible if the property is within the declaring class - if (flags & ModifierFlags.Private) { - const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!)!; - if (!isNodeWithinClass(node, declaringClassDeclaration)) { - error(errorNode, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(getDeclaringClass(prop)!)); - return false; - } - return true; - } - - // Property is known to be protected at this point - - // All protected properties of a supertype are accessible in a super access - if (isSuper) { - return true; - } - - // Find the first enclosing class that has the declaring classes of the protected constituents - // of the property as base classes - let enclosingClass = forEachEnclosingClass(node, enclosingDeclaration => { - const enclosingClass = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration)!); - return isClassDerivedFromDeclaringClasses(enclosingClass, prop) ? enclosingClass : undefined; - }); - // A protected property is accessible if the property is within the declaring class or classes derived from it - if (!enclosingClass) { - // allow PropertyAccessibility if context is in function with this parameter - // static member access is disallow - let thisParameter: ParameterDeclaration | undefined; - if (flags & ModifierFlags.Static || !(thisParameter = getThisParameterFromNodeContext(node)) || !thisParameter.type) { - error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(getDeclaringClass(prop) || type)); - return false; - } - - const thisType = getTypeFromTypeNode(thisParameter.type); - enclosingClass = ((thisType.flags & TypeFlags.TypeParameter) ? getConstraintOfTypeParameter(thisType) : thisType) as InterfaceType; - } - // No further restrictions for static properties - if (flags & ModifierFlags.Static) { - return true; - } - if (type.flags & TypeFlags.TypeParameter) { - // get the original type -- represented as the type constraint of the 'this' type - type = (type as TypeParameter).isThisType ? getConstraintOfTypeParameter(type)! : getBaseConstraintOfType(type)!; // TODO: GH#18217 Use a different variable that's allowed to be undefined - } - if (!type || !hasBaseType(type, enclosingClass)) { - error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass)); - return false; - } - return true; - } - - function getThisParameterFromNodeContext (node: Node) { - const thisContainer = getThisContainer(node, /* includeArrowFunctions */ false); - return thisContainer && isFunctionLike(thisContainer) ? getThisParameter(thisContainer) : undefined; - } - - function symbolHasNonMethodDeclaration(symbol: Symbol) { - return !!forEachProperty(symbol, prop => !(prop.flags & SymbolFlags.Method)); - } - - function checkNonNullExpression(node: Expression | QualifiedName) { - return checkNonNullType(checkExpression(node), node); - } - - function isNullableType(type: Type) { - return !!((strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable); - } - - function getNonNullableTypeIfNeeded(type: Type) { - return isNullableType(type) ? getNonNullableType(type) : type; - } - - function reportObjectPossiblyNullOrUndefinedError(node: Node, flags: TypeFlags) { - error(node, flags & TypeFlags.Undefined ? flags & TypeFlags.Null ? - Diagnostics.Object_is_possibly_null_or_undefined : - Diagnostics.Object_is_possibly_undefined : - Diagnostics.Object_is_possibly_null - ); - } - - function reportCannotInvokePossiblyNullOrUndefinedError(node: Node, flags: TypeFlags) { - error(node, flags & TypeFlags.Undefined ? flags & TypeFlags.Null ? - Diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined : - Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined : - Diagnostics.Cannot_invoke_an_object_which_is_possibly_null - ); - } - - function checkNonNullTypeWithReporter( - type: Type, - node: Node, - reportError: (node: Node, kind: TypeFlags) => void - ): Type { - if (strictNullChecks && type.flags & TypeFlags.Unknown) { - error(node, Diagnostics.Object_is_of_type_unknown); - return errorType; - } - const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable; - if (kind) { - reportError(node, kind); - const t = getNonNullableType(type); - return t.flags & (TypeFlags.Nullable | TypeFlags.Never) ? errorType : t; - } - return type; - } - - function checkNonNullType(type: Type, node: Node) { - return checkNonNullTypeWithReporter(type, node, reportObjectPossiblyNullOrUndefinedError); - } - - function checkNonNullNonVoidType(type: Type, node: Node): Type { - const nonNullType = checkNonNullType(type, node); - if (nonNullType !== errorType && nonNullType.flags & TypeFlags.Void) { - error(node, Diagnostics.Object_is_possibly_undefined); - } - return nonNullType; - } - - function checkPropertyAccessExpression(node: PropertyAccessExpression) { - return node.flags & NodeFlags.OptionalChain ? checkPropertyAccessChain(node as PropertyAccessChain) : - checkPropertyAccessExpressionOrQualifiedName(node, node.expression, checkNonNullExpression(node.expression), node.name); - } - - function checkPropertyAccessChain(node: PropertyAccessChain) { - const leftType = checkExpression(node.expression); - const nonOptionalType = getOptionalExpressionType(leftType, node.expression); - return propagateOptionalTypeMarker(checkPropertyAccessExpressionOrQualifiedName(node, node.expression, checkNonNullType(nonOptionalType, node.expression), node.name), node, nonOptionalType !== leftType); - } - - function checkQualifiedName(node: QualifiedName) { - return checkPropertyAccessExpressionOrQualifiedName(node, node.left, checkNonNullExpression(node.left), node.right); - } - - function isMethodAccessForCall(node: Node) { - while (node.parent.kind === SyntaxKind.ParenthesizedExpression) { - node = node.parent; - } - return isCallOrNewExpression(node.parent) && node.parent.expression === node; - } - - // Lookup the private identifier lexically. - function lookupSymbolForPrivateIdentifierDeclaration(propName: __String, location: Node): Symbol | undefined { - for (let containingClass = getContainingClass(location); !!containingClass; containingClass = getContainingClass(containingClass)) { - const { symbol } = containingClass; - const name = getSymbolNameForPrivateIdentifier(symbol, propName); - const prop = (symbol.members && symbol.members.get(name)) || (symbol.exports && symbol.exports.get(name)); - if (prop) { - return prop; - } - } - } - - function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined { - return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName); - } - - function checkPrivateIdentifierPropertyAccess(leftType: Type, right: PrivateIdentifier, lexicallyScopedIdentifier: Symbol | undefined): boolean { - // Either the identifier could not be looked up in the lexical scope OR the lexically scoped identifier did not exist on the type. - // Find a private identifier with the same description on the type. - let propertyOnType: Symbol | undefined; - const properties = getPropertiesOfType(leftType); - if (properties) { - forEach(properties, (symbol: Symbol) => { - const decl = symbol.valueDeclaration; - if (decl && isNamedDeclaration(decl) && isPrivateIdentifier(decl.name) && decl.name.escapedText === right.escapedText) { - propertyOnType = symbol; - return true; - } - }); - } - const diagName = diagnosticName(right); - if (propertyOnType) { - const typeValueDecl = propertyOnType.valueDeclaration; - const typeClass = getContainingClass(typeValueDecl); - Debug.assert(!!typeClass); - // We found a private identifier property with the same description. - // Either: - // - There is a lexically scoped private identifier AND it shadows the one we found on the type. - // - It is an attempt to access the private identifier outside of the class. - if (lexicallyScopedIdentifier) { - const lexicalValueDecl = lexicallyScopedIdentifier.valueDeclaration; - const lexicalClass = getContainingClass(lexicalValueDecl); - Debug.assert(!!lexicalClass); - if (findAncestor(lexicalClass, n => typeClass === n)) { - const diagnostic = error( - right, - Diagnostics.The_property_0_cannot_be_accessed_on_type_1_within_this_class_because_it_is_shadowed_by_another_private_identifier_with_the_same_spelling, - diagName, - typeToString(leftType) - ); - - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - lexicalValueDecl, - Diagnostics.The_shadowing_declaration_of_0_is_defined_here, - diagName - ), - createDiagnosticForNode( - typeValueDecl, - Diagnostics.The_declaration_of_0_that_you_probably_intended_to_use_is_defined_here, - diagName - ) - ); - return true; - } - } - error( - right, - Diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier, - diagName, - diagnosticName(typeClass!.name || anon) - ); - return true; - } - return false; - } - - function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, leftType: Type, right: Identifier | PrivateIdentifier) { - const parentSymbol = getNodeLinks(left).resolvedSymbol; - const assignmentKind = getAssignmentTargetKind(node); - const apparentType = getApparentType(assignmentKind !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(leftType) : leftType); - if (isPrivateIdentifier(right)) { - if (isOptionalChain(node)) { - grammarErrorOnNode(right, Diagnostics.An_optional_chain_cannot_contain_private_identifiers); - return anyType; - } - checkExternalEmitHelpers(node, ExternalEmitHelpers.ClassPrivateFieldGet); - } - const isAnyLike = isTypeAny(apparentType) || apparentType === silentNeverType; - let prop: Symbol | undefined; - if (isPrivateIdentifier(right)) { - const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(right.escapedText, right); - if (isAnyLike) { - if (lexicallyScopedSymbol) { - return apparentType; - } - if (!getContainingClass(right)) { - grammarErrorOnNode(right, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - return anyType; - } - } - prop = lexicallyScopedSymbol ? getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedSymbol) : undefined; - // Check for private-identifier-specific shadowing and lexical-scoping errors. - if (!prop && checkPrivateIdentifierPropertyAccess(leftType, right, lexicallyScopedSymbol)) { - return errorType; - } - } - else { - if (isAnyLike) { - if (isIdentifier(left) && parentSymbol) { - markAliasReferenced(parentSymbol, node); - } - return apparentType; - } - prop = getPropertyOfType(apparentType, right.escapedText); - } - if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) { - markAliasReferenced(parentSymbol, node); - } - - let propType: Type; - if (!prop) { - const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; - if (!(indexInfo && indexInfo.type)) { - if (isJSLiteralType(leftType)) { - return anyType; - } - if (leftType.symbol === globalThisSymbol) { - if (globalThisSymbol.exports!.has(right.escapedText) && (globalThisSymbol.exports!.get(right.escapedText)!.flags & SymbolFlags.BlockScoped)) { - error(right, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(right.escapedText), typeToString(leftType)); - } - else if (noImplicitAny) { - error(right, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(leftType)); - } - return anyType; - } - if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) { - reportNonexistentProperty(right, isThisTypeParameter(leftType) ? apparentType : leftType); - } - return errorType; - } - if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) { - error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType)); - } - propType = indexInfo.type; - } - else { - checkPropertyNotUsedBeforeDeclaration(prop, node, right); - markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword); - getNodeLinks(node).resolvedSymbol = prop; - checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop); - if (isAssignmentToReadonlyEntity(node as Expression, prop, assignmentKind)) { - error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, idText(right)); - return errorType; - } - propType = getConstraintForLocation(getTypeOfSymbol(prop), node); - } - return getFlowTypeOfAccessExpression(node, prop, propType, right); - } - - function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node) { - // Only compute control flow type if this is a property access expression that isn't an - // assignment target, and the referenced property was declared as a variable, property, - // accessor, or optional method. - const assignmentKind = getAssignmentTargetKind(node); - if (!isAccessExpression(node) || - assignmentKind === AssignmentKind.Definite || - prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { - return propType; - } - // If strict null checks and strict property initialization checks are enabled, if we have - // a this.xxx property access, if the property is an instance property without an initializer, - // and if we are in a constructor of the same class as the property declaration, assume that - // the property is uninitialized at the top of the control flow. - let assumeUninitialized = false; - if (strictNullChecks && strictPropertyInitialization && node.expression.kind === SyntaxKind.ThisKeyword) { - const declaration = prop && prop.valueDeclaration; - if (declaration && isInstancePropertyWithoutInitializer(declaration)) { - const flowContainer = getControlFlowContainer(node); - if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) { - assumeUninitialized = true; - } - } - } - else if (strictNullChecks && prop && prop.valueDeclaration && - isPropertyAccessExpression(prop.valueDeclaration) && - getAssignmentDeclarationPropertyAccessKind(prop.valueDeclaration) && - getControlFlowContainer(node) === getControlFlowContainer(prop.valueDeclaration)) { - assumeUninitialized = true; - } - const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType); - if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { - error(errorNode, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217 - // Return the declared type to reduce follow-on errors - return propType; - } - return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType; - } - - function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateIdentifier): void { - const { valueDeclaration } = prop; - if (!valueDeclaration || getSourceFileOfNode(node).isDeclarationFile) { - return; - } - - let diagnosticMessage; - const declarationName = idText(right); - if (isInPropertyInitializer(node) - && !(isAccessExpression(node) && isAccessExpression(node.expression)) - && !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) - && !isPropertyDeclaredInAncestorClass(prop)) { - diagnosticMessage = error(right, Diagnostics.Property_0_is_used_before_its_initialization, declarationName); - } - else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration && - node.parent.kind !== SyntaxKind.TypeReference && - !(valueDeclaration.flags & NodeFlags.Ambient) && - !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)) { - diagnosticMessage = error(right, Diagnostics.Class_0_used_before_its_declaration, declarationName); - } - - if (diagnosticMessage) { - addRelatedInfo(diagnosticMessage, - createDiagnosticForNode(valueDeclaration, Diagnostics._0_is_declared_here, declarationName) - ); - } - } - - function isInPropertyInitializer(node: Node): boolean { - return !!findAncestor(node, node => { - switch (node.kind) { - case SyntaxKind.PropertyDeclaration: - return true; - case SyntaxKind.PropertyAssignment: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.SpreadAssignment: - case SyntaxKind.ComputedPropertyName: - case SyntaxKind.TemplateSpan: - case SyntaxKind.JsxExpression: - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxAttributes: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.ExpressionWithTypeArguments: - case SyntaxKind.HeritageClause: - return false; - default: - return isExpressionNode(node) ? false : "quit"; - } - }); - } - - /** - * It's possible that "prop.valueDeclaration" is a local declaration, but the property was also declared in a superclass. - * In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration. - */ - function isPropertyDeclaredInAncestorClass(prop: Symbol): boolean { - if (!(prop.parent!.flags & SymbolFlags.Class)) { - return false; - } - let classType: InterfaceType | undefined = getTypeOfSymbol(prop.parent!) as InterfaceType; - while (true) { - classType = classType.symbol && getSuperClass(classType) as InterfaceType | undefined; - if (!classType) { - return false; - } - const superProperty = getPropertyOfType(classType, prop.escapedName); - if (superProperty && superProperty.valueDeclaration) { - return true; - } - } - } - - function getSuperClass(classType: InterfaceType): Type | undefined { - const x = getBaseTypes(classType); - if (x.length === 0) { - return undefined; - } - return getIntersectionType(x); - } - - function reportNonexistentProperty(propNode: Identifier | PrivateIdentifier, containingType: Type) { - let errorInfo: DiagnosticMessageChain | undefined; - let relatedInfo: Diagnostic | undefined; - if (!isPrivateIdentifier(propNode) && containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) { - for (const subtype of (containingType as UnionType).types) { - if (!getPropertyOfType(subtype, propNode.escapedText) && !getIndexInfoOfType(subtype, IndexKind.String)) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(subtype)); - break; - } - } - } - if (typeHasStaticProperty(propNode.escapedText, containingType)) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_is_a_static_member_of_type_1, declarationNameToString(propNode), typeToString(containingType)); - } - else { - const promisedType = getPromisedTypeOfPromise(containingType); - if (promisedType && getPropertyOfType(promisedType, propNode.escapedText)) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); - relatedInfo = createDiagnosticForNode(propNode, Diagnostics.Did_you_forget_to_use_await); - } - else { - const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType); - if (suggestion !== undefined) { - const suggestedName = symbolName(suggestion); - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName); - relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName); - } - else { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); - } - } - } - const resultDiagnostic = createDiagnosticForNodeFromMessageChain(propNode, errorInfo); - if (relatedInfo) { - addRelatedInfo(resultDiagnostic, relatedInfo); - } - diagnostics.add(resultDiagnostic); - } - - function typeHasStaticProperty(propName: __String, containingType: Type): boolean { - const prop = containingType.symbol && getPropertyOfType(getTypeOfSymbol(containingType.symbol), propName); - return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static); - } - - function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { - return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value); - } - - function getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): string | undefined { - const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType); - return suggestion && symbolName(suggestion); - } - - function getSuggestedSymbolForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): Symbol | undefined { - Debug.assert(outerName !== undefined, "outername should always be defined"); - const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, (symbols, name, meaning) => { - Debug.assertEqual(outerName, name, "name should equal outerName"); - const symbol = getSymbol(symbols, name, meaning); - // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function - // So the table *contains* `x` but `x` isn't actually in scope. - // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. - return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning); - }); - return result; - } - - function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined { - const symbolResult = getSuggestedSymbolForNonexistentSymbol(location, outerName, meaning); - return symbolResult && symbolName(symbolResult); - } - - function getSuggestedSymbolForNonexistentModule(name: Identifier, targetModule: Symbol): Symbol | undefined { - return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember); - } - - function getSuggestionForNonexistentExport(name: Identifier, targetModule: Symbol): string | undefined { - const suggestion = getSuggestedSymbolForNonexistentModule(name, targetModule); - return suggestion && symbolName(suggestion); - } - - function getSuggestionForNonexistentIndexSignature(objectType: Type, expr: ElementAccessExpression, keyedType: Type): string | undefined { - // check if object type has setter or getter - function hasProp(name: "set" | "get") { - const prop = getPropertyOfObjectType(objectType, <__String>name); - if (prop) { - const s = getSingleCallSignature(getTypeOfSymbol(prop)); - return !!s && getMinArgumentCount(s) >= 1 && isTypeAssignableTo(keyedType, getTypeAtPosition(s, 0)); - } - return false; - }; - - const suggestedMethod = isAssignmentTarget(expr) ? "set" : "get"; - if (!hasProp(suggestedMethod)) { - return undefined; - } - - let suggestion = tryGetPropertyAccessOrIdentifierToString(expr.expression); - if (suggestion === undefined) { - suggestion = suggestedMethod; - } - else { - suggestion += "." + suggestedMethod; - } - - return suggestion; - } - - /** - * Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. - * Names less than length 3 only check for case-insensitive equality, not levenshtein distance. - * - * If there is a candidate that's the same except for case, return that. - * If there is a candidate that's within one edit of the name, return that. - * Otherwise, return the candidate with the smallest Levenshtein distance, - * except for candidates: - * * With no name - * * Whose meaning doesn't match the `meaning` parameter. - * * Whose length differs from the target name by more than 0.34 of the length of the name. - * * Whose levenshtein distance is more than 0.4 of the length of the name - * (0.4 allows 1 substitution/transposition for every 5 characters, - * and 1 insertion/deletion at 3 characters) - */ - function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined { - return getSpellingSuggestion(name, symbols, getCandidateName); - function getCandidateName(candidate: Symbol) { - const candidateName = symbolName(candidate); - return !startsWith(candidateName, "\"") && candidate.flags & meaning ? candidateName : undefined; - } - } - - function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) { - - const valueDeclaration = prop && (prop.flags & SymbolFlags.ClassMember) && prop.valueDeclaration; - if (!valueDeclaration) { - return; - } - const hasPrivateModifier = hasModifier(valueDeclaration, ModifierFlags.Private); - const hasPrivateIdentifier = isNamedDeclaration(prop.valueDeclaration) && isPrivateIdentifier(prop.valueDeclaration.name); - if (!hasPrivateModifier && !hasPrivateIdentifier) { - return; - } - if (nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly) && !(prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor))) { - return; - } - - if (isThisAccess) { - // Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters). - const containingMethod = findAncestor(nodeForCheckWriteOnly, isFunctionLikeDeclaration); - if (containingMethod && containingMethod.symbol === prop) { - return; - } - } - - (getCheckFlags(prop) & CheckFlags.Instantiated ? getSymbolLinks(prop).target : prop)!.isReferenced = SymbolFlags.All; - } - - function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: __String): boolean { - switch (node.kind) { - case SyntaxKind.PropertyAccessExpression: - return isValidPropertyAccessWithType(node, node.expression.kind === SyntaxKind.SuperKeyword, propertyName, getWidenedType(checkExpression(node.expression))); - case SyntaxKind.QualifiedName: - return isValidPropertyAccessWithType(node, /*isSuper*/ false, propertyName, getWidenedType(checkExpression(node.left))); - case SyntaxKind.ImportType: - return isValidPropertyAccessWithType(node, /*isSuper*/ false, propertyName, getTypeFromTypeNode(node)); - } - } - - function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean { - return isValidPropertyAccessWithType(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type); - // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. - } - - function isValidPropertyAccessWithType( - node: PropertyAccessExpression | QualifiedName | ImportTypeNode, - isSuper: boolean, - propertyName: __String, - type: Type): boolean { - - if (type === errorType || isTypeAny(type)) { - return true; - } - const prop = getPropertyOfType(type, propertyName); - if (prop) { - if (isPropertyAccessExpression(node) && prop.valueDeclaration && isPrivateIdentifierPropertyDeclaration(prop.valueDeclaration)) { - const declClass = getContainingClass(prop.valueDeclaration); - return !isOptionalChain(node) && !!findAncestor(node, parent => parent === declClass); - } - return checkPropertyAccessibility(node, isSuper, type, prop); - } - // In js files properties of unions are allowed in completion - return isInJSFile(node) && (type.flags & TypeFlags.Union) !== 0 && (type).types.some(elementType => isValidPropertyAccessWithType(node, isSuper, propertyName, elementType)); - } - - /** - * Return the symbol of the for-in variable declared or referenced by the given for-in statement. - */ - function getForInVariableSymbol(node: ForInStatement): Symbol | undefined { - const initializer = node.initializer; - if (initializer.kind === SyntaxKind.VariableDeclarationList) { - const variable = (initializer).declarations[0]; - if (variable && !isBindingPattern(variable.name)) { - return getSymbolOfNode(variable); - } - } - else if (initializer.kind === SyntaxKind.Identifier) { - return getResolvedSymbol(initializer); - } - return undefined; - } - - /** - * Return true if the given type is considered to have numeric property names. - */ - function hasNumericPropertyNames(type: Type) { - return getIndexTypeOfType(type, IndexKind.Number) && !getIndexTypeOfType(type, IndexKind.String); - } - - /** - * Return true if given node is an expression consisting of an identifier (possibly parenthesized) - * that references a for-in variable for an object with numeric property names. - */ - function isForInVariableForNumericPropertyNames(expr: Expression) { - const e = skipParentheses(expr); - if (e.kind === SyntaxKind.Identifier) { - const symbol = getResolvedSymbol(e); - if (symbol.flags & SymbolFlags.Variable) { - let child: Node = expr; - let node = expr.parent; - while (node) { - if (node.kind === SyntaxKind.ForInStatement && - child === (node).statement && - getForInVariableSymbol(node) === symbol && - hasNumericPropertyNames(getTypeOfExpression((node).expression))) { - return true; - } - child = node; - node = node.parent; - } - } - } - return false; - } - - function checkIndexedAccess(node: ElementAccessExpression): Type { - return node.flags & NodeFlags.OptionalChain ? checkElementAccessChain(node as ElementAccessChain) : - checkElementAccessExpression(node, checkNonNullExpression(node.expression)); - } - - function checkElementAccessChain(node: ElementAccessChain) { - const exprType = checkExpression(node.expression); - const nonOptionalType = getOptionalExpressionType(exprType, node.expression); - return propagateOptionalTypeMarker(checkElementAccessExpression(node, checkNonNullType(nonOptionalType, node.expression)), node, nonOptionalType !== exprType); - } - - function checkElementAccessExpression(node: ElementAccessExpression, exprType: Type): Type { - const objectType = getAssignmentTargetKind(node) !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(exprType) : exprType; - const indexExpression = node.argumentExpression; - const indexType = checkExpression(indexExpression); - - if (objectType === errorType || objectType === silentNeverType) { - return objectType; - } - - if (isConstEnumObjectType(objectType) && !isStringLiteralLike(indexExpression)) { - error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal); - return errorType; - } - - const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType; - const accessFlags = isAssignmentTarget(node) ? - AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) : - AccessFlags.None; - const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType; - return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node); - } - - function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean { - if (expressionType === errorType) { - // There is already an error, so no need to report one. - return false; - } - - if (!isWellKnownSymbolSyntactically(expression)) { - return false; - } - - // Make sure the property type is the primitive symbol type - if ((expressionType.flags & TypeFlags.ESSymbolLike) === 0) { - if (reportError) { - error(expression, Diagnostics.A_computed_property_name_of_the_form_0_must_be_of_type_symbol, getTextOfNode(expression)); - } - return false; - } - - // The name is Symbol., so make sure Symbol actually resolves to the - // global Symbol object - const leftHandSide = (expression).expression; - const leftHandSideSymbol = getResolvedSymbol(leftHandSide); - if (!leftHandSideSymbol) { - return false; - } - - const globalESSymbol = getGlobalESSymbolConstructorSymbol(/*reportErrors*/ true); - if (!globalESSymbol) { - // Already errored when we tried to look up the symbol - return false; - } - - if (leftHandSideSymbol !== globalESSymbol) { - if (reportError) { - error(leftHandSide, Diagnostics.Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object); - } - return false; - } - - return true; - } - - function callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement { - return isCallOrNewExpression(node) || isTaggedTemplateExpression(node) || isJsxOpeningLikeElement(node); - } - - function resolveUntypedCall(node: CallLikeExpression): Signature { - if (callLikeExpressionMayHaveTypeArguments(node)) { - // Check type arguments even though we will give an error that untyped calls may not accept type arguments. - // This gets us diagnostics for the type arguments and marks them as referenced. - forEach(node.typeArguments, checkSourceElement); - } - - if (node.kind === SyntaxKind.TaggedTemplateExpression) { - checkExpression(node.template); - } - else if (isJsxOpeningLikeElement(node)) { - checkExpression(node.attributes); - } - else if (node.kind !== SyntaxKind.Decorator) { - forEach((node).arguments, argument => { - checkExpression(argument); - }); - } - return anySignature; - } - - function resolveErrorCall(node: CallLikeExpression): Signature { - resolveUntypedCall(node); - return unknownSignature; - } - - // Re-order candidate signatures into the result array. Assumes the result array to be empty. - // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order - // A nit here is that we reorder only signatures that belong to the same symbol, - // so order how inherited signatures are processed is still preserved. - // interface A { (x: string): void } - // interface B extends A { (x: 'foo'): string } - // const b: B; - // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] - function reorderCandidates(signatures: readonly Signature[], result: Signature[], callChainFlags: SignatureFlags): void { - let lastParent: Node | undefined; - let lastSymbol: Symbol | undefined; - let cutoffIndex = 0; - let index: number | undefined; - let specializedIndex = -1; - let spliceIndex: number; - Debug.assert(!result.length); - for (const signature of signatures) { - const symbol = signature.declaration && getSymbolOfNode(signature.declaration); - const parent = signature.declaration && signature.declaration.parent; - if (!lastSymbol || symbol === lastSymbol) { - if (lastParent && parent === lastParent) { - index = index! + 1; - } - else { - lastParent = parent; - index = cutoffIndex; - } - } - else { - // current declaration belongs to a different symbol - // set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex - index = cutoffIndex = result.length; - lastParent = parent; - } - lastSymbol = symbol; - - // specialized signatures always need to be placed before non-specialized signatures regardless - // of the cutoff position; see GH#1133 - if (signatureHasLiteralTypes(signature)) { - specializedIndex++; - spliceIndex = specializedIndex; - // The cutoff index always needs to be greater than or equal to the specialized signature index - // in order to prevent non-specialized signatures from being added before a specialized - // signature. - cutoffIndex++; - } - else { - spliceIndex = index; - } - - result.splice(spliceIndex, 0, callChainFlags ? getOptionalCallSignature(signature, callChainFlags) : signature); - } - } - - function isSpreadArgument(arg: Expression | undefined): arg is Expression { - return !!arg && (arg.kind === SyntaxKind.SpreadElement || arg.kind === SyntaxKind.SyntheticExpression && (arg).isSpread); - } - - function getSpreadArgumentIndex(args: readonly Expression[]): number { - return findIndex(args, isSpreadArgument); - } - - function acceptsVoid(t: Type): boolean { - return !!(t.flags & TypeFlags.Void); - } - - function hasCorrectArity(node: CallLikeExpression, args: readonly Expression[], signature: Signature, signatureHelpTrailingComma = false) { - let argCount: number; - let callIsIncomplete = false; // In incomplete call we want to be lenient when we have too few arguments - let effectiveParameterCount = getParameterCount(signature); - let effectiveMinimumArguments = getMinArgumentCount(signature); - - if (node.kind === SyntaxKind.TaggedTemplateExpression) { - argCount = args.length; - if (node.template.kind === SyntaxKind.TemplateExpression) { - // If a tagged template expression lacks a tail literal, the call is incomplete. - // Specifically, a template only can end in a TemplateTail or a Missing literal. - const lastSpan = last(node.template.templateSpans); // we should always have at least one span. - callIsIncomplete = nodeIsMissing(lastSpan.literal) || !!lastSpan.literal.isUnterminated; - } - else { - // If the template didn't end in a backtick, or its beginning occurred right prior to EOF, - // then this might actually turn out to be a TemplateHead in the future; - // so we consider the call to be incomplete. - const templateLiteral = node.template; - Debug.assert(templateLiteral.kind === SyntaxKind.NoSubstitutionTemplateLiteral); - callIsIncomplete = !!templateLiteral.isUnterminated; - } - } - else if (node.kind === SyntaxKind.Decorator) { - argCount = getDecoratorArgumentCount(node, signature); - } - else if (isJsxOpeningLikeElement(node)) { - callIsIncomplete = node.attributes.end === node.end; - if (callIsIncomplete) { - return true; - } - argCount = effectiveMinimumArguments === 0 ? args.length : 1; - effectiveParameterCount = args.length === 0 ? effectiveParameterCount : 1; // class may have argumentless ctor functions - still resolve ctor and compare vs props member type - effectiveMinimumArguments = Math.min(effectiveMinimumArguments, 1); // sfc may specify context argument - handled by framework and not typechecked - } - else { - if (!node.arguments) { - // This only happens when we have something of the form: 'new C' - Debug.assert(node.kind === SyntaxKind.NewExpression); - return getMinArgumentCount(signature) === 0; - } - - argCount = signatureHelpTrailingComma ? args.length + 1 : args.length; - - // If we are missing the close parenthesis, the call is incomplete. - callIsIncomplete = node.arguments.end === node.end; - - // If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range. - const spreadArgIndex = getSpreadArgumentIndex(args); - if (spreadArgIndex >= 0) { - return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature)); - } - } - - // Too many arguments implies incorrect arity. - if (!hasEffectiveRestParameter(signature) && argCount > effectiveParameterCount) { - return false; - } - - // If the call is incomplete, we should skip the lower bound check. - // JSX signatures can have extra parameters provided by the library which we don't check - if (callIsIncomplete || argCount >= effectiveMinimumArguments) { - return true; - } - for (let i = argCount; i < effectiveMinimumArguments; i++) { - const type = getTypeAtPosition(signature, i); - if (filterType(type, acceptsVoid).flags & TypeFlags.Never) { - return false; - } - } - return true; - } - - function hasCorrectTypeArgumentArity(signature: Signature, typeArguments: NodeArray | undefined) { - // If the user supplied type arguments, but the number of type arguments does not match - // the declared number of type parameters, the call has an incorrect arity. - const numTypeParameters = length(signature.typeParameters); - const minTypeArgumentCount = getMinTypeArgumentCount(signature.typeParameters); - return !some(typeArguments) || - (typeArguments.length >= minTypeArgumentCount && typeArguments.length <= numTypeParameters); - } - - // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. - function getSingleCallSignature(type: Type): Signature | undefined { - return getSingleSignature(type, SignatureKind.Call, /*allowMembers*/ false); - } - - function getSingleCallOrConstructSignature(type: Type): Signature | undefined { - return getSingleSignature(type, SignatureKind.Call, /*allowMembers*/ false) || - getSingleSignature(type, SignatureKind.Construct, /*allowMembers*/ false); - } - - function getSingleSignature(type: Type, kind: SignatureKind, allowMembers: boolean): Signature | undefined { - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type); - if (allowMembers || resolved.properties.length === 0 && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { - if (kind === SignatureKind.Call && resolved.callSignatures.length === 1 && resolved.constructSignatures.length === 0) { - return resolved.callSignatures[0]; - } - if (kind === SignatureKind.Construct && resolved.constructSignatures.length === 1 && resolved.callSignatures.length === 0) { - return resolved.constructSignatures[0]; - } - } - } - return undefined; - } - - // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) - function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, inferenceContext?: InferenceContext, compareTypes?: TypeComparer): Signature { - const context = createInferenceContext(signature.typeParameters!, signature, InferenceFlags.None, compareTypes); - // We clone the inferenceContext to avoid fixing. For example, when the source signature is (x: T) => T[] and - // the contextual signature is (...args: A) => B, we want to infer the element type of A's constraint (say 'any') - // for T but leave it possible to later infer '[any]' back to A. - const restType = getEffectiveRestType(contextualSignature); - const mapper = inferenceContext && (restType && restType.flags & TypeFlags.TypeParameter ? inferenceContext.nonFixingMapper : inferenceContext.mapper); - const sourceSignature = mapper ? instantiateSignature(contextualSignature, mapper) : contextualSignature; - applyToParameterTypes(sourceSignature, signature, (source, target) => { - // Type parameters from outer context referenced by source type are fixed by instantiation of the source type - inferTypes(context.inferences, source, target); - }); - if (!inferenceContext) { - applyToReturnTypes(contextualSignature, signature, (source, target) => { - inferTypes(context.inferences, source, target, InferencePriority.ReturnType); - }); - } - return getSignatureInstantiation(signature, getInferredTypes(context), isInJSFile(contextualSignature.declaration)); - } - - function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, checkMode: CheckMode, context: InferenceContext): Type[] { - const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, context, checkMode); - inferTypes(context.inferences, checkAttrType, paramType); - return getInferredTypes(context); - } - - function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: readonly Expression[], checkMode: CheckMode, context: InferenceContext): Type[] { - if (isJsxOpeningLikeElement(node)) { - return inferJsxTypeArguments(node, signature, checkMode, context); - } - - // If a contextual type is available, infer from that type to the return type of the call expression. For - // example, given a 'function wrap(cb: (x: T) => U): (x: T) => U' and a call expression - // 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the - // return type of 'wrap'. - if (node.kind !== SyntaxKind.Decorator) { - const contextualType = getContextualType(node); - if (contextualType) { - // We clone the inference context to avoid disturbing a resolution in progress for an - // outer call expression. Effectively we just want a snapshot of whatever has been - // inferred for any outer call expression so far. - const outerContext = getInferenceContext(node); - const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault)); - const instantiatedType = instantiateType(contextualType, outerMapper); - // If the contextual type is a generic function type with a single call signature, we - // instantiate the type with its own type parameters and type arguments. This ensures that - // the type parameters are not erased to type any during type inference such that they can - // be inferred as actual types from the contextual type. For example: - // declare function arrayMap(f: (x: T) => U): (a: T[]) => U[]; - // const boxElements: (a: A[]) => { value: A }[] = arrayMap(value => ({ value })); - // Above, the type of the 'value' parameter is inferred to be 'A'. - const contextualSignature = getSingleCallSignature(instantiatedType); - const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ? - getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) : - instantiatedType; - const inferenceTargetType = getReturnTypeOfSignature(signature); - // Inferences made from return types have lower priority than all other inferences. - inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType); - // Create a type mapper for instantiating generic contextual types using the inferences made - // from the return type. We need a separate inference pass here because (a) instantiation of - // the source type uses the outer context's return mapper (which excludes inferences made from - // outer arguments), and (b) we don't want any further inferences going into this context. - const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags); - const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper); - inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType); - context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined; - } - } - - const thisType = getThisTypeOfSignature(signature); - if (thisType) { - const thisArgumentNode = getThisArgumentOfCall(node); - const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType; - inferTypes(context.inferences, thisArgumentType, thisType); - } - - const restType = getNonArrayRestType(signature); - const argCount = restType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length; - for (let i = 0; i < argCount; i++) { - const arg = args[i]; - if (arg.kind !== SyntaxKind.OmittedExpression) { - const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); - inferTypes(context.inferences, argType, paramType); - } - } - - if (restType) { - const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, context); - inferTypes(context.inferences, spreadType, restType); - } - - return getInferredTypes(context); - } - - function getArrayifiedType(type: Type) { - return type.flags & TypeFlags.Union ? mapType(type, getArrayifiedType) : - type.flags & (TypeFlags.Any | TypeFlags.Instantiable) || isMutableArrayOrTuple(type) ? type : - isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.associatedNames) : - createArrayType(getIndexedAccessType(type, numberType)); - } - - function getSpreadArgumentType(args: readonly Expression[], index: number, argCount: number, restType: Type, context: InferenceContext | undefined) { - if (index >= argCount - 1) { - const arg = args[argCount - 1]; - if (isSpreadArgument(arg)) { - // We are inferring from a spread expression in the last argument position, i.e. both the parameter - // and the argument are ...x forms. - return arg.kind === SyntaxKind.SyntheticExpression ? - createArrayType((arg).type) : - getArrayifiedType(checkExpressionWithContextualType((arg).expression, restType, context, CheckMode.Normal)); - } - } - const types = []; - let spreadIndex = -1; - for (let i = index; i < argCount; i++) { - const contextualType = getIndexedAccessType(restType, getLiteralType(i - index)); - const argType = checkExpressionWithContextualType(args[i], contextualType, context, CheckMode.Normal); - if (spreadIndex < 0 && isSpreadArgument(args[i])) { - spreadIndex = i - index; - } - const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index); - types.push(hasPrimitiveContextualType ? getRegularTypeOfLiteralType(argType) : getWidenedLiteralType(argType)); - } - return spreadIndex < 0 ? - createTupleType(types) : - createTupleType(append(types.slice(0, spreadIndex), getUnionType(types.slice(spreadIndex))), spreadIndex, /*hasRestElement*/ true); - } - - function checkTypeArguments(signature: Signature, typeArgumentNodes: readonly TypeNode[], reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { - const isJavascript = isInJSFile(signature.declaration); - const typeParameters = signature.typeParameters!; - const typeArgumentTypes = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, getMinTypeArgumentCount(typeParameters), isJavascript); - let mapper: TypeMapper | undefined; - for (let i = 0; i < typeArgumentNodes.length; i++) { - Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments"); - const constraint = getConstraintOfTypeParameter(typeParameters[i]); - if (constraint) { - const errorInfo = reportErrors && headMessage ? (() => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1)) : undefined; - const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1; - if (!mapper) { - mapper = createTypeMapper(typeParameters, typeArgumentTypes); - } - const typeArgument = typeArgumentTypes[i]; - if (!checkTypeAssignableTo( - typeArgument, - getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument), - reportErrors ? typeArgumentNodes[i] : undefined, - typeArgumentHeadMessage, - errorInfo)) { - return undefined; - } - } - } - return typeArgumentTypes; - } - - function getJsxReferenceKind(node: JsxOpeningLikeElement): JsxReferenceKind { - if (isJsxIntrinsicIdentifier(node.tagName)) { - return JsxReferenceKind.Mixed; - } - const tagType = getApparentType(checkExpression(node.tagName)); - if (length(getSignaturesOfType(tagType, SignatureKind.Construct))) { - return JsxReferenceKind.Component; - } - if (length(getSignaturesOfType(tagType, SignatureKind.Call))) { - return JsxReferenceKind.Function; - } - return JsxReferenceKind.Mixed; - } - - /** - * Check if the given signature can possibly be a signature called by the JSX opening-like element. - * @param node a JSX opening-like element we are trying to figure its call signature - * @param signature a candidate signature we are trying whether it is a call signature - * @param relation a relationship to check parameter and argument type - */ - function checkApplicableSignatureForJsxOpeningLikeElement( - node: JsxOpeningLikeElement, - signature: Signature, - relation: Map, - checkMode: CheckMode, - reportErrors: boolean, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } - ) { - // Stateless function components can have maximum of three arguments: "props", "context", and "updater". - // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, - // can be specified by users through attributes property. - const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*inferenceContext*/ undefined, checkMode); - return checkTypeRelatedToAndOptionallyElaborate( - attributesType, - paramType, - relation, - reportErrors ? node.tagName : undefined, - node.attributes, - /*headMessage*/ undefined, - containingMessageChain, - errorOutputContainer); - } - - function getSignatureApplicabilityError( - node: CallLikeExpression, - args: readonly Expression[], - signature: Signature, - relation: Map, - checkMode: CheckMode, - reportErrors: boolean, - containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - ): readonly Diagnostic[] | undefined { - - const errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } = { errors: undefined, skipLogging: true }; - if (isJsxOpeningLikeElement(node)) { - if (!checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) { - Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors"); - return errorOutputContainer.errors || emptyArray; - } - return undefined; - } - const thisType = getThisTypeOfSignature(signature); - if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { - // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType - // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. - // If the expression is a new expression, then the check is skipped. - const thisArgumentNode = getThisArgumentOfCall(node); - let thisArgumentType: Type; - if (thisArgumentNode) { - thisArgumentType = checkExpression(thisArgumentNode); - if (isOptionalChainRoot(thisArgumentNode.parent)) { - thisArgumentType = getNonNullableType(thisArgumentType); - } - else if (isOptionalChain(thisArgumentNode.parent)) { - thisArgumentType = removeOptionalTypeMarker(thisArgumentType); - } - } - else { - thisArgumentType = voidType; - } - - const errorNode = reportErrors ? (thisArgumentNode || node) : undefined; - const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1; - if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) { - Debug.assert(!reportErrors || !!errorOutputContainer.errors, "this parameter should have errors when reporting errors"); - return errorOutputContainer.errors || emptyArray; - } - } - const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; - const restType = getNonArrayRestType(signature); - const argCount = restType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length; - for (let i = 0; i < argCount; i++) { - const arg = args[i]; - if (arg.kind !== SyntaxKind.OmittedExpression) { - const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType(arg, paramType, /*inferenceContext*/ undefined, checkMode); - // If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive), - // we obtain the regular type of any object literal arguments because we may not have inferred complete - // parameter types yet and therefore excess property checks may yield false positives (see #17041). - const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; - if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage, containingMessageChain, errorOutputContainer)) { - Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors"); - maybeAddMissingAwaitInfo(arg, checkArgType, paramType); - return errorOutputContainer.errors || emptyArray; - } - } - } - if (restType) { - const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, /*context*/ undefined); - const errorNode = reportErrors ? argCount < args.length ? args[argCount] : node : undefined; - if (!checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage, /*containingMessageChain*/ undefined, errorOutputContainer)) { - Debug.assert(!reportErrors || !!errorOutputContainer.errors, "rest parameter should have errors when reporting errors"); - maybeAddMissingAwaitInfo(errorNode, spreadType, restType); - return errorOutputContainer.errors || emptyArray; - } - } - return undefined; - - function maybeAddMissingAwaitInfo(errorNode: Node | undefined, source: Type, target: Type) { - if (errorNode && reportErrors && errorOutputContainer.errors && errorOutputContainer.errors.length) { - // Bail if target is Promise-like---something else is wrong - if (getAwaitedTypeOfPromise(target)) { - return; - } - const awaitedTypeOfSource = getAwaitedTypeOfPromise(source); - if (awaitedTypeOfSource && isTypeRelatedTo(awaitedTypeOfSource, target, relation)) { - addRelatedInfo(errorOutputContainer.errors[0], createDiagnosticForNode(errorNode, Diagnostics.Did_you_forget_to_use_await)); - } - } - } - } - - /** - * Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise. - */ - function getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression | undefined { - if (node.kind === SyntaxKind.CallExpression) { - const callee = skipOuterExpressions(node.expression); - if (isAccessExpression(callee)) { - return callee.expression; - } - } - } - - function createSyntheticExpression(parent: Node, type: Type, isSpread?: boolean) { - const result = createNode(SyntaxKind.SyntheticExpression, parent.pos, parent.end); - result.parent = parent; - result.type = type; - result.isSpread = isSpread || false; - return result; - } - - /** - * Returns the effective arguments for an expression that works like a function invocation. - */ - function getEffectiveCallArguments(node: CallLikeExpression): readonly Expression[] { - if (node.kind === SyntaxKind.TaggedTemplateExpression) { - const template = node.template; - const args: Expression[] = [createSyntheticExpression(template, getGlobalTemplateStringsArrayType())]; - if (template.kind === SyntaxKind.TemplateExpression) { - forEach(template.templateSpans, span => { - args.push(span.expression); - }); - } - return args; - } - if (node.kind === SyntaxKind.Decorator) { - return getEffectiveDecoratorArguments(node); - } - if (isJsxOpeningLikeElement(node)) { - return node.attributes.properties.length > 0 || (isJsxOpeningElement(node) && node.parent.children.length > 0) ? [node.attributes] : emptyArray; - } - const args = node.arguments || emptyArray; - const length = args.length; - if (length && isSpreadArgument(args[length - 1]) && getSpreadArgumentIndex(args) === length - 1) { - // We have a spread argument in the last position and no other spread arguments. If the type - // of the argument is a tuple type, spread the tuple elements into the argument list. We can - // call checkExpressionCached because spread expressions never have a contextual type. - const spreadArgument = args[length - 1]; - const type = flowLoopCount ? checkExpression(spreadArgument.expression) : checkExpressionCached(spreadArgument.expression); - if (isTupleType(type)) { - const typeArguments = getTypeArguments(type); - const restIndex = type.target.hasRestElement ? typeArguments.length - 1 : -1; - const syntheticArgs = map(typeArguments, (t, i) => createSyntheticExpression(spreadArgument, t, /*isSpread*/ i === restIndex)); - return concatenate(args.slice(0, length - 1), syntheticArgs); - } - } - return args; - } - - /** - * Returns the synthetic argument list for a decorator invocation. - */ - function getEffectiveDecoratorArguments(node: Decorator): readonly Expression[] { - const parent = node.parent; - const expr = node.expression; - switch (parent.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - // For a class decorator, the `target` is the type of the class (e.g. the - // "static" or "constructor" side of the class). - return [ - createSyntheticExpression(expr, getTypeOfSymbol(getSymbolOfNode(parent))) - ]; - case SyntaxKind.Parameter: - // A parameter declaration decorator will have three arguments (see - // `ParameterDecorator` in core.d.ts). - const func = parent.parent; - return [ - createSyntheticExpression(expr, parent.parent.kind === SyntaxKind.Constructor ? getTypeOfSymbol(getSymbolOfNode(func)) : errorType), - createSyntheticExpression(expr, anyType), - createSyntheticExpression(expr, numberType) - ]; - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // A method or accessor declaration decorator will have two or three arguments (see - // `PropertyDecorator` and `MethodDecorator` in core.d.ts). If we are emitting decorators - // for ES3, we will only pass two arguments. - const hasPropDesc = parent.kind !== SyntaxKind.PropertyDeclaration && languageVersion !== ScriptTarget.ES3; - return [ - createSyntheticExpression(expr, getParentTypeOfClassElement(parent)), - createSyntheticExpression(expr, getClassElementPropertyKeyType(parent)), - createSyntheticExpression(expr, hasPropDesc ? createTypedPropertyDescriptorType(getTypeOfNode(parent)) : anyType) - ]; - } - return Debug.fail(); - } - - /** - * Returns the argument count for a decorator node that works like a function invocation. - */ - function getDecoratorArgumentCount(node: Decorator, signature: Signature) { - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - return 1; - case SyntaxKind.PropertyDeclaration: - return 2; - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // For ES3 or decorators with only two parameters we supply only two arguments - return languageVersion === ScriptTarget.ES3 || signature.parameters.length <= 2 ? 2 : 3; - case SyntaxKind.Parameter: - return 3; - default: - return Debug.fail(); - } - } - function getDiagnosticSpanForCallNode(node: CallExpression, doNotIncludeArguments?: boolean) { - let start: number; - let length: number; - const sourceFile = getSourceFileOfNode(node); - - if (isPropertyAccessExpression(node.expression)) { - const nameSpan = getErrorSpanForNode(sourceFile, node.expression.name); - start = nameSpan.start; - length = doNotIncludeArguments ? nameSpan.length : node.end - start; - } - else { - const expressionSpan = getErrorSpanForNode(sourceFile, node.expression); - start = expressionSpan.start; - length = doNotIncludeArguments ? expressionSpan.length : node.end - start; - } - return { start, length, sourceFile }; - } - function getDiagnosticForCallNode(node: CallLikeExpression, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation { - if (isCallExpression(node)) { - const { sourceFile, start, length } = getDiagnosticSpanForCallNode(node); - return createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2, arg3); - } - else { - return createDiagnosticForNode(node, message, arg0, arg1, arg2, arg3); - } - } - - function getArgumentArityError(node: CallLikeExpression, signatures: readonly Signature[], args: readonly Expression[]) { - let min = Number.POSITIVE_INFINITY; - let max = Number.NEGATIVE_INFINITY; - let belowArgCount = Number.NEGATIVE_INFINITY; - let aboveArgCount = Number.POSITIVE_INFINITY; - - let argCount = args.length; - let closestSignature: Signature | undefined; - for (const sig of signatures) { - const minCount = getMinArgumentCount(sig); - const maxCount = getParameterCount(sig); - if (minCount < argCount && minCount > belowArgCount) belowArgCount = minCount; - if (argCount < maxCount && maxCount < aboveArgCount) aboveArgCount = maxCount; - if (minCount < min) { - min = minCount; - closestSignature = sig; - } - max = Math.max(max, maxCount); - } - - const hasRestParameter = some(signatures, hasEffectiveRestParameter); - const paramRange = hasRestParameter ? min : - min < max ? min + "-" + max : - min; - const hasSpreadArgument = getSpreadArgumentIndex(args) > -1; - if (argCount <= max && hasSpreadArgument) { - argCount--; - } - - let spanArray: NodeArray; - let related: DiagnosticWithLocation | undefined; - - const error = hasRestParameter || hasSpreadArgument ? hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more : - hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 : - Diagnostics.Expected_0_arguments_but_got_1_or_more : Diagnostics.Expected_0_arguments_but_got_1; - - if (closestSignature && getMinArgumentCount(closestSignature) > argCount && closestSignature.declaration) { - const paramDecl = closestSignature.declaration.parameters[closestSignature.thisParameter ? argCount + 1 : argCount]; - if (paramDecl) { - related = createDiagnosticForNode( - paramDecl, - isBindingPattern(paramDecl.name) ? Diagnostics.An_argument_matching_this_binding_pattern_was_not_provided : Diagnostics.An_argument_for_0_was_not_provided, - !paramDecl.name ? argCount : !isBindingPattern(paramDecl.name) ? idText(getFirstIdentifier(paramDecl.name)) : undefined - ); - } - } - if (min < argCount && argCount < max) { - return getDiagnosticForCallNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount); - } - - if (!hasSpreadArgument && argCount < min) { - const diagnostic = getDiagnosticForCallNode(node, error, paramRange, argCount); - return related ? addRelatedInfo(diagnostic, related) : diagnostic; - } - - if (hasRestParameter || hasSpreadArgument) { - spanArray = createNodeArray(args); - if (hasSpreadArgument && argCount) { - const nextArg = elementAt(args, getSpreadArgumentIndex(args) + 1) || undefined; - spanArray = createNodeArray(args.slice(max > argCount && nextArg ? args.indexOf(nextArg) : Math.min(max, args.length - 1))); - } - } - else { - spanArray = createNodeArray(args.slice(max)); - } - - spanArray.pos = first(spanArray).pos; - spanArray.end = last(spanArray).end; - if (spanArray.end === spanArray.pos) { - spanArray.end++; - } - const diagnostic = createDiagnosticForNodeArray( - getSourceFileOfNode(node), spanArray, error, paramRange, argCount); - return related ? addRelatedInfo(diagnostic, related) : diagnostic; - } - - function getTypeArgumentArityError(node: Node, signatures: readonly Signature[], typeArguments: NodeArray) { - const argCount = typeArguments.length; - // No overloads exist - if (signatures.length === 1) { - const sig = signatures[0]; - const min = getMinTypeArgumentCount(sig.typeParameters); - const max = length(sig.typeParameters); - return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, min < max ? min + "-" + max : min , argCount); - } - // Overloads exist - let belowArgCount = -Infinity; - let aboveArgCount = Infinity; - for (const sig of signatures) { - const min = getMinTypeArgumentCount(sig.typeParameters); - const max = length(sig.typeParameters); - if (min > argCount) { - aboveArgCount = Math.min(aboveArgCount, min); - } - else if (max < argCount) { - belowArgCount = Math.max(belowArgCount, max); - } - } - if (belowArgCount !== -Infinity && aboveArgCount !== Infinity) { - return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.No_overload_expects_0_type_arguments_but_overloads_do_exist_that_expect_either_1_or_2_type_arguments, argCount, belowArgCount, aboveArgCount); - } - return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount); - } - - function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, callChainFlags: SignatureFlags, fallbackError?: DiagnosticMessage): Signature { - const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression; - const isDecorator = node.kind === SyntaxKind.Decorator; - const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node); - const reportErrors = !candidatesOutArray; - - let typeArguments: NodeArray | undefined; - - if (!isDecorator) { - typeArguments = (node).typeArguments; - - // We already perform checking on the type arguments on the class declaration itself. - if (isTaggedTemplate || isJsxOpeningOrSelfClosingElement || (node).expression.kind !== SyntaxKind.SuperKeyword) { - forEach(typeArguments, checkSourceElement); - } - } - - const candidates = candidatesOutArray || []; - // reorderCandidates fills up the candidates array directly - reorderCandidates(signatures, candidates, callChainFlags); - if (!candidates.length) { - if (reportErrors) { - diagnostics.add(getDiagnosticForCallNode(node, Diagnostics.Call_target_does_not_contain_any_signatures)); - } - return resolveErrorCall(node); - } - - const args = getEffectiveCallArguments(node); - - // The excludeArgument array contains true for each context sensitive argument (an argument - // is context sensitive it is susceptible to a one-time permanent contextual typing). - // - // The idea is that we will perform type argument inference & assignability checking once - // without using the susceptible parameters that are functions, and once more for those - // parameters, contextually typing each as we go along. - // - // For a tagged template, then the first argument be 'undefined' if necessary because it - // represents a TemplateStringsArray. - // - // For a decorator, no arguments are susceptible to contextual typing due to the fact - // decorators are applied to a declaration by the emitter, and not to an expression. - const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters; - let argCheckMode = !isDecorator && !isSingleNonGenericCandidate && some(args, isContextSensitive) ? CheckMode.SkipContextSensitive : CheckMode.Normal; - - // The following variables are captured and modified by calls to chooseOverload. - // If overload resolution or type argument inference fails, we want to report the - // best error possible. The best error is one which says that an argument was not - // assignable to a parameter. This implies that everything else about the overload - // was fine. So if there is any overload that is only incorrect because of an - // argument, we will report an error on that one. - // - // function foo(s: string): void; - // function foo(n: number): void; // Report argument error on this overload - // function foo(): void; - // foo(true); - // - // If none of the overloads even made it that far, there are two possibilities. - // There was a problem with type arguments for some overload, in which case - // report an error on that. Or none of the overloads even had correct arity, - // in which case give an arity error. - // - // function foo(x: T): void; // Report type argument error - // function foo(): void; - // foo(0); - // - let candidatesForArgumentError: Signature[] | undefined; - let candidateForArgumentArityError: Signature | undefined; - let candidateForTypeArgumentError: Signature | undefined; - let result: Signature | undefined; - - // If we are in signature help, a trailing comma indicates that we intend to provide another argument, - // so we will only accept overloads with arity at least 1 higher than the current number of provided arguments. - const signatureHelpTrailingComma = - !!(checkMode & CheckMode.IsForSignatureHelp) && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma; - - // Section 4.12.1: - // if the candidate list contains one or more signatures for which the type of each argument - // expression is a subtype of each corresponding parameter type, the return type of the first - // of those signatures becomes the return type of the function call. - // Otherwise, the return type of the first signature in the candidate list becomes the return - // type of the function call. - // - // Whether the call is an error is determined by assignability of the arguments. The subtype pass - // is just important for choosing the best signature. So in the case where there is only one - // signature, the subtype pass is useless. So skipping it is an optimization. - if (candidates.length > 1) { - result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); - } - if (!result) { - result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma); - } - if (result) { - return result; - } - - // No signatures were applicable. Now report errors based on the last applicable signature with - // no arguments excluded from assignability checks. - // If candidate is undefined, it means that no candidates had a suitable arity. In that case, - // skip the checkApplicableSignature check. - if (reportErrors) { - if (candidatesForArgumentError) { - if (candidatesForArgumentError.length === 1 || candidatesForArgumentError.length > 3) { - const last = candidatesForArgumentError[candidatesForArgumentError.length - 1]; - let chain: DiagnosticMessageChain | undefined; - if (candidatesForArgumentError.length > 3) { - chain = chainDiagnosticMessages(chain, Diagnostics.The_last_overload_gave_the_following_error); - chain = chainDiagnosticMessages(chain, Diagnostics.No_overload_matches_this_call); - } - const diags = getSignatureApplicabilityError(node, args, last, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, () => chain); - if (diags) { - for (const d of diags) { - if (last.declaration && candidatesForArgumentError.length > 3) { - addRelatedInfo(d, createDiagnosticForNode(last.declaration, Diagnostics.The_last_overload_is_declared_here)); - } - diagnostics.add(d); - } - } - else { - Debug.fail("No error for last overload signature"); - } - } - else { - const allDiagnostics: (readonly DiagnosticRelatedInformation[])[] = []; - let max = 0; - let min = Number.MAX_VALUE; - let minIndex = 0; - let i = 0; - for (const c of candidatesForArgumentError) { - const chain = () => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Overload_0_of_1_2_gave_the_following_error, i + 1, candidates.length, signatureToString(c)); - const diags = getSignatureApplicabilityError(node, args, c, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, chain); - if (diags) { - if (diags.length <= min) { - min = diags.length; - minIndex = i; - } - max = Math.max(max, diags.length); - allDiagnostics.push(diags); - } - else { - Debug.fail("No error for 3 or fewer overload signatures"); - } - i++; - } - - const diags = max > 1 ? allDiagnostics[minIndex] : flatten(allDiagnostics); - Debug.assert(diags.length > 0, "No errors reported for 3 or fewer overload signatures"); - const chain = chainDiagnosticMessages( - map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText), - Diagnostics.No_overload_matches_this_call); - const related = flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[]; - if (every(diags, d => d.start === diags[0].start && d.length === diags[0].length && d.file === diags[0].file)) { - const { file, start, length } = diags[0]; - diagnostics.add({ file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related }); - } - else { - diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chain, related)); - } - } - } - else if (candidateForArgumentArityError) { - diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args)); - } - else if (candidateForTypeArgumentError) { - checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression | JsxOpeningLikeElement).typeArguments!, /*reportErrors*/ true, fallbackError); - } - else { - const signaturesWithCorrectTypeArgumentArity = filter(signatures, s => hasCorrectTypeArgumentArity(s, typeArguments)); - if (signaturesWithCorrectTypeArgumentArity.length === 0) { - diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!)); - } - else if (!isDecorator) { - diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args)); - } - else if (fallbackError) { - diagnostics.add(getDiagnosticForCallNode(node, fallbackError)); - } - } - } - - return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); - - function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { - candidatesForArgumentError = undefined; - candidateForArgumentArityError = undefined; - candidateForTypeArgumentError = undefined; - - if (isSingleNonGenericCandidate) { - const candidate = candidates[0]; - if (some(typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { - return undefined; - } - if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { - candidatesForArgumentError = [candidate]; - return undefined; - } - return candidate; - } - - for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) { - const candidate = candidates[candidateIndex]; - if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { - continue; - } - - let checkCandidate: Signature; - let inferenceContext: InferenceContext | undefined; - - if (candidate.typeParameters) { - let typeArgumentTypes: Type[] | undefined; - if (some(typeArguments)) { - typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false); - if (!typeArgumentTypes) { - candidateForTypeArgumentError = candidate; - continue; - } - } - else { - inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext); - argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal; - } - checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { - candidateForArgumentArityError = checkCandidate; - continue; - } - } - else { - checkCandidate = candidate; - } - if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); - continue; - } - if (argCheckMode) { - // If one or more context sensitive arguments were excluded, we start including - // them now (and keeping do so for any subsequent candidates) and perform a second - // round of type inference and applicability checking for this particular candidate. - argCheckMode = CheckMode.Normal; - if (inferenceContext) { - const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext); - checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { - candidateForArgumentArityError = checkCandidate; - continue; - } - } - if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); - continue; - } - } - candidates[candidateIndex] = checkCandidate; - return checkCandidate; - } - - return undefined; - } - } - - // No signature was applicable. We have already reported the errors for the invalid signature. - // If this is a type resolution session, e.g. Language Service, try to get better information than anySignature. - function getCandidateForOverloadFailure( - node: CallLikeExpression, - candidates: Signature[], - args: readonly Expression[], - hasCandidatesOutArray: boolean, - ): Signature { - Debug.assert(candidates.length > 0); // Else should not have called this. - // Normally we will combine overloads. Skip this if they have type parameters since that's hard to combine. - // Don't do this if there is a `candidatesOutArray`, - // because then we want the chosen best candidate to be one of the overloads, not a combination. - return hasCandidatesOutArray || candidates.length === 1 || candidates.some(c => !!c.typeParameters) - ? pickLongestCandidateSignature(node, candidates, args) - : createUnionOfSignaturesForOverloadFailure(candidates); - } - - function createUnionOfSignaturesForOverloadFailure(candidates: readonly Signature[]): Signature { - const thisParameters = mapDefined(candidates, c => c.thisParameter); - let thisParameter: Symbol | undefined; - if (thisParameters.length) { - thisParameter = createCombinedSymbolFromTypes(thisParameters, thisParameters.map(getTypeOfParameter)); - } - const { min: minArgumentCount, max: maxNonRestParam } = minAndMax(candidates, getNumNonRestParameters); - const parameters: Symbol[] = []; - for (let i = 0; i < maxNonRestParam; i++) { - const symbols = mapDefined(candidates, s => signatureHasRestParameter(s) ? - i < s.parameters.length - 1 ? s.parameters[i] : last(s.parameters) : - i < s.parameters.length ? s.parameters[i] : undefined); - Debug.assert(symbols.length !== 0); - parameters.push(createCombinedSymbolFromTypes(symbols, mapDefined(candidates, candidate => tryGetTypeAtPosition(candidate, i)))); - } - const restParameterSymbols = mapDefined(candidates, c => signatureHasRestParameter(c) ? last(c.parameters) : undefined); - let flags = SignatureFlags.None; - if (restParameterSymbols.length !== 0) { - const type = createArrayType(getUnionType(mapDefined(candidates, tryGetRestTypeOfSignature), UnionReduction.Subtype)); - parameters.push(createCombinedSymbolForOverloadFailure(restParameterSymbols, type)); - flags |= SignatureFlags.HasRestParameter; - } - if (candidates.some(signatureHasLiteralTypes)) { - flags |= SignatureFlags.HasLiteralTypes; - } - return createSignature( - candidates[0].declaration, - /*typeParameters*/ undefined, // Before calling this we tested for `!candidates.some(c => !!c.typeParameters)`. - thisParameter, - parameters, - /*resolvedReturnType*/ getIntersectionType(candidates.map(getReturnTypeOfSignature)), - /*typePredicate*/ undefined, - minArgumentCount, - flags); - } - - function getNumNonRestParameters(signature: Signature): number { - const numParams = signature.parameters.length; - return signatureHasRestParameter(signature) ? numParams - 1 : numParams; - } - - function createCombinedSymbolFromTypes(sources: readonly Symbol[], types: Type[]): Symbol { - return createCombinedSymbolForOverloadFailure(sources, getUnionType(types, UnionReduction.Subtype)); - } - - function createCombinedSymbolForOverloadFailure(sources: readonly Symbol[], type: Type): Symbol { - // This function is currently only used for erroneous overloads, so it's good enough to just use the first source. - return createSymbolWithType(first(sources), type); - } - - function pickLongestCandidateSignature(node: CallLikeExpression, candidates: Signature[], args: readonly Expression[]): Signature { - // Pick the longest signature. This way we can get a contextual type for cases like: - // declare function f(a: { xa: number; xb: number; }, b: number); - // f({ | - // Also, use explicitly-supplied type arguments if they are provided, so we can get a contextual signature in cases like: - // declare function f(k: keyof T); - // f(" - const bestIndex = getLongestCandidateIndex(candidates, apparentArgumentCount === undefined ? args.length : apparentArgumentCount); - const candidate = candidates[bestIndex]; - const { typeParameters } = candidate; - if (!typeParameters) { - return candidate; - } - - const typeArgumentNodes: readonly TypeNode[] | undefined = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments : undefined; - const instantiated = typeArgumentNodes - ? createSignatureInstantiation(candidate, getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters, isInJSFile(node))) - : inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args); - candidates[bestIndex] = instantiated; - return instantiated; - } - - function getTypeArgumentsFromNodes(typeArgumentNodes: readonly TypeNode[], typeParameters: readonly TypeParameter[], isJs: boolean): readonly Type[] { - const typeArguments = typeArgumentNodes.map(getTypeOfNode); - while (typeArguments.length > typeParameters.length) { - typeArguments.pop(); - } - while (typeArguments.length < typeParameters.length) { - typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs)); - } - return typeArguments; - } - - function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: readonly TypeParameter[], candidate: Signature, args: readonly Expression[]): Signature { - const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - const typeArgumentTypes = inferTypeArguments(node, candidate, args, CheckMode.SkipContextSensitive | CheckMode.SkipGenericFunctions, inferenceContext); - return createSignatureInstantiation(candidate, typeArgumentTypes); - } - - function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number { - let maxParamsIndex = -1; - let maxParams = -1; - - for (let i = 0; i < candidates.length; i++) { - const candidate = candidates[i]; - const paramCount = getParameterCount(candidate); - if (hasEffectiveRestParameter(candidate) || paramCount >= argsCount) { - return i; - } - if (paramCount > maxParams) { - maxParams = paramCount; - maxParamsIndex = i; - } - } - - return maxParamsIndex; - } - - function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - if (node.expression.kind === SyntaxKind.SuperKeyword) { - const superType = checkSuperExpression(node.expression); - if (isTypeAny(superType)) { - for (const arg of node.arguments) { - checkExpression(arg); // Still visit arguments so they get marked for visibility, etc - } - return anySignature; - } - if (superType !== errorType) { - // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated - // with the type arguments specified in the extends clause. - const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!); - if (baseTypeNode) { - const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode); - return resolveCall(node, baseConstructors, candidatesOutArray, checkMode, SignatureFlags.None); - } - } - return resolveUntypedCall(node); - } - - let callChainFlags: SignatureFlags; - let funcType = checkExpression(node.expression); - if (isCallChain(node)) { - const nonOptionalType = getOptionalExpressionType(funcType, node.expression); - callChainFlags = nonOptionalType === funcType ? SignatureFlags.None : - isOutermostOptionalChain(node) ? SignatureFlags.IsOuterCallChain : - SignatureFlags.IsInnerCallChain; - funcType = nonOptionalType; - } - else { - callChainFlags = SignatureFlags.None; - } - - funcType = checkNonNullTypeWithReporter( - funcType, - node.expression, - reportCannotInvokePossiblyNullOrUndefinedError - ); - - if (funcType === silentNeverType) { - return silentNeverSignature; - } - - const apparentType = getApparentType(funcType); - if (apparentType === errorType) { - // Another error has already been reported - return resolveErrorCall(node); - } - - // Technically, this signatures list may be incomplete. We are taking the apparent type, - // but we are not including call signatures that may have been added to the Object or - // Function interface, since they have none by default. This is a bit of a leap of faith - // that the user will not add any. - const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - const numConstructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct).length; - - // TS 1.0 Spec: 4.12 - // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual - // types are provided for the argument expressions, and the result is always of type Any. - if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, numConstructSignatures)) { - // The unknownType indicates that an error already occurred (and was reported). No - // need to report another error in this case. - if (funcType !== errorType && node.typeArguments) { - error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); - } - return resolveUntypedCall(node); - } - // If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call. - // TypeScript employs overload resolution in typed function calls in order to support functions - // with multiple call signatures. - if (!callSignatures.length) { - if (numConstructSignatures) { - error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); - } - else { - let relatedInformation: DiagnosticRelatedInformation | undefined; - if (node.arguments.length === 1) { - const text = getSourceFileOfNode(node).text; - if (isLineBreak(text.charCodeAt(skipTrivia(text, node.expression.end, /* stopAfterLineBreak */ true) - 1))) { - relatedInformation = createDiagnosticForNode(node.expression, Diagnostics.It_is_highly_likely_that_you_are_missing_a_semicolon); - } - } - invocationError(node.expression, apparentType, SignatureKind.Call, relatedInformation); - } - return resolveErrorCall(node); - } - // When a call to a generic function is an argument to an outer call to a generic function for which - // inference is in process, we have a choice to make. If the inner call relies on inferences made from - // its contextual type to its return type, deferring the inner call processing allows the best possible - // contextual type to accumulate. But if the outer call relies on inferences made from the return type of - // the inner call, the inner call should be processed early. There's no sure way to know which choice is - // right (only a full unification algorithm can determine that), so we resort to the following heuristic: - // If no type arguments are specified in the inner call and at least one call signature is generic and - // returns a function type, we choose to defer processing. This narrowly permits function composition - // operators to flow inferences through return types, but otherwise processes calls right away. We - // use the resolvingSignature singleton to indicate that we deferred processing. This result will be - // propagated out and eventually turned into nonInferrableType (a type that is assignable to anything and - // from which we never make inferences). - if (checkMode & CheckMode.SkipGenericFunctions && !node.typeArguments && callSignatures.some(isGenericFunctionReturningFunction)) { - skippedGenericFunction(node, checkMode); - return resolvingSignature; - } - // If the function is explicitly marked with `@class`, then it must be constructed. - if (callSignatures.some(sig => isInJSFile(sig.declaration) && !!getJSDocClassTag(sig.declaration!))) { - error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); - return resolveErrorCall(node); - } - - return resolveCall(node, callSignatures, candidatesOutArray, checkMode, callChainFlags); - } - - function isGenericFunctionReturningFunction(signature: Signature) { - return !!(signature.typeParameters && isFunctionType(getReturnTypeOfSignature(signature))); - } - - /** - * TS 1.0 spec: 4.12 - * If FuncExpr is of type Any, or of an object type that has no call or construct signatures - * but is a subtype of the Function interface, the call is an untyped function call. - */ - function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number): boolean { - // We exclude union types because we may have a union of function types that happen to have no common signatures. - return isTypeAny(funcType) || isTypeAny(apparentFuncType) && !!(funcType.flags & TypeFlags.TypeParameter) || - !numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType); - } - - function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - if (node.arguments && languageVersion < ScriptTarget.ES5) { - const spreadIndex = getSpreadArgumentIndex(node.arguments); - if (spreadIndex >= 0) { - error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher); - } - } - - let expressionType = checkNonNullExpression(node.expression); - if (expressionType === silentNeverType) { - return silentNeverSignature; - } - - // If expressionType's apparent type(section 3.8.1) is an object type with one or - // more construct signatures, the expression is processed in the same manner as a - // function call, but using the construct signatures as the initial set of candidate - // signatures for overload resolution. The result type of the function call becomes - // the result type of the operation. - expressionType = getApparentType(expressionType); - if (expressionType === errorType) { - // Another error has already been reported - return resolveErrorCall(node); - } - - // TS 1.0 spec: 4.11 - // If expressionType is of type Any, Args can be any argument - // list and the result of the operation is of type Any. - if (isTypeAny(expressionType)) { - if (node.typeArguments) { - error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); - } - return resolveUntypedCall(node); - } - - // Technically, this signatures list may be incomplete. We are taking the apparent type, - // but we are not including construct signatures that may have been added to the Object or - // Function interface, since they have none by default. This is a bit of a leap of faith - // that the user will not add any. - const constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct); - if (constructSignatures.length) { - if (!isConstructorAccessible(node, constructSignatures[0])) { - return resolveErrorCall(node); - } - // If the expression is a class of abstract type, then it cannot be instantiated. - // Note, only class declarations can be declared abstract. - // In the case of a merged class-module or class-interface declaration, - // only the class declaration node will have the Abstract flag set. - const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol); - if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) { - error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class); - return resolveErrorCall(node); - } - - return resolveCall(node, constructSignatures, candidatesOutArray, checkMode, SignatureFlags.None); - } - - // If expressionType's apparent type is an object type with no construct signatures but - // one or more call signatures, the expression is processed as a function call. A compile-time - // error occurs if the result of the function call is not Void. The type of the result of the - // operation is Any. It is an error to have a Void this type. - const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); - if (callSignatures.length) { - const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlags.None); - if (!noImplicitAny) { - if (signature.declaration && !isJSConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) { - error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); - } - if (getThisTypeOfSignature(signature) === voidType) { - error(node, Diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void); - } - } - return signature; - } - - invocationError(node.expression, expressionType, SignatureKind.Construct); - return resolveErrorCall(node); - } - - function typeHasProtectedAccessibleBase(target: Symbol, type: InterfaceType): boolean { - const baseTypes = getBaseTypes(type); - if (!length(baseTypes)) { - return false; - } - const firstBase = baseTypes[0]; - if (firstBase.flags & TypeFlags.Intersection) { - const types = (firstBase as IntersectionType).types; - const mixinFlags = findMixins(types); - let i = 0; - for (const intersectionMember of (firstBase as IntersectionType).types) { - // We want to ignore mixin ctors - if (!mixinFlags[i]) { - if (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) { - if (intersectionMember.symbol === target) { - return true; - } - if (typeHasProtectedAccessibleBase(target, intersectionMember as InterfaceType)) { - return true; - } - } - } - i++; - } - return false; - } - if (firstBase.symbol === target) { - return true; - } - return typeHasProtectedAccessibleBase(target, firstBase as InterfaceType); - } - - function isConstructorAccessible(node: NewExpression, signature: Signature) { - if (!signature || !signature.declaration) { - return true; - } - - const declaration = signature.declaration; - const modifiers = getSelectedModifierFlags(declaration, ModifierFlags.NonPublicAccessibilityModifier); - - // (1) Public constructors and (2) constructor functions are always accessible. - if (!modifiers || declaration.kind !== SyntaxKind.Constructor) { - return true; - } - - const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(declaration.parent.symbol)!; - const declaringClass = getDeclaredTypeOfSymbol(declaration.parent.symbol); - - // A private or protected constructor can only be instantiated within its own class (or a subclass, for protected) - if (!isNodeWithinClass(node, declaringClassDeclaration)) { - const containingClass = getContainingClass(node); - if (containingClass && modifiers & ModifierFlags.Protected) { - const containingType = getTypeOfNode(containingClass); - if (typeHasProtectedAccessibleBase(declaration.parent.symbol, containingType as InterfaceType)) { - return true; - } - } - if (modifiers & ModifierFlags.Private) { - error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass)); - } - if (modifiers & ModifierFlags.Protected) { - error(node, Diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, typeToString(declaringClass)); - } - return false; - } - - return true; - } - - function invocationErrorDetails(apparentType: Type, kind: SignatureKind): { messageChain: DiagnosticMessageChain, relatedMessage: DiagnosticMessage | undefined } { - let errorInfo: DiagnosticMessageChain | undefined; - const isCall = kind === SignatureKind.Call; - const awaitedType = getAwaitedType(apparentType); - const maybeMissingAwait = awaitedType && getSignaturesOfType(awaitedType, kind).length > 0; - if (apparentType.flags & TypeFlags.Union) { - const types = (apparentType as UnionType).types; - let hasSignatures = false; - for (const constituent of types) { - const signatures = getSignaturesOfType(constituent, kind); - if (signatures.length !== 0) { - hasSignatures = true; - if (errorInfo) { - // Bail early if we already have an error, no chance of "No constituent of type is callable" - break; - } - } - else { - // Error on the first non callable constituent only - if (!errorInfo) { - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall ? - Diagnostics.Type_0_has_no_call_signatures : - Diagnostics.Type_0_has_no_construct_signatures, - typeToString(constituent) - ); - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall ? - Diagnostics.Not_all_constituents_of_type_0_are_callable : - Diagnostics.Not_all_constituents_of_type_0_are_constructable, - typeToString(apparentType) - ); - } - if (hasSignatures) { - // Bail early if we already found a siganture, no chance of "No constituent of type is callable" - break; - } - } - } - if (!hasSignatures) { - errorInfo = chainDiagnosticMessages( - /* detials */ undefined, - isCall ? - Diagnostics.No_constituent_of_type_0_is_callable : - Diagnostics.No_constituent_of_type_0_is_constructable, - typeToString(apparentType) - ); - } - if (!errorInfo) { - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall ? - Diagnostics.Each_member_of_the_union_type_0_has_signatures_but_none_of_those_signatures_are_compatible_with_each_other : - Diagnostics.Each_member_of_the_union_type_0_has_construct_signatures_but_none_of_those_signatures_are_compatible_with_each_other, - typeToString(apparentType) - ); - } - } - else { - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall ? - Diagnostics.Type_0_has_no_call_signatures : - Diagnostics.Type_0_has_no_construct_signatures, - typeToString(apparentType) - ); - } - return { - messageChain: chainDiagnosticMessages( - errorInfo, - isCall ? Diagnostics.This_expression_is_not_callable : Diagnostics.This_expression_is_not_constructable - ), - relatedMessage: maybeMissingAwait ? Diagnostics.Did_you_forget_to_use_await : undefined, - }; - } - function invocationError(errorTarget: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) { - const { messageChain, relatedMessage: relatedInfo } = invocationErrorDetails(apparentType, kind); - const diagnostic = createDiagnosticForNodeFromMessageChain(errorTarget, messageChain); - if (relatedInfo) { - addRelatedInfo(diagnostic, createDiagnosticForNode(errorTarget, relatedInfo)); - } - if (isCallExpression(errorTarget.parent)) { - const { start, length } = getDiagnosticSpanForCallNode(errorTarget.parent, /* doNotIncludeArguments */ true); - diagnostic.start = start; - diagnostic.length = length; - } - diagnostics.add(diagnostic); - invocationErrorRecovery(apparentType, kind, relatedInformation ? addRelatedInfo(diagnostic, relatedInformation) : diagnostic); - } - - function invocationErrorRecovery(apparentType: Type, kind: SignatureKind, diagnostic: Diagnostic) { - if (!apparentType.symbol) { - return; - } - const importNode = getSymbolLinks(apparentType.symbol).originatingImport; - // Create a diagnostic on the originating import if possible onto which we can attach a quickfix - // An import call expression cannot be rewritten into another form to correct the error - the only solution is to use `.default` at the use-site - if (importNode && !isImportCall(importNode)) { - const sigs = getSignaturesOfType(getTypeOfSymbol(getSymbolLinks(apparentType.symbol).target!), kind); - if (!sigs || !sigs.length) return; - - addRelatedInfo(diagnostic, - createDiagnosticForNode(importNode, Diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead) - ); - } - } - - function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - const tagType = checkExpression(node.tag); - const apparentType = getApparentType(tagType); - - if (apparentType === errorType) { - // Another error has already been reported - return resolveErrorCall(node); - } - - const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - const numConstructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct).length; - - if (isUntypedFunctionCall(tagType, apparentType, callSignatures.length, numConstructSignatures)) { - return resolveUntypedCall(node); - } - - if (!callSignatures.length) { - invocationError(node.tag, apparentType, SignatureKind.Call); - return resolveErrorCall(node); - } - - return resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlags.None); - } - - /** - * Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression. - */ - function getDiagnosticHeadMessageForDecoratorResolution(node: Decorator) { - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - return Diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression; - - case SyntaxKind.Parameter: - return Diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression; - - case SyntaxKind.PropertyDeclaration: - return Diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return Diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression; - - default: - return Debug.fail(); - } - } - - /** - * Resolves a decorator as if it were a call expression. - */ - function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - const funcType = checkExpression(node.expression); - const apparentType = getApparentType(funcType); - if (apparentType === errorType) { - return resolveErrorCall(node); - } - - const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - const numConstructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct).length; - if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, numConstructSignatures)) { - return resolveUntypedCall(node); - } - - if (isPotentiallyUncalledDecorator(node, callSignatures)) { - const nodeStr = getTextOfNode(node.expression, /*includeTrivia*/ false); - error(node, Diagnostics._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0, nodeStr); - return resolveErrorCall(node); - } - - const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node); - if (!callSignatures.length) { - const errorDetails = invocationErrorDetails(apparentType, SignatureKind.Call); - const messageChain = chainDiagnosticMessages(errorDetails.messageChain, headMessage); - const diag = createDiagnosticForNodeFromMessageChain(node.expression, messageChain); - if (errorDetails.relatedMessage) { - addRelatedInfo(diag, createDiagnosticForNode(node.expression, errorDetails.relatedMessage)); - } - diagnostics.add(diag); - invocationErrorRecovery(apparentType, SignatureKind.Call, diag); - return resolveErrorCall(node); - } - - return resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlags.None, headMessage); - } - - function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature { - const namespace = getJsxNamespaceAt(node); - const exports = namespace && getExportsOfSymbol(namespace); - // We fake up a SFC signature for each intrinsic, however a more specific per-element signature drawn from the JSX declaration - // file would probably be preferable. - const typeSymbol = exports && getSymbol(exports, JsxNames.Element, SymbolFlags.Type); - const returnNode = typeSymbol && nodeBuilder.symbolToEntityName(typeSymbol, SymbolFlags.Type, node); - const declaration = createFunctionTypeNode(/*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotdotdot*/ undefined, "props", /*questionMark*/ undefined, nodeBuilder.typeToTypeNode(result, node))], - returnNode ? createTypeReferenceNode(returnNode, /*typeArguments*/ undefined) : createKeywordTypeNode(SyntaxKind.AnyKeyword) - ); - const parameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "props" as __String); - parameterSymbol.type = result; - return createSignature( - declaration, - /*typeParameters*/ undefined, - /*thisParameter*/ undefined, - [parameterSymbol], - typeSymbol ? getDeclaredTypeOfSymbol(typeSymbol) : errorType, - /*returnTypePredicate*/ undefined, - 1, - SignatureFlags.None - ); - } - - function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - if (isJsxIntrinsicIdentifier(node.tagName)) { - const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); - const fakeSignature = createSignatureForJSXIntrinsic(node, result); - checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined, CheckMode.Normal), result, node.tagName, node.attributes); - return fakeSignature; - } - const exprTypes = checkExpression(node.tagName); - const apparentType = getApparentType(exprTypes); - if (apparentType === errorType) { - return resolveErrorCall(node); - } - - const signatures = getUninstantiatedJsxSignaturesOfType(exprTypes, node); - if (isUntypedFunctionCall(exprTypes, apparentType, signatures.length, /*constructSignatures*/ 0)) { - return resolveUntypedCall(node); - } - - if (signatures.length === 0) { - // We found no signatures at all, which is an error - error(node.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node.tagName)); - return resolveErrorCall(node); - } - - return resolveCall(node, signatures, candidatesOutArray, checkMode, SignatureFlags.None); - } - - /** - * Sometimes, we have a decorator that could accept zero arguments, - * but is receiving too many arguments as part of the decorator invocation. - * In those cases, a user may have meant to *call* the expression before using it as a decorator. - */ - function isPotentiallyUncalledDecorator(decorator: Decorator, signatures: readonly Signature[]) { - return signatures.length && every(signatures, signature => - signature.minArgumentCount === 0 && - !signatureHasRestParameter(signature) && - signature.parameters.length < getDecoratorArgumentCount(decorator, signature)); - } - - function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - switch (node.kind) { - case SyntaxKind.CallExpression: - return resolveCallExpression(node, candidatesOutArray, checkMode); - case SyntaxKind.NewExpression: - return resolveNewExpression(node, candidatesOutArray, checkMode); - case SyntaxKind.TaggedTemplateExpression: - return resolveTaggedTemplateExpression(node, candidatesOutArray, checkMode); - case SyntaxKind.Decorator: - return resolveDecorator(node, candidatesOutArray, checkMode); - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSelfClosingElement: - return resolveJsxOpeningLikeElement(node, candidatesOutArray, checkMode); - } - throw Debug.assertNever(node, "Branch in 'resolveSignature' should be unreachable."); - } - - /** - * Resolve a signature of a given call-like expression. - * @param node a call-like expression to try resolve a signature for - * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service; - * the function will fill it up with appropriate candidate signatures - * @return a signature of the call-like expression or undefined if one can't be found - */ - function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, checkMode?: CheckMode): Signature { - const links = getNodeLinks(node); - // If getResolvedSignature has already been called, we will have cached the resolvedSignature. - // However, it is possible that either candidatesOutArray was not passed in the first time, - // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work - // to correctly fill the candidatesOutArray. - const cached = links.resolvedSignature; - if (cached && cached !== resolvingSignature && !candidatesOutArray) { - return cached; - } - links.resolvedSignature = resolvingSignature; - const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal); - // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call - // resolution should be deferred. - if (result !== resolvingSignature) { - // If signature resolution originated in control flow type analysis (for example to compute the - // assigned type in a flow assignment) we don't cache the result as it may be based on temporary - // types from the control flow analysis. - links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached; - } - return result; - } - - /** - * Indicates whether a declaration can be treated as a constructor in a JavaScript - * file. - */ - function isJSConstructor(node: Node | undefined): node is FunctionDeclaration | FunctionExpression { - if (!node || !isInJSFile(node)) { - return false; - } - const func = isFunctionDeclaration(node) || isFunctionExpression(node) ? node : - isVariableDeclaration(node) && node.initializer && isFunctionExpression(node.initializer) ? node.initializer : - undefined; - if (func) { - // If the node has a @class tag, treat it like a constructor. - if (getJSDocClassTag(node)) return true; - - // If the symbol of the node has members, treat it like a constructor. - const symbol = getSymbolOfNode(func); - return !!symbol && hasEntries(symbol.members); - } - return false; - } - - function mergeJSSymbols(target: Symbol, source: Symbol | undefined) { - if (source) { - const links = getSymbolLinks(source); - if (!links.inferredClassSymbol || !links.inferredClassSymbol.has("" + getSymbolId(target))) { - const inferred = isTransientSymbol(target) ? target : cloneSymbol(target) as TransientSymbol; - inferred.exports = inferred.exports || createSymbolTable(); - inferred.members = inferred.members || createSymbolTable(); - inferred.flags |= source.flags & SymbolFlags.Class; - if (hasEntries(source.exports)) { - mergeSymbolTable(inferred.exports, source.exports); - } - if (hasEntries(source.members)) { - mergeSymbolTable(inferred.members, source.members); - } - (links.inferredClassSymbol || (links.inferredClassSymbol = createMap())).set("" + getSymbolId(inferred), inferred); - return inferred; - } - return links.inferredClassSymbol.get("" + getSymbolId(target)); - } - } - - function getAssignedClassSymbol(decl: Declaration): Symbol | undefined { - const assignmentSymbol = decl && decl.parent && - (isFunctionDeclaration(decl) && getSymbolOfNode(decl) || - isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left) || - isVariableDeclaration(decl.parent) && getSymbolOfNode(decl.parent)); - const prototype = assignmentSymbol && assignmentSymbol.exports && assignmentSymbol.exports.get("prototype" as __String); - const init = prototype && prototype.valueDeclaration && getAssignedJSPrototype(prototype.valueDeclaration); - return init ? getSymbolOfNode(init) : undefined; - } - - function getAssignedJSPrototype(node: Node) { - if (!node.parent) { - return false; - } - let parent: Node = node.parent; - while (parent && parent.kind === SyntaxKind.PropertyAccessExpression) { - parent = parent.parent; - } - if (parent && isBinaryExpression(parent) && isPrototypeAccess(parent.left) && parent.operatorToken.kind === SyntaxKind.EqualsToken) { - const right = getInitializerOfBinaryExpression(parent); - return isObjectLiteralExpression(right) && right; - } - } - - /** - * Syntactically and semantically checks a call or new expression. - * @param node The call/new expression to be checked. - * @returns On success, the expression's signature's return type. On failure, anyType. - */ - function checkCallExpression(node: CallExpression | NewExpression, checkMode?: CheckMode): Type { - if (!checkGrammarTypeArguments(node, node.typeArguments)) checkGrammarArguments(node.arguments); - - const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode); - if (signature === resolvingSignature) { - // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that - // returns a function type. We defer checking and return nonInferrableType. - return nonInferrableType; - } - - if (node.expression.kind === SyntaxKind.SuperKeyword) { - return voidType; - } - - if (node.kind === SyntaxKind.NewExpression) { - const declaration = signature.declaration; - - if (declaration && - declaration.kind !== SyntaxKind.Constructor && - declaration.kind !== SyntaxKind.ConstructSignature && - declaration.kind !== SyntaxKind.ConstructorType && - !isJSDocConstructSignature(declaration) && - !isJSConstructor(declaration)) { - - // When resolved signature is a call signature (and not a construct signature) the result type is any - if (noImplicitAny) { - error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type); - } - return anyType; - } - } - - // In JavaScript files, calls to any identifier 'require' are treated as external module imports - if (isInJSFile(node) && isCommonJsRequire(node)) { - return resolveExternalModuleTypeByLiteral(node.arguments![0] as StringLiteral); - } - - const returnType = getReturnTypeOfSignature(signature); - // Treat any call to the global 'Symbol' function that is part of a const variable or readonly property - // as a fresh unique symbol literal type. - if (returnType.flags & TypeFlags.ESSymbolLike && isSymbolOrSymbolForCall(node)) { - return getESSymbolLikeTypeForNode(walkUpParenthesizedExpressions(node.parent)); - } - if (node.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.ExpressionStatement && - returnType.flags & TypeFlags.Void && getTypePredicateOfSignature(signature)) { - if (!isDottedName(node.expression)) { - error(node.expression, Diagnostics.Assertions_require_the_call_target_to_be_an_identifier_or_qualified_name); - } - else if (!getEffectsSignature(node)) { - const diagnostic = error(node.expression, Diagnostics.Assertions_require_every_name_in_the_call_target_to_be_declared_with_an_explicit_type_annotation); - getTypeOfDottedName(node.expression, diagnostic); - } - } - - if (isInJSFile(node)) { - const decl = getDeclarationOfExpando(node); - if (decl) { - const jsSymbol = getSymbolOfNode(decl); - if (jsSymbol && hasEntries(jsSymbol.exports)) { - const jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined); - jsAssignmentType.objectFlags |= ObjectFlags.JSLiteral; - return getIntersectionType([returnType, jsAssignmentType]); - } - } - } - - return returnType; - } - - function isSymbolOrSymbolForCall(node: Node) { - if (!isCallExpression(node)) return false; - let left = node.expression; - if (isPropertyAccessExpression(left) && left.name.escapedText === "for") { - left = left.expression; - } - if (!isIdentifier(left) || left.escapedText !== "Symbol") { - return false; - } - - // make sure `Symbol` is the global symbol - const globalESSymbol = getGlobalESSymbolConstructorSymbol(/*reportErrors*/ false); - if (!globalESSymbol) { - return false; - } - - return globalESSymbol === resolveName(left, "Symbol" as __String, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false); - } - - function checkImportCallExpression(node: ImportCall): Type { - // Check grammar of dynamic import - if (!checkGrammarArguments(node.arguments)) checkGrammarImportCallExpression(node); - - if (node.arguments.length === 0) { - return createPromiseReturnType(node, anyType); - } - const specifier = node.arguments[0]; - const specifierType = checkExpressionCached(specifier); - // Even though multiple arguments is grammatically incorrect, type-check extra arguments for completion - for (let i = 1; i < node.arguments.length; ++i) { - checkExpressionCached(node.arguments[i]); - } - - if (specifierType.flags & TypeFlags.Undefined || specifierType.flags & TypeFlags.Null || !isTypeAssignableTo(specifierType, stringType)) { - error(specifier, Diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, typeToString(specifierType)); - } - - // resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal - const moduleSymbol = resolveExternalModuleName(node, specifier); - if (moduleSymbol) { - const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true, /*suppressUsageError*/ false); - if (esModuleSymbol) { - return createPromiseReturnType(node, getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol)); - } - } - return createPromiseReturnType(node, anyType); - } - - function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol, originalSymbol: Symbol): Type { - if (allowSyntheticDefaultImports && type && type !== errorType) { - const synthType = type as SyntheticDefaultModuleType; - if (!synthType.syntheticType) { - const file = find(originalSymbol.declarations, isSourceFile); - const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false); - if (hasSyntheticDefault) { - const memberTable = createSymbolTable(); - const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default); - newSymbol.nameType = getLiteralType("default"); - newSymbol.target = resolveSymbol(symbol); - memberTable.set(InternalSymbolName.Default, newSymbol); - const anonymousSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); - const defaultContainingObject = createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); - anonymousSymbol.type = defaultContainingObject; - synthType.syntheticType = isValidSpreadType(type) ? getSpreadType(type, defaultContainingObject, anonymousSymbol, /*objectFlags*/ 0, /*readonly*/ false) : defaultContainingObject; - } - else { - synthType.syntheticType = type; - } - } - return synthType.syntheticType; - } - return type; - } - - function isCommonJsRequire(node: Node): boolean { - if (!isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) { - return false; - } - - // Make sure require is not a local function - if (!isIdentifier(node.expression)) return Debug.fail(); - const resolvedRequire = resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true)!; // TODO: GH#18217 - if (resolvedRequire === requireSymbol) { - return true; - } - // project includes symbol named 'require' - make sure that it is ambient and local non-alias - if (resolvedRequire.flags & SymbolFlags.Alias) { - return false; - } - - const targetDeclarationKind = resolvedRequire.flags & SymbolFlags.Function - ? SyntaxKind.FunctionDeclaration - : resolvedRequire.flags & SymbolFlags.Variable - ? SyntaxKind.VariableDeclaration - : SyntaxKind.Unknown; - if (targetDeclarationKind !== SyntaxKind.Unknown) { - const decl = getDeclarationOfKind(resolvedRequire, targetDeclarationKind)!; - // function/variable declaration should be ambient - return !!decl && !!(decl.flags & NodeFlags.Ambient); - } - return false; - } - - function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type { - if (!checkGrammarTaggedTemplateChain(node)) checkGrammarTypeArguments(node, node.typeArguments); - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject); - } - return getReturnTypeOfSignature(getResolvedSignature(node)); - } - - function checkAssertion(node: AssertionExpression) { - return checkAssertionWorker(node, node.type, node.expression); - } - - function isValidConstAssertionArgument(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - return true; - case SyntaxKind.ParenthesizedExpression: - return isValidConstAssertionArgument((node).expression); - case SyntaxKind.PrefixUnaryExpression: - const op = (node).operator; - const arg = (node).operand; - return op === SyntaxKind.MinusToken && (arg.kind === SyntaxKind.NumericLiteral || arg.kind === SyntaxKind.BigIntLiteral) || - op === SyntaxKind.PlusToken && arg.kind === SyntaxKind.NumericLiteral; - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - const expr = (node).expression; - if (isIdentifier(expr)) { - let symbol = getSymbolAtLocation(expr); - if (symbol && symbol.flags & SymbolFlags.Alias) { - symbol = resolveAlias(symbol); - } - return !!(symbol && (symbol.flags & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal); - } - } - return false; - } - - function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { - let exprType = checkExpression(expression, checkMode); - if (isConstTypeReference(type)) { - if (!isValidConstAssertionArgument(expression)) { - error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); - } - return getRegularTypeOfLiteralType(exprType); - } - checkSourceElement(type); - exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); - const targetType = getTypeFromTypeNode(type); - if (produceDiagnostics && targetType !== errorType) { - const widenedType = getWidenedType(exprType); - if (!isTypeComparableTo(targetType, widenedType)) { - checkTypeComparableTo(exprType, targetType, errNode, - Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); - } - } - return targetType; - } - - function checkNonNullAssertion(node: NonNullExpression) { - return getNonNullableType(checkExpression(node.expression)); - } - - function checkMetaProperty(node: MetaProperty): Type { - checkGrammarMetaProperty(node); - - if (node.keywordToken === SyntaxKind.NewKeyword) { - return checkNewTargetMetaProperty(node); - } - - if (node.keywordToken === SyntaxKind.ImportKeyword) { - return checkImportMetaProperty(node); - } - - return Debug.assertNever(node.keywordToken); - } - - function checkNewTargetMetaProperty(node: MetaProperty) { - const container = getNewTargetContainer(node); - if (!container) { - error(node, Diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target"); - return errorType; - } - else if (container.kind === SyntaxKind.Constructor) { - const symbol = getSymbolOfNode(container.parent as ClassLikeDeclaration); - return getTypeOfSymbol(symbol); - } - else { - const symbol = getSymbolOfNode(container)!; - return getTypeOfSymbol(symbol); - } - } - - function checkImportMetaProperty(node: MetaProperty) { - if (moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) { - error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_esnext_or_system); - } - const file = getSourceFileOfNode(node); - Debug.assert(!!(file.flags & NodeFlags.PossiblyContainsImportMeta), "Containing file is missing import meta node flag."); - Debug.assert(!!file.externalModuleIndicator, "Containing file should be a module."); - return node.name.escapedText === "meta" ? getGlobalImportMetaType() : errorType; - } - - function getTypeOfParameter(symbol: Symbol) { - const type = getTypeOfSymbol(symbol); - if (strictNullChecks) { - const declaration = symbol.valueDeclaration; - if (declaration && hasInitializer(declaration)) { - return getOptionalType(type); - } - } - return type; - } - - function getParameterNameAtPosition(signature: Signature, pos: number) { - const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0); - if (pos < paramCount) { - return signature.parameters[pos].escapedName; - } - const restParameter = signature.parameters[paramCount] || unknownSymbol; - const restType = getTypeOfSymbol(restParameter); - if (isTupleType(restType)) { - const associatedNames = ((restType).target).associatedNames; - const index = pos - paramCount; - return associatedNames && associatedNames[index] || restParameter.escapedName + "_" + index as __String; - } - return restParameter.escapedName; - } - - function getTypeAtPosition(signature: Signature, pos: number): Type { - return tryGetTypeAtPosition(signature, pos) || anyType; - } - - function tryGetTypeAtPosition(signature: Signature, pos: number): Type | undefined { - const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0); - if (pos < paramCount) { - return getTypeOfParameter(signature.parameters[pos]); - } - if (signatureHasRestParameter(signature)) { - // We want to return the value undefined for an out of bounds parameter position, - // so we need to check bounds here before calling getIndexedAccessType (which - // otherwise would return the type 'undefined'). - const restType = getTypeOfSymbol(signature.parameters[paramCount]); - const index = pos - paramCount; - if (!isTupleType(restType) || restType.target.hasRestElement || index < getTypeArguments(restType).length) { - return getIndexedAccessType(restType, getLiteralType(index)); - } - } - return undefined; - } - - function getRestTypeAtPosition(source: Signature, pos: number): Type { - const paramCount = getParameterCount(source); - const restType = getEffectiveRestType(source); - const nonRestCount = paramCount - (restType ? 1 : 0); - if (restType && pos === nonRestCount) { - return restType; - } - const types = []; - const names = []; - for (let i = pos; i < nonRestCount; i++) { - types.push(getTypeAtPosition(source, i)); - names.push(getParameterNameAtPosition(source, i)); - } - if (restType) { - types.push(getIndexedAccessType(restType, numberType)); - names.push(getParameterNameAtPosition(source, nonRestCount)); - } - const minArgumentCount = getMinArgumentCount(source); - const minLength = minArgumentCount < pos ? 0 : minArgumentCount - pos; - return createTupleType(types, minLength, !!restType, /*readonly*/ false, names); - } - - function getParameterCount(signature: Signature) { - const length = signature.parameters.length; - if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[length - 1]); - if (isTupleType(restType)) { - return length + getTypeArguments(restType).length - 1; - } - } - return length; - } - - function getMinArgumentCount(signature: Signature) { - if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); - if (isTupleType(restType)) { - const minLength = restType.target.minLength; - if (minLength > 0) { - return signature.parameters.length - 1 + minLength; - } - } - } - return signature.minArgumentCount; - } - - function hasEffectiveRestParameter(signature: Signature) { - if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); - return !isTupleType(restType) || restType.target.hasRestElement; - } - return false; - } - - function getEffectiveRestType(signature: Signature) { - if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); - return isTupleType(restType) ? getRestArrayTypeOfTupleType(restType) : restType; - } - return undefined; - } - - function getNonArrayRestType(signature: Signature) { - const restType = getEffectiveRestType(signature); - return restType && !isArrayType(restType) && !isTypeAny(restType) ? restType : undefined; - } - - function getTypeOfFirstParameterOfSignature(signature: Signature) { - return getTypeOfFirstParameterOfSignatureWithFallback(signature, neverType); - } - - function getTypeOfFirstParameterOfSignatureWithFallback(signature: Signature, fallbackType: Type) { - return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : fallbackType; - } - - function inferFromAnnotatedParameters(signature: Signature, context: Signature, inferenceContext: InferenceContext) { - const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0); - for (let i = 0; i < len; i++) { - const declaration = signature.parameters[i].valueDeclaration; - if (declaration.type) { - const typeNode = getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - inferTypes(inferenceContext.inferences, getTypeFromTypeNode(typeNode), getTypeAtPosition(context, i)); - } - } - } - const restType = getEffectiveRestType(context); - if (restType && restType.flags & TypeFlags.TypeParameter) { - // The contextual signature has a generic rest parameter. We first instantiate the contextual - // signature (without fixing type parameters) and assign types to contextually typed parameters. - const instantiatedContext = instantiateSignature(context, inferenceContext.nonFixingMapper); - assignContextualParameterTypes(signature, instantiatedContext); - // We then infer from a tuple type representing the parameters that correspond to the contextual - // rest parameter. - const restPos = getParameterCount(context) - 1; - inferTypes(inferenceContext.inferences, getRestTypeAtPosition(signature, restPos), restType); - } - } - - function assignContextualParameterTypes(signature: Signature, context: Signature) { - signature.typeParameters = context.typeParameters; - if (context.thisParameter) { - const parameter = signature.thisParameter; - if (!parameter || parameter.valueDeclaration && !(parameter.valueDeclaration).type) { - if (!parameter) { - signature.thisParameter = createSymbolWithType(context.thisParameter, /*type*/ undefined); - } - assignTypeToParameterAndFixTypeParameters(signature.thisParameter!, getTypeOfSymbol(context.thisParameter)); - } - } - const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0); - for (let i = 0; i < len; i++) { - const parameter = signature.parameters[i]; - if (!getEffectiveTypeAnnotationNode(parameter.valueDeclaration)) { - const contextualParameterType = getTypeAtPosition(context, i); - assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType); - } - } - if (signatureHasRestParameter(signature)) { - // parameter might be a transient symbol generated by use of `arguments` in the function body. - const parameter = last(signature.parameters); - if (isTransientSymbol(parameter) || !getEffectiveTypeAnnotationNode(parameter.valueDeclaration)) { - const contextualParameterType = getRestTypeAtPosition(context, len); - assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType); - } - } - } - - // When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push - // the destructured type into the contained binding elements. - function assignBindingElementTypes(pattern: BindingPattern) { - for (const element of pattern.elements) { - if (!isOmittedExpression(element)) { - if (element.name.kind === SyntaxKind.Identifier) { - getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element); - } - else { - assignBindingElementTypes(element.name); - } - } - } - } - - function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type) { - const links = getSymbolLinks(parameter); - if (!links.type) { - links.type = contextualType; - const decl = parameter.valueDeclaration as ParameterDeclaration; - if (decl.name.kind !== SyntaxKind.Identifier) { - // if inference didn't come up with anything but unknown, fall back to the binding pattern if present. - if (links.type === unknownType) { - links.type = getTypeFromBindingPattern(decl.name); - } - assignBindingElementTypes(decl.name); - } - } - } - - function createPromiseType(promisedType: Type): Type { - // creates a `Promise` type where `T` is the promisedType argument - const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true); - if (globalPromiseType !== emptyGenericType) { - // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type - promisedType = getAwaitedType(promisedType) || unknownType; - return createTypeReference(globalPromiseType, [promisedType]); - } - - return unknownType; - } - - function createPromiseLikeType(promisedType: Type): Type { - // creates a `PromiseLike` type where `T` is the promisedType argument - const globalPromiseLikeType = getGlobalPromiseLikeType(/*reportErrors*/ true); - if (globalPromiseLikeType !== emptyGenericType) { - // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type - promisedType = getAwaitedType(promisedType) || unknownType; - return createTypeReference(globalPromiseLikeType, [promisedType]); - } - - return unknownType; - } - - function createPromiseReturnType(func: FunctionLikeDeclaration | ImportCall, promisedType: Type) { - const promiseType = createPromiseType(promisedType); - if (promiseType === unknownType) { - error(func, isImportCall(func) ? - Diagnostics.A_dynamic_import_call_returns_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option : - Diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option); - return errorType; - } - else if (!getGlobalPromiseConstructorSymbol(/*reportErrors*/ true)) { - error(func, isImportCall(func) ? - Diagnostics.A_dynamic_import_call_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option : - Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option); - } - - return promiseType; - } - - function getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type { - if (!func.body) { - return errorType; - } - - const functionFlags = getFunctionFlags(func); - const isAsync = (functionFlags & FunctionFlags.Async) !== 0; - const isGenerator = (functionFlags & FunctionFlags.Generator) !== 0; - - let returnType: Type | undefined; - let yieldType: Type | undefined; - let nextType: Type | undefined; - let fallbackReturnType: Type = voidType; - if (func.body.kind !== SyntaxKind.Block) { // Async or normal arrow function - returnType = checkExpressionCached(func.body, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); - if (isAsync) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which we will wrap in - // the native Promise type later in this function. - returnType = checkAwaitedType(returnType, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - } - } - else if (isGenerator) { // Generator or AsyncGenerator function - const returnTypes = checkAndAggregateReturnExpressionTypes(func, checkMode); - if (!returnTypes) { - fallbackReturnType = neverType; - } - else if (returnTypes.length > 0) { - returnType = getUnionType(returnTypes, UnionReduction.Subtype); - } - const { yieldTypes, nextTypes } = checkAndAggregateYieldOperandTypes(func, checkMode); - yieldType = some(yieldTypes) ? getUnionType(yieldTypes, UnionReduction.Subtype) : undefined; - nextType = some(nextTypes) ? getIntersectionType(nextTypes) : undefined; - } - else { // Async or normal function - const types = checkAndAggregateReturnExpressionTypes(func, checkMode); - if (!types) { - // For an async function, the return type will not be never, but rather a Promise for never. - return functionFlags & FunctionFlags.Async - ? createPromiseReturnType(func, neverType) // Async function - : neverType; // Normal function - } - if (types.length === 0) { - // For an async function, the return type will not be void, but rather a Promise for void. - return functionFlags & FunctionFlags.Async - ? createPromiseReturnType(func, voidType) // Async function - : voidType; // Normal function - } - - // Return a union of the return expression types. - returnType = getUnionType(types, UnionReduction.Subtype); - } - - if (returnType || yieldType || nextType) { - const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); - if (!contextualSignature) { - if (yieldType) reportErrorsFromWidening(func, yieldType, WideningKind.GeneratorYield); - if (returnType) reportErrorsFromWidening(func, returnType); - if (nextType) reportErrorsFromWidening(func, nextType); - } - if (returnType && isUnitType(returnType) || - yieldType && isUnitType(yieldType) || - nextType && isUnitType(nextType)) { - const contextualType = !contextualSignature ? undefined : - contextualSignature === getSignatureFromDeclaration(func) ? isGenerator ? undefined : returnType : - instantiateContextualType(getReturnTypeOfSignature(contextualSignature), func); - if (isGenerator) { - yieldType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKind.Yield, isAsync); - returnType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKind.Return, isAsync); - nextType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(nextType, contextualType, IterationTypeKind.Next, isAsync); - } - else { - returnType = getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(returnType, contextualType, isAsync); - } - } - - if (yieldType) yieldType = getWidenedType(yieldType); - if (returnType) returnType = getWidenedType(returnType); - if (nextType) nextType = getWidenedType(nextType); - } - - if (isGenerator) { - return createGeneratorReturnType( - yieldType || neverType, - returnType || fallbackReturnType, - nextType || getContextualIterationType(IterationTypeKind.Next, func) || unknownType, - isAsync); - } - else { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body is awaited type of the body, wrapped in a native Promise type. - return isAsync - ? createPromiseType(returnType || fallbackReturnType) - : returnType || fallbackReturnType; - } - } - - function createGeneratorReturnType(yieldType: Type, returnType: Type, nextType: Type, isAsyncGenerator: boolean) { - const resolver = isAsyncGenerator ? asyncIterationTypesResolver : syncIterationTypesResolver; - const globalGeneratorType = resolver.getGlobalGeneratorType(/*reportErrors*/ false); - yieldType = resolver.resolveIterationType(yieldType, /*errorNode*/ undefined) || unknownType; - returnType = resolver.resolveIterationType(returnType, /*errorNode*/ undefined) || unknownType; - nextType = resolver.resolveIterationType(nextType, /*errorNode*/ undefined) || unknownType; - if (globalGeneratorType === emptyGenericType) { - // Fall back to the global IterableIterator if returnType is assignable to the expected return iteration - // type of IterableIterator, and the expected next iteration type of IterableIterator is assignable to - // nextType. - const globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false); - const iterationTypes = globalType !== emptyGenericType ? getIterationTypesOfGlobalIterableType(globalType, resolver) : undefined; - const iterableIteratorReturnType = iterationTypes ? iterationTypes.returnType : anyType; - const iterableIteratorNextType = iterationTypes ? iterationTypes.nextType : undefinedType; - if (isTypeAssignableTo(returnType, iterableIteratorReturnType) && - isTypeAssignableTo(iterableIteratorNextType, nextType)) { - if (globalType !== emptyGenericType) { - return createTypeFromGenericGlobalType(globalType, [yieldType]); - } - - // The global IterableIterator type doesn't exist, so report an error - resolver.getGlobalIterableIteratorType(/*reportErrors*/ true); - return emptyObjectType; - } - - // The global Generator type doesn't exist, so report an error - resolver.getGlobalGeneratorType(/*reportErrors*/ true); - return emptyObjectType; - } - - return createTypeFromGenericGlobalType(globalGeneratorType, [yieldType, returnType, nextType]); - } - - function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode | undefined) { - const yieldTypes: Type[] = []; - const nextTypes: Type[] = []; - const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0; - forEachYieldExpression(func.body, yieldExpression => { - const yieldExpressionType = yieldExpression.expression ? checkExpression(yieldExpression.expression, checkMode) : undefinedWideningType; - pushIfUnique(yieldTypes, getYieldedTypeOfYieldExpression(yieldExpression, yieldExpressionType, anyType, isAsync)); - let nextType: Type | undefined; - if (yieldExpression.asteriskToken) { - const iterationTypes = getIterationTypesOfIterable( - yieldExpressionType, - isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar, - yieldExpression.expression); - nextType = iterationTypes && iterationTypes.nextType; - } - else { - nextType = getContextualType(yieldExpression); - } - if (nextType) pushIfUnique(nextTypes, nextType); - }); - return { yieldTypes, nextTypes }; - } - - function getYieldedTypeOfYieldExpression(node: YieldExpression, expressionType: Type, sentType: Type, isAsync: boolean): Type | undefined { - const errorNode = node.expression || node; - // A `yield*` expression effectively yields everything that its operand yields - const yieldedType = node.asteriskToken ? checkIteratedTypeOrElementType(isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar, expressionType, sentType, errorNode) : expressionType; - return !isAsync ? yieldedType : getAwaitedType(yieldedType, errorNode, node.asteriskToken - ? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - : Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - } - - /** - * Collect the TypeFacts learned from a typeof switch with - * total clauses `witnesses`, and the active clause ranging - * from `start` to `end`. Parameter `hasDefault` denotes - * whether the active clause contains a default clause. - */ - function getFactsFromTypeofSwitch(start: number, end: number, witnesses: string[], hasDefault: boolean): TypeFacts { - let facts: TypeFacts = TypeFacts.None; - // When in the default we only collect inequality facts - // because default is 'in theory' a set of infinite - // equalities. - if (hasDefault) { - // Value is not equal to any types after the active clause. - for (let i = end; i < witnesses.length; i++) { - facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject; - } - // Remove inequalities for types that appear in the - // active clause because they appear before other - // types collected so far. - for (let i = start; i < end; i++) { - facts &= ~(typeofNEFacts.get(witnesses[i]) || 0); - } - // Add inequalities for types before the active clause unconditionally. - for (let i = 0; i < start; i++) { - facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject; - } - } - // When in an active clause without default the set of - // equalities is finite. - else { - // Add equalities for all types in the active clause. - for (let i = start; i < end; i++) { - facts |= typeofEQFacts.get(witnesses[i]) || TypeFacts.TypeofEQHostObject; - } - // Remove equalities for types that appear before the - // active clause. - for (let i = 0; i < start; i++) { - facts &= ~(typeofEQFacts.get(witnesses[i]) || 0); - } - } - return facts; - } - - function isExhaustiveSwitchStatement(node: SwitchStatement): boolean { - const links = getNodeLinks(node); - return links.isExhaustive !== undefined ? links.isExhaustive : (links.isExhaustive = computeExhaustiveSwitchStatement(node)); - } - - function computeExhaustiveSwitchStatement(node: SwitchStatement): boolean { - if (node.expression.kind === SyntaxKind.TypeOfExpression) { - const operandType = getTypeOfExpression((node.expression as TypeOfExpression).expression); - // This cast is safe because the switch is possibly exhaustive and does not contain a default case, so there can be no undefined. - const witnesses = getSwitchClauseTypeOfWitnesses(node); - // notEqualFacts states that the type of the switched value is not equal to every type in the switch. - const notEqualFacts = getFactsFromTypeofSwitch(0, 0, witnesses, /*hasDefault*/ true); - const type = getBaseConstraintOfType(operandType) || operandType; - return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never); - } - const type = getTypeOfExpression(node.expression); - if (!isLiteralType(type)) { - return false; - } - const switchTypes = getSwitchClauseTypes(node); - if (!switchTypes.length || some(switchTypes, isNeitherUnitTypeNorNever)) { - return false; - } - return eachTypeContainedIn(mapType(type, getRegularTypeOfLiteralType), switchTypes); - } - - function functionHasImplicitReturn(func: FunctionLikeDeclaration) { - return func.endFlowNode && isReachableFlowNode(func.endFlowNode); - } - - /** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means func returns `void`, `undefined` means it returns `never`. */ - function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode | undefined): Type[] | undefined { - const functionFlags = getFunctionFlags(func); - const aggregatedTypes: Type[] = []; - let hasReturnWithNoExpression = functionHasImplicitReturn(func); - let hasReturnOfTypeNever = false; - forEachReturnStatement(func.body, returnStatement => { - const expr = returnStatement.expression; - if (expr) { - let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); - if (functionFlags & FunctionFlags.Async) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which should be wrapped in - // the native Promise type by the caller. - type = checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - } - if (type.flags & TypeFlags.Never) { - hasReturnOfTypeNever = true; - } - pushIfUnique(aggregatedTypes, type); - } - else { - hasReturnWithNoExpression = true; - } - }); - if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || mayReturnNever(func))) { - return undefined; - } - if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression && - !(isJSConstructor(func) && aggregatedTypes.some(t => t.symbol === func.symbol))) { - // Javascript "callable constructors", containing eg `if (!(this instanceof A)) return new A()` should not add undefined - pushIfUnique(aggregatedTypes, undefinedType); - } - return aggregatedTypes; - } - function mayReturnNever(func: FunctionLikeDeclaration): boolean { - switch (func.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return true; - case SyntaxKind.MethodDeclaration: - return func.parent.kind === SyntaxKind.ObjectLiteralExpression; - default: - return false; - } - } - - /** - * TypeScript Specification 1.0 (6.3) - July 2014 - * An explicitly typed function whose return type isn't the Void type, - * the Any type, or a union type containing the Void or Any type as a constituent - * must have at least one return statement somewhere in its body. - * An exception to this rule is if the function implementation consists of a single 'throw' statement. - * - * @param returnType - return type of the function, can be undefined if return type is not explicitly specified - */ - function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration | MethodSignature, returnType: Type | undefined): void { - if (!produceDiagnostics) { - return; - } - - const functionFlags = getFunctionFlags(func); - const type = returnType && getReturnOrPromisedType(returnType, functionFlags); - - // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. - if (type && maybeTypeOfKind(type, TypeFlags.Any | TypeFlags.Void)) { - return; - } - - // If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check. - // also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw - if (func.kind === SyntaxKind.MethodSignature || nodeIsMissing(func.body) || func.body!.kind !== SyntaxKind.Block || !functionHasImplicitReturn(func)) { - return; - } - - const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn; - - if (type && type.flags & TypeFlags.Never) { - error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point); - } - else if (type && !hasExplicitReturn) { - // minimal check: function has syntactic return type annotation and no explicit return statements in the body - // this function does not conform to the specification. - // NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present - error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value); - } - else if (type && strictNullChecks && !isTypeAssignableTo(undefinedType, type)) { - error(getEffectiveReturnTypeNode(func) || func, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined); - } - else if (compilerOptions.noImplicitReturns) { - if (!type) { - // If return type annotation is omitted check if function has any explicit return statements. - // If it does not have any - its inferred return type is void - don't do any checks. - // Otherwise get inferred return type from function body and report error only if it is not void / anytype - if (!hasExplicitReturn) { - return; - } - const inferredReturnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func)); - if (isUnwrappedReturnTypeVoidOrAny(func, inferredReturnType)) { - return; - } - } - error(getEffectiveReturnTypeNode(func) || func, Diagnostics.Not_all_code_paths_return_a_value); - } - } - - function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, checkMode?: CheckMode): Type { - Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - checkNodeDeferred(node); - - // The identityMapper object is used to indicate that function expressions are wildcards - if (checkMode && checkMode & CheckMode.SkipContextSensitive && isContextSensitive(node)) { - // Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage - if (!getEffectiveReturnTypeNode(node) && hasContextSensitiveReturnExpression(node)) { - // Return plain anyFunctionType if there is no possibility we'll make inferences from the return type - const contextualSignature = getContextualSignature(node); - if (contextualSignature && couldContainTypeVariables(getReturnTypeOfSignature(contextualSignature))) { - const links = getNodeLinks(node); - if (links.contextFreeType) { - return links.contextFreeType; - } - const returnType = getReturnTypeFromBody(node, checkMode); - const returnOnlySignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); - const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, undefined, undefined); - returnOnlyType.objectFlags |= ObjectFlags.NonInferrableType; - return links.contextFreeType = returnOnlyType; - } - } - return anyFunctionType; - } - - // Grammar checking - const hasGrammarError = checkGrammarFunctionLikeDeclaration(node); - if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) { - checkGrammarForGenerator(node); - } - - const type = getTypeOfSymbol(getMergedSymbol(node.symbol)); - if (isTypeAny(type)) { - return type; - } - - contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode); - - return type; - } - - function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode?: CheckMode) { - const links = getNodeLinks(node); - // Check if function expression is contextually typed and assign parameter types if so. - if (!(links.flags & NodeCheckFlags.ContextChecked)) { - const contextualSignature = getContextualSignature(node); - // If a type check is started at a function expression that is an argument of a function call, obtaining the - // contextual type may recursively get back to here during overload resolution of the call. If so, we will have - // already assigned contextual types. - if (!(links.flags & NodeCheckFlags.ContextChecked)) { - links.flags |= NodeCheckFlags.ContextChecked; - if (contextualSignature) { - const type = getTypeOfSymbol(getMergedSymbol(node.symbol)); - if (isTypeAny(type)) { - return; - } - const signature = getSignaturesOfType(type, SignatureKind.Call)[0]; - if (isContextSensitive(node)) { - const inferenceContext = getInferenceContext(node); - if (checkMode && checkMode & CheckMode.Inferential) { - inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!); - } - const instantiatedContextualSignature = inferenceContext ? - instantiateSignature(contextualSignature, inferenceContext.mapper) : contextualSignature; - assignContextualParameterTypes(signature, instantiatedContextualSignature); - } - if (!getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) { - const returnType = getReturnTypeFromBody(node, checkMode); - if (!signature.resolvedReturnType) { - signature.resolvedReturnType = returnType; - } - } - } - checkSignatureDeclaration(node); - } - } - } - - function getReturnOrPromisedType(type: Type | undefined, functionFlags: FunctionFlags) { - const isGenerator = !!(functionFlags & FunctionFlags.Generator); - const isAsync = !!(functionFlags & FunctionFlags.Async); - return type && isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, type, isAsync) || errorType : - type && isAsync ? getAwaitedType(type) || errorType : - type; - } - - function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) { - Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - - const functionFlags = getFunctionFlags(node); - const returnType = getReturnTypeFromAnnotation(node); - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); - - if (node.body) { - if (!getEffectiveReturnTypeNode(node)) { - // There are some checks that are only performed in getReturnTypeFromBody, that may produce errors - // we need. An example is the noImplicitAny errors resulting from widening the return expression - // of a function. Because checking of function expression bodies is deferred, there was never an - // appropriate time to do this during the main walk of the file (see the comment at the top of - // checkFunctionExpressionBodies). So it must be done now. - getReturnTypeOfSignature(getSignatureFromDeclaration(node)); - } - - if (node.body.kind === SyntaxKind.Block) { - checkSourceElement(node.body); - } - else { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so we - // should not be checking assignability of a promise to the return type. Instead, we need to - // check assignability of the awaited type of the expression body against the promised type of - // its return type annotation. - const exprType = checkExpression(node.body); - const returnOrPromisedType = getReturnOrPromisedType(returnType, functionFlags); - if (returnOrPromisedType) { - if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function - const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, node.body, node.body); - } - else { // Normal function - checkTypeAssignableToAndOptionallyElaborate(exprType, returnOrPromisedType, node.body, node.body); - } - } - } - } - } - - function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage, isAwaitValid = false): boolean { - if (!isTypeAssignableTo(type, numberOrBigIntType)) { - const awaitedType = isAwaitValid && getAwaitedTypeOfPromise(type); - errorAndMaybeSuggestAwait( - operand, - !!awaitedType && isTypeAssignableTo(awaitedType, numberOrBigIntType), - diagnostic); - return false; - } - return true; - } - - function isReadonlyAssignmentDeclaration(d: Declaration) { - if (!isCallExpression(d)) { - return false; - } - if (!isBindableObjectDefinePropertyCall(d)) { - return false; - } - const objectLitType = checkExpressionCached(d.arguments[2]); - const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String); - if (valueType) { - const writableProp = getPropertyOfType(objectLitType, "writable" as __String); - const writableType = writableProp && getTypeOfSymbol(writableProp); - if (!writableType || writableType === falseType || writableType === regularFalseType) { - return true; - } - // We include this definition whereupon we walk back and check the type at the declaration because - // The usual definition of `Object.defineProperty` will _not_ cause literal types to be preserved in the - // argument types, should the type be contextualized by the call itself. - if (writableProp && writableProp.valueDeclaration && isPropertyAssignment(writableProp.valueDeclaration)) { - const initializer = writableProp.valueDeclaration.initializer; - const rawOriginalType = checkExpression(initializer); - if (rawOriginalType === falseType || rawOriginalType === regularFalseType) { - return true; - } - } - return false; - } - const setProp = getPropertyOfType(objectLitType, "set" as __String); - return !setProp; - } - - function isReadonlySymbol(symbol: Symbol): boolean { - // The following symbols are considered read-only: - // Properties with a 'readonly' modifier - // Variables declared with 'const' - // Get accessors without matching set accessors - // Enum members - // Object.defineProperty assignments with writable false or no setter - // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) - return !!(getCheckFlags(symbol) & CheckFlags.Readonly || - symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly || - symbol.flags & SymbolFlags.Variable && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const || - symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) || - symbol.flags & SymbolFlags.EnumMember || - some(symbol.declarations, isReadonlyAssignmentDeclaration) - ); - } - - function isAssignmentToReadonlyEntity(expr: Expression, symbol: Symbol, assignmentKind: AssignmentKind) { - if (assignmentKind === AssignmentKind.None) { - // no assigment means it doesn't matter whether the entity is readonly - return false; - } - if (isReadonlySymbol(symbol)) { - // Allow assignments to readonly properties within constructors of the same class declaration. - if (symbol.flags & SymbolFlags.Property && - isAccessExpression(expr) && - expr.expression.kind === SyntaxKind.ThisKeyword) { - // Look for if this is the constructor for the class that `symbol` is a property of. - const ctor = getContainingFunction(expr); - if (!(ctor && ctor.kind === SyntaxKind.Constructor)) { - return true; - } - if (symbol.valueDeclaration) { - const isAssignmentDeclaration = isBinaryExpression(symbol.valueDeclaration); - const isLocalPropertyDeclaration = ctor.parent === symbol.valueDeclaration.parent; - const isLocalParameterProperty = ctor === symbol.valueDeclaration.parent; - const isLocalThisPropertyAssignment = isAssignmentDeclaration && symbol.parent?.valueDeclaration === ctor.parent; - const isLocalThisPropertyAssignmentConstructorFunction = isAssignmentDeclaration && symbol.parent?.valueDeclaration === ctor; - const isWriteableSymbol = - isLocalPropertyDeclaration - || isLocalParameterProperty - || isLocalThisPropertyAssignment - || isLocalThisPropertyAssignmentConstructorFunction; - return !isWriteableSymbol; - } - } - return true; - } - if (isAccessExpression(expr)) { - // references through namespace import should be readonly - const node = skipParentheses(expr.expression); - if (node.kind === SyntaxKind.Identifier) { - const symbol = getNodeLinks(node).resolvedSymbol!; - if (symbol.flags & SymbolFlags.Alias) { - const declaration = getDeclarationOfAliasSymbol(symbol); - return !!declaration && declaration.kind === SyntaxKind.NamespaceImport; - } - } - } - return false; - } - - function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean { - // References are combinations of identifiers, parentheses, and property accesses. - const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses); - if (node.kind !== SyntaxKind.Identifier && !isAccessExpression(node)) { - error(expr, invalidReferenceMessage); - return false; - } - if (node.flags & NodeFlags.OptionalChain) { - error(expr, invalidOptionalChainMessage); - return false; - } - return true; - } - - function checkDeleteExpression(node: DeleteExpression): Type { - checkExpression(node.expression); - const expr = skipParentheses(node.expression); - if (!isAccessExpression(expr)) { - error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference); - return booleanType; - } - if (expr.kind === SyntaxKind.PropertyAccessExpression && isPrivateIdentifier((expr as PropertyAccessExpression).name)) { - error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_private_identifier); - } - const links = getNodeLinks(expr); - const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol); - if (symbol && isReadonlySymbol(symbol)) { - error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property); - } - return booleanType; - } - - function checkTypeOfExpression(node: TypeOfExpression): Type { - checkExpression(node.expression); - return typeofType; - } - - function checkVoidExpression(node: VoidExpression): Type { - checkExpression(node.expression); - return undefinedWideningType; - } - - function isTopLevelAwait(node: AwaitExpression) { - const container = getThisContainer(node, /*includeArrowFunctions*/ true); - return isSourceFile(container); - } - - function checkAwaitExpression(node: AwaitExpression): Type { - // Grammar checking - if (produceDiagnostics) { - if (!(node.flags & NodeFlags.AwaitContext)) { - if (isTopLevelAwait(node)) { - const sourceFile = getSourceFileOfNode(node); - if ((moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) || - languageVersion < ScriptTarget.ES2017 || - !isEffectiveExternalModule(sourceFile, compilerOptions)) { - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition(sourceFile, node.pos); - const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, - Diagnostics.await_outside_of_an_async_function_is_only_allowed_at_the_top_level_of_a_module_when_module_is_esnext_or_system_and_target_is_es2017_or_higher); - diagnostics.add(diagnostic); - } - } - } - else { - // use of 'await' in non-async function - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition(sourceFile, node.pos); - const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expression_is_only_allowed_within_an_async_function); - const func = getContainingFunction(node); - if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) { - const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async); - addRelatedInfo(diagnostic, relatedInfo); - } - diagnostics.add(diagnostic); - } - } - } - - if (isInParameterInitializerBeforeContainingFunction(node)) { - error(node, Diagnostics.await_expressions_cannot_be_used_in_a_parameter_initializer); - } - } - - const operandType = checkExpression(node.expression); - const awaitedType = checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - if (awaitedType === operandType && awaitedType !== errorType && !(operandType.flags & TypeFlags.AnyOrUnknown)) { - addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(node, Diagnostics.await_has_no_effect_on_the_type_of_this_expression)); - } - return awaitedType; - } - - function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { - const operandType = checkExpression(node.operand); - if (operandType === silentNeverType) { - return silentNeverType; - } - switch (node.operand.kind) { - case SyntaxKind.NumericLiteral: - switch (node.operator) { - case SyntaxKind.MinusToken: - return getFreshTypeOfLiteralType(getLiteralType(-(node.operand as NumericLiteral).text)); - case SyntaxKind.PlusToken: - return getFreshTypeOfLiteralType(getLiteralType(+(node.operand as NumericLiteral).text)); - } - break; - case SyntaxKind.BigIntLiteral: - if (node.operator === SyntaxKind.MinusToken) { - return getFreshTypeOfLiteralType(getLiteralType({ - negative: true, - base10Value: parsePseudoBigInt((node.operand as BigIntLiteral).text) - })); - } - } - switch (node.operator) { - case SyntaxKind.PlusToken: - case SyntaxKind.MinusToken: - case SyntaxKind.TildeToken: - checkNonNullType(operandType, node.operand); - if (maybeTypeOfKind(operandType, TypeFlags.ESSymbolLike)) { - error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator)); - } - if (node.operator === SyntaxKind.PlusToken) { - if (maybeTypeOfKind(operandType, TypeFlags.BigIntLike)) { - error(node.operand, Diagnostics.Operator_0_cannot_be_applied_to_type_1, tokenToString(node.operator), typeToString(getBaseTypeOfLiteralType(operandType))); - } - return numberType; - } - return getUnaryResultType(operandType); - case SyntaxKind.ExclamationToken: - checkTruthinessExpression(node.operand); - const facts = getTypeFacts(operandType) & (TypeFacts.Truthy | TypeFacts.Falsy); - return facts === TypeFacts.Truthy ? falseType : - facts === TypeFacts.Falsy ? trueType : - booleanType; - case SyntaxKind.PlusPlusToken: - case SyntaxKind.MinusMinusToken: - const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand), - Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type); - if (ok) { - // run check only if former checks succeeded to avoid reporting cascading errors - checkReferenceExpression( - node.operand, - Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, - Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access); - } - return getUnaryResultType(operandType); - } - return errorType; - } - - function checkPostfixUnaryExpression(node: PostfixUnaryExpression): Type { - const operandType = checkExpression(node.operand); - if (operandType === silentNeverType) { - return silentNeverType; - } - const ok = checkArithmeticOperandType( - node.operand, - checkNonNullType(operandType, node.operand), - Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type); - if (ok) { - // run check only if former checks succeeded to avoid reporting cascading errors - checkReferenceExpression( - node.operand, - Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, - Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access); - } - return getUnaryResultType(operandType); - } - - function getUnaryResultType(operandType: Type): Type { - if (maybeTypeOfKind(operandType, TypeFlags.BigIntLike)) { - return isTypeAssignableToKind(operandType, TypeFlags.AnyOrUnknown) || maybeTypeOfKind(operandType, TypeFlags.NumberLike) - ? numberOrBigIntType - : bigintType; - } - // If it's not a bigint type, implicit coercion will result in a number - return numberType; - } - - // Return true if type might be of the given kind. A union or intersection type might be of a given - // kind if at least one constituent type is of the given kind. - function maybeTypeOfKind(type: Type, kind: TypeFlags): boolean { - if (type.flags & kind & ~TypeFlags.GenericMappedType || kind & TypeFlags.GenericMappedType && isGenericMappedType(type)) { - return true; - } - if (type.flags & TypeFlags.UnionOrIntersection) { - const types = (type).types; - for (const t of types) { - if (maybeTypeOfKind(t, kind)) { - return true; - } - } - } - return false; - } - - function isTypeAssignableToKind(source: Type, kind: TypeFlags, strict?: boolean): boolean { - if (source.flags & kind) { - return true; - } - if (strict && source.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null)) { - return false; - } - return !!(kind & TypeFlags.NumberLike) && isTypeAssignableTo(source, numberType) || - !!(kind & TypeFlags.BigIntLike) && isTypeAssignableTo(source, bigintType) || - !!(kind & TypeFlags.StringLike) && isTypeAssignableTo(source, stringType) || - !!(kind & TypeFlags.BooleanLike) && isTypeAssignableTo(source, booleanType) || - !!(kind & TypeFlags.Void) && isTypeAssignableTo(source, voidType) || - !!(kind & TypeFlags.Never) && isTypeAssignableTo(source, neverType) || - !!(kind & TypeFlags.Null) && isTypeAssignableTo(source, nullType) || - !!(kind & TypeFlags.Undefined) && isTypeAssignableTo(source, undefinedType) || - !!(kind & TypeFlags.ESSymbol) && isTypeAssignableTo(source, esSymbolType) || - !!(kind & TypeFlags.NonPrimitive) && isTypeAssignableTo(source, nonPrimitiveType); - } - - function allTypesAssignableToKind(source: Type, kind: TypeFlags, strict?: boolean): boolean { - return source.flags & TypeFlags.Union ? - every((source as UnionType).types, subType => allTypesAssignableToKind(subType, kind, strict)) : - isTypeAssignableToKind(source, kind, strict); - } - - function isConstEnumObjectType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Anonymous) && !!type.symbol && isConstEnumSymbol(type.symbol); - } - - function isConstEnumSymbol(symbol: Symbol): boolean { - return (symbol.flags & SymbolFlags.ConstEnum) !== 0; - } - - function checkInstanceOfExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; - } - // TypeScript 1.0 spec (April 2014): 4.15.4 - // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, - // and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature. - // The result is always of the Boolean primitive type. - // NOTE: do not raise error if leftType is unknown as related error was already reported - if (!isTypeAny(leftType) && - allTypesAssignableToKind(leftType, TypeFlags.Primitive)) { - error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); - } - // NOTE: do not raise error if right is unknown as related error was already reported - if (!(isTypeAny(rightType) || typeHasCallOrConstructSignatures(rightType) || isTypeSubtypeOf(rightType, globalFunctionType))) { - error(right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type); - } - return booleanType; - } - - function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; - } - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - // TypeScript 1.0 spec (April 2014): 4.15.5 - // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, - // and the right operand to be of type Any, an object type, or a type parameter type. - // The result is always of the Boolean primitive type. - if (!(isTypeComparableTo(leftType, stringType) || isTypeAssignableToKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbolLike))) { - error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); - } - if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive)) { - error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); - } - return booleanType; - } - - function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, rightIsThis?: boolean): Type { - const properties = node.properties; - if (strictNullChecks && properties.length === 0) { - return checkNonNullType(sourceType, node); - } - for (let i = 0; i < properties.length; i++) { - checkObjectLiteralDestructuringPropertyAssignment(node, sourceType, i, properties, rightIsThis); - } - return sourceType; - } - - /** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */ - function checkObjectLiteralDestructuringPropertyAssignment(node: ObjectLiteralExpression, objectLiteralType: Type, propertyIndex: number, allProperties?: NodeArray, rightIsThis = false) { - const properties = node.properties; - const property = properties[propertyIndex]; - if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) { - const name = property.name; - const exprType = getLiteralTypeFromPropertyName(name); - if (isTypeUsableAsPropertyName(exprType)) { - const text = getPropertyNameFromType(exprType); - const prop = getPropertyOfType(objectLiteralType, text); - if (prop) { - markPropertyAsReferenced(prop, property, rightIsThis); - checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop); - } - } - const elementType = getIndexedAccessType(objectLiteralType, exprType, name); - const type = getFlowTypeOfDestructuring(property, elementType); - return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type); - } - else if (property.kind === SyntaxKind.SpreadAssignment) { - if (propertyIndex < properties.length - 1) { - error(property, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern); - } - else { - if (languageVersion < ScriptTarget.ESNext) { - checkExternalEmitHelpers(property, ExternalEmitHelpers.Rest); - } - const nonRestNames: PropertyName[] = []; - if (allProperties) { - for (const otherProperty of allProperties) { - if (!isSpreadAssignment(otherProperty)) { - nonRestNames.push(otherProperty.name); - } - } - } - const type = getRestType(objectLiteralType, nonRestNames, objectLiteralType.symbol); - checkGrammarForDisallowedTrailingComma(allProperties, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma); - return checkDestructuringAssignment(property.expression, type); - } - } - else { - error(property, Diagnostics.Property_assignment_expected); - } - } - - function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type { - const elements = node.elements; - if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Read); - } - // This elementType will be used if the specific property corresponding to this index is not - // present (aka the tuple element property). This call also checks that the parentType is in - // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, sourceType, undefinedType, node) || errorType; - for (let i = 0; i < elements.length; i++) { - checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode); - } - return sourceType; - } - - function checkArrayLiteralDestructuringElementAssignment(node: ArrayLiteralExpression, sourceType: Type, - elementIndex: number, elementType: Type, checkMode?: CheckMode) { - const elements = node.elements; - const element = elements[elementIndex]; - if (element.kind !== SyntaxKind.OmittedExpression) { - if (element.kind !== SyntaxKind.SpreadElement) { - const indexType = getLiteralType(elementIndex); - if (isArrayLikeType(sourceType)) { - // We create a synthetic expression so that getIndexedAccessType doesn't get confused - // when the element is a SyntaxKind.ElementAccessExpression. - const accessFlags = hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0; - const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, createSyntheticExpression(element, indexType), accessFlags) || errorType; - const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType; - const type = getFlowTypeOfDestructuring(element, assignedType); - return checkDestructuringAssignment(element, type, checkMode); - } - return checkDestructuringAssignment(element, elementType, checkMode); - } - if (elementIndex < elements.length - 1) { - error(element, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern); - } - else { - const restExpression = (element).expression; - if (restExpression.kind === SyntaxKind.BinaryExpression && (restExpression).operatorToken.kind === SyntaxKind.EqualsToken) { - error((restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer); - } - else { - checkGrammarForDisallowedTrailingComma(node.elements, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma); - const type = everyType(sourceType, isTupleType) ? - mapType(sourceType, t => sliceTupleType(t, elementIndex)) : - createArrayType(elementType); - return checkDestructuringAssignment(restExpression, type, checkMode); - } - } - } - return undefined; - } - - function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode, rightIsThis?: boolean): Type { - let target: Expression; - if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) { - const prop = exprOrAssignment; - if (prop.objectAssignmentInitializer) { - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - if (strictNullChecks && - !(getFalsyFlags(checkExpression(prop.objectAssignmentInitializer)) & TypeFlags.Undefined)) { - sourceType = getTypeWithFacts(sourceType, TypeFacts.NEUndefined); - } - checkBinaryLikeExpression(prop.name, prop.equalsToken!, prop.objectAssignmentInitializer, checkMode); - } - target = (exprOrAssignment).name; - } - else { - target = exprOrAssignment; - } - - if (target.kind === SyntaxKind.BinaryExpression && (target).operatorToken.kind === SyntaxKind.EqualsToken) { - checkBinaryExpression(target, checkMode); - target = (target).left; - } - if (target.kind === SyntaxKind.ObjectLiteralExpression) { - return checkObjectLiteralAssignment(target, sourceType, rightIsThis); - } - if (target.kind === SyntaxKind.ArrayLiteralExpression) { - return checkArrayLiteralAssignment(target, sourceType, checkMode); - } - return checkReferenceAssignment(target, sourceType, checkMode); - } - - function checkReferenceAssignment(target: Expression, sourceType: Type, checkMode?: CheckMode): Type { - const targetType = checkExpression(target, checkMode); - const error = target.parent.kind === SyntaxKind.SpreadAssignment ? - Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access : - Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access; - const optionalError = target.parent.kind === SyntaxKind.SpreadAssignment ? - Diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access : - Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access; - if (checkReferenceExpression(target, error, optionalError)) { - checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target); - } - if (isPrivateIdentifierPropertyAccessExpression(target)) { - checkExternalEmitHelpers(target.parent, ExternalEmitHelpers.ClassPrivateFieldSet); - } - return sourceType; - } - - /** - * This is a *shallow* check: An expression is side-effect-free if the - * evaluation of the expression *itself* cannot produce side effects. - * For example, x++ / 3 is side-effect free because the / operator - * does not have side effects. - * The intent is to "smell test" an expression for correctness in positions where - * its value is discarded (e.g. the left side of the comma operator). - */ - function isSideEffectFree(node: Node): boolean { - node = skipParentheses(node); - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.TemplateExpression: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ClassExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.NonNullExpression: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxElement: - return true; - - case SyntaxKind.ConditionalExpression: - return isSideEffectFree((node as ConditionalExpression).whenTrue) && - isSideEffectFree((node as ConditionalExpression).whenFalse); - - case SyntaxKind.BinaryExpression: - if (isAssignmentOperator((node as BinaryExpression).operatorToken.kind)) { - return false; - } - return isSideEffectFree((node as BinaryExpression).left) && - isSideEffectFree((node as BinaryExpression).right); - - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - // Unary operators ~, !, +, and - have no side effects. - // The rest do. - switch ((node as PrefixUnaryExpression).operator) { - case SyntaxKind.ExclamationToken: - case SyntaxKind.PlusToken: - case SyntaxKind.MinusToken: - case SyntaxKind.TildeToken: - return true; - } - return false; - - // Some forms listed here for clarity - case SyntaxKind.VoidExpression: // Explicit opt-out - case SyntaxKind.TypeAssertionExpression: // Not SEF, but can produce useful type warnings - case SyntaxKind.AsExpression: // Not SEF, but can produce useful type warnings - default: - return false; - } - } - - function isTypeEqualityComparableTo(source: Type, target: Type) { - return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); - } - - function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) { - if (isInJSFile(node) && getAssignedExpandoInitializer(node)) { - return checkExpression(node.right, checkMode); - } - checkGrammarNullishCoalesceWithLogicalExpression(node); - return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node); - } - - function checkGrammarNullishCoalesceWithLogicalExpression(node: BinaryExpression) { - const { left, operatorToken, right } = node; - if (operatorToken.kind === SyntaxKind.QuestionQuestionToken) { - if (isBinaryExpression(left) && (left.operatorToken.kind === SyntaxKind.BarBarToken || left.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken)) { - grammarErrorOnNode(left, Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses, tokenToString(left.operatorToken.kind), tokenToString(operatorToken.kind)); - } - if (isBinaryExpression(right) && (right.operatorToken.kind === SyntaxKind.BarBarToken || right.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken)) { - grammarErrorOnNode(right, Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses, tokenToString(right.operatorToken.kind), tokenToString(operatorToken.kind)); - } - } - } - - function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, checkMode?: CheckMode, errorNode?: Node): Type { - const operator = operatorToken.kind; - if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) { - return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode, right.kind === SyntaxKind.ThisKeyword); - } - let leftType: Type; - if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) { - leftType = checkTruthinessExpression(left, checkMode); - } - else { - leftType = checkExpression(left, checkMode); - } - - let rightType = checkExpression(right, checkMode); - switch (operator) { - case SyntaxKind.AsteriskToken: - case SyntaxKind.AsteriskAsteriskToken: - case SyntaxKind.AsteriskEqualsToken: - case SyntaxKind.AsteriskAsteriskEqualsToken: - case SyntaxKind.SlashToken: - case SyntaxKind.SlashEqualsToken: - case SyntaxKind.PercentToken: - case SyntaxKind.PercentEqualsToken: - case SyntaxKind.MinusToken: - case SyntaxKind.MinusEqualsToken: - case SyntaxKind.LessThanLessThanToken: - case SyntaxKind.LessThanLessThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanToken: - case SyntaxKind.GreaterThanGreaterThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - case SyntaxKind.BarToken: - case SyntaxKind.BarEqualsToken: - case SyntaxKind.CaretToken: - case SyntaxKind.CaretEqualsToken: - case SyntaxKind.AmpersandToken: - case SyntaxKind.AmpersandEqualsToken: - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; - } - - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - - let suggestedOperator: SyntaxKind | undefined; - // if a user tries to apply a bitwise operator to 2 boolean operands - // try and return them a helpful suggestion - if ((leftType.flags & TypeFlags.BooleanLike) && - (rightType.flags & TypeFlags.BooleanLike) && - (suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) { - error(errorNode || operatorToken, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(operatorToken.kind), tokenToString(suggestedOperator)); - return numberType; - } - else { - // otherwise just check each operand separately and report errors as normal - const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ true); - const rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ true); - let resultType: Type; - // If both are any or unknown, allow operation; assume it will resolve to number - if ((isTypeAssignableToKind(leftType, TypeFlags.AnyOrUnknown) && isTypeAssignableToKind(rightType, TypeFlags.AnyOrUnknown)) || - // Or, if neither could be bigint, implicit coercion results in a number result - !(maybeTypeOfKind(leftType, TypeFlags.BigIntLike) || maybeTypeOfKind(rightType, TypeFlags.BigIntLike)) - ) { - resultType = numberType; - } - // At least one is assignable to bigint, so check that both are - else if (bothAreBigIntLike(leftType, rightType)) { - switch (operator) { - case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - reportOperatorError(); - } - resultType = bigintType; - } - // Exactly one of leftType/rightType is assignable to bigint - else { - reportOperatorError(bothAreBigIntLike); - resultType = errorType; - } - if (leftOk && rightOk) { - checkAssignmentOperator(resultType); - } - return resultType; - } - case SyntaxKind.PlusToken: - case SyntaxKind.PlusEqualsToken: - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; - } - - if (!isTypeAssignableToKind(leftType, TypeFlags.StringLike) && !isTypeAssignableToKind(rightType, TypeFlags.StringLike)) { - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - } - - let resultType: Type | undefined; - if (isTypeAssignableToKind(leftType, TypeFlags.NumberLike, /*strict*/ true) && isTypeAssignableToKind(rightType, TypeFlags.NumberLike, /*strict*/ true)) { - // Operands of an enum type are treated as having the primitive type Number. - // If both operands are of the Number primitive type, the result is of the Number primitive type. - resultType = numberType; - } - else if (isTypeAssignableToKind(leftType, TypeFlags.BigIntLike, /*strict*/ true) && isTypeAssignableToKind(rightType, TypeFlags.BigIntLike, /*strict*/ true)) { - // If both operands are of the BigInt primitive type, the result is of the BigInt primitive type. - resultType = bigintType; - } - else if (isTypeAssignableToKind(leftType, TypeFlags.StringLike, /*strict*/ true) || isTypeAssignableToKind(rightType, TypeFlags.StringLike, /*strict*/ true)) { - // If one or both operands are of the String primitive type, the result is of the String primitive type. - resultType = stringType; - } - else if (isTypeAny(leftType) || isTypeAny(rightType)) { - // Otherwise, the result is of type Any. - // NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we. - resultType = leftType === errorType || rightType === errorType ? errorType : anyType; - } - - // Symbols are not allowed at all in arithmetic expressions - if (resultType && !checkForDisallowedESSymbolOperand(operator)) { - return resultType; - } - - if (!resultType) { - // Types that have a reasonably good chance of being a valid operand type. - // If both types have an awaited type of one of these, we'll assume the user - // might be missing an await without doing an exhaustive check that inserting - // await(s) will actually be a completely valid binary expression. - const closeEnoughKind = TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.AnyOrUnknown; - reportOperatorError((left, right) => - isTypeAssignableToKind(left, closeEnoughKind) && - isTypeAssignableToKind(right, closeEnoughKind)); - return anyType; - } - - if (operator === SyntaxKind.PlusEqualsToken) { - checkAssignmentOperator(resultType); - } - return resultType; - case SyntaxKind.LessThanToken: - case SyntaxKind.GreaterThanToken: - case SyntaxKind.LessThanEqualsToken: - case SyntaxKind.GreaterThanEqualsToken: - if (checkForDisallowedESSymbolOperand(operator)) { - leftType = getBaseTypeOfLiteralType(checkNonNullType(leftType, left)); - rightType = getBaseTypeOfLiteralType(checkNonNullType(rightType, right)); - reportOperatorErrorUnless((left, right) => - isTypeComparableTo(left, right) || isTypeComparableTo(right, left) || ( - isTypeAssignableTo(left, numberOrBigIntType) && isTypeAssignableTo(right, numberOrBigIntType))); - } - return booleanType; - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left)); - return booleanType; - - case SyntaxKind.InstanceOfKeyword: - return checkInstanceOfExpression(left, right, leftType, rightType); - case SyntaxKind.InKeyword: - return checkInExpression(left, right, leftType, rightType); - case SyntaxKind.AmpersandAmpersandToken: - return getTypeFacts(leftType) & TypeFacts.Truthy ? - getUnionType([extractDefinitelyFalsyTypes(strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType)), rightType]) : - leftType; - case SyntaxKind.BarBarToken: - return getTypeFacts(leftType) & TypeFacts.Falsy ? - getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) : - leftType; - case SyntaxKind.QuestionQuestionToken: - return getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull ? - getUnionType([getNonNullableType(leftType), rightType], UnionReduction.Subtype) : - leftType; - case SyntaxKind.EqualsToken: - const declKind = isBinaryExpression(left.parent) ? getAssignmentDeclarationKind(left.parent) : AssignmentDeclarationKind.None; - checkAssignmentDeclaration(declKind, rightType); - if (isAssignmentDeclaration(declKind)) { - if (!(rightType.flags & TypeFlags.Object) || - declKind !== AssignmentDeclarationKind.ModuleExports && - declKind !== AssignmentDeclarationKind.Prototype && - !isEmptyObjectType(rightType) && - !isFunctionObjectType(rightType as ObjectType) && - !(getObjectFlags(rightType) & ObjectFlags.Class)) { - // don't check assignability of module.exports=, C.prototype=, or expando types because they will necessarily be incomplete - checkAssignmentOperator(rightType); - } - return leftType; - } - else { - checkAssignmentOperator(rightType); - return getRegularTypeOfObjectLiteral(rightType); - } - case SyntaxKind.CommaToken: - if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isEvalNode(right)) { - error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects); - } - return rightType; - - default: - return Debug.fail(); - } - - function bothAreBigIntLike(left: Type, right: Type): boolean { - return isTypeAssignableToKind(left, TypeFlags.BigIntLike) && isTypeAssignableToKind(right, TypeFlags.BigIntLike); - } - - function checkAssignmentDeclaration(kind: AssignmentDeclarationKind, rightType: Type) { - if (kind === AssignmentDeclarationKind.ModuleExports) { - for (const prop of getPropertiesOfObjectType(rightType)) { - const propType = getTypeOfSymbol(prop); - if (propType.symbol && propType.symbol.flags & SymbolFlags.Class) { - const name = prop.escapedName; - const symbol = resolveName(prop.valueDeclaration, name, SymbolFlags.Type, undefined, name, /*isUse*/ false); - if (symbol && symbol.declarations.some(isJSDocTypedefTag)) { - grammarErrorOnNode(symbol.declarations[0], Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(name)); - return grammarErrorOnNode(prop.valueDeclaration, Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(name)); - } - } - } - } - } - - function isEvalNode(node: Expression) { - return node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "eval"; - } - - // Return true if there was no error, false if there was an error. - function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean { - const offendingSymbolOperand = - maybeTypeOfKind(leftType, TypeFlags.ESSymbolLike) ? left : - maybeTypeOfKind(rightType, TypeFlags.ESSymbolLike) ? right : - undefined; - - if (offendingSymbolOperand) { - error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator)); - return false; - } - - return true; - } - - function getSuggestedBooleanOperator(operator: SyntaxKind): SyntaxKind | undefined { - switch (operator) { - case SyntaxKind.BarToken: - case SyntaxKind.BarEqualsToken: - return SyntaxKind.BarBarToken; - case SyntaxKind.CaretToken: - case SyntaxKind.CaretEqualsToken: - return SyntaxKind.ExclamationEqualsEqualsToken; - case SyntaxKind.AmpersandToken: - case SyntaxKind.AmpersandEqualsToken: - return SyntaxKind.AmpersandAmpersandToken; - default: - return undefined; - } - } - - function checkAssignmentOperator(valueType: Type): void { - if (produceDiagnostics && isAssignmentOperator(operator)) { - // TypeScript 1.0 spec (April 2014): 4.17 - // An assignment of the form - // VarExpr = ValueExpr - // requires VarExpr to be classified as a reference - // A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1) - // and the type of the non-compound operation to be assignable to the type of VarExpr. - - if (checkReferenceExpression(left, - Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access, - Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access) - && (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) { - // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported - checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right); - } - } - } - - function isAssignmentDeclaration(kind: AssignmentDeclarationKind) { - switch (kind) { - case AssignmentDeclarationKind.ModuleExports: - return true; - case AssignmentDeclarationKind.ExportsProperty: - case AssignmentDeclarationKind.Property: - case AssignmentDeclarationKind.Prototype: - case AssignmentDeclarationKind.PrototypeProperty: - case AssignmentDeclarationKind.ThisProperty: - const symbol = getSymbolOfNode(left); - const init = getAssignedExpandoInitializer(right); - return init && isObjectLiteralExpression(init) && - symbol && hasEntries(symbol.exports); - default: - return false; - } - } - - /** - * Returns true if an error is reported - */ - function reportOperatorErrorUnless(typesAreCompatible: (left: Type, right: Type) => boolean): boolean { - if (!typesAreCompatible(leftType, rightType)) { - reportOperatorError(typesAreCompatible); - return true; - } - return false; - } - - function reportOperatorError(isRelated?: (left: Type, right: Type) => boolean) { - let wouldWorkWithAwait = false; - const errNode = errorNode || operatorToken; - if (isRelated) { - const awaitedLeftType = getAwaitedType(leftType); - const awaitedRightType = getAwaitedType(rightType); - wouldWorkWithAwait = !(awaitedLeftType === leftType && awaitedRightType === rightType) - && !!(awaitedLeftType && awaitedRightType) - && isRelated(awaitedLeftType, awaitedRightType); - } - - let effectiveLeft = leftType; - let effectiveRight = rightType; - if (!wouldWorkWithAwait && isRelated) { - [effectiveLeft, effectiveRight] = getBaseTypesIfUnrelated(leftType, rightType, isRelated); - } - const [leftStr, rightStr] = getTypeNamesForErrorDisplay(effectiveLeft, effectiveRight); - if (!tryGiveBetterPrimaryError(errNode, wouldWorkWithAwait, leftStr, rightStr)) { - errorAndMaybeSuggestAwait( - errNode, - wouldWorkWithAwait, - Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, - tokenToString(operatorToken.kind), - leftStr, - rightStr, - ); - } - } - - function tryGiveBetterPrimaryError(errNode: Node, maybeMissingAwait: boolean, leftStr: string, rightStr: string) { - let typeName: string | undefined; - switch (operatorToken.kind) { - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.EqualsEqualsToken: - typeName = "false"; - break; - case SyntaxKind.ExclamationEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - typeName = "true"; - } - - if (typeName) { - return errorAndMaybeSuggestAwait( - errNode, - maybeMissingAwait, - Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, - typeName, leftStr, rightStr); - } - - return undefined; - } - } - - function getBaseTypesIfUnrelated(leftType: Type, rightType: Type, isRelated: (left: Type, right: Type) => boolean): [Type, Type] { - let effectiveLeft = leftType; - let effectiveRight = rightType; - const leftBase = getBaseTypeOfLiteralType(leftType); - const rightBase = getBaseTypeOfLiteralType(rightType); - if (!isRelated(leftBase, rightBase)) { - effectiveLeft = leftBase; - effectiveRight = rightBase; - } - return [ effectiveLeft, effectiveRight ]; - } - - function checkYieldExpression(node: YieldExpression): Type { - // Grammar checking - if (produceDiagnostics) { - if (!(node.flags & NodeFlags.YieldContext)) { - grammarErrorOnFirstToken(node, Diagnostics.A_yield_expression_is_only_allowed_in_a_generator_body); - } - - if (isInParameterInitializerBeforeContainingFunction(node)) { - error(node, Diagnostics.yield_expressions_cannot_be_used_in_a_parameter_initializer); - } - } - - const func = getContainingFunction(node); - if (!func) return anyType; - const functionFlags = getFunctionFlags(func); - - if (!(functionFlags & FunctionFlags.Generator)) { - // If the user's code is syntactically correct, the func should always have a star. After all, we are in a yield context. - return anyType; - } - - const isAsync = (functionFlags & FunctionFlags.Async) !== 0; - if (node.asteriskToken) { - // Async generator functions prior to ESNext require the __await, __asyncDelegator, - // and __asyncValues helpers - if (isAsync && languageVersion < ScriptTarget.ESNext) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes); - } - - // Generator functions prior to ES2015 require the __values helper - if (!isAsync && languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Values); - } - } - - // There is no point in doing an assignability check if the function - // has no explicit return type because the return type is directly computed - // from the yield expressions. - const returnType = getReturnTypeFromAnnotation(func); - const iterationTypes = returnType && getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsync); - const signatureYieldType = iterationTypes && iterationTypes.yieldType || anyType; - const signatureNextType = iterationTypes && iterationTypes.nextType || anyType; - const resolvedSignatureNextType = isAsync ? getAwaitedType(signatureNextType) || anyType : signatureNextType; - const yieldExpressionType = node.expression ? checkExpression(node.expression) : undefinedWideningType; - const yieldedType = getYieldedTypeOfYieldExpression(node, yieldExpressionType, resolvedSignatureNextType, isAsync); - if (returnType && yieldedType) { - checkTypeAssignableToAndOptionallyElaborate(yieldedType, signatureYieldType, node.expression || node, node.expression); - } - - if (node.asteriskToken) { - const use = isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar; - return getIterationTypeOfIterable(use, IterationTypeKind.Return, yieldExpressionType, node.expression) - || anyType; - } - else if (returnType) { - return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, isAsync) - || anyType; - } - - return getContextualIterationType(IterationTypeKind.Next, func) || anyType; - } - - function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type { - checkTruthinessExpression(node.condition); - const type1 = checkExpression(node.whenTrue, checkMode); - const type2 = checkExpression(node.whenFalse, checkMode); - return getUnionType([type1, type2], UnionReduction.Subtype); - } - - function checkTemplateExpression(node: TemplateExpression): Type { - // We just want to check each expressions, but we are unconcerned with - // the type of each expression, as any value may be coerced into a string. - // It is worth asking whether this is what we really want though. - // A place where we actually *are* concerned with the expressions' types are - // in tagged templates. - forEach(node.templateSpans, templateSpan => { - if (maybeTypeOfKind(checkExpression(templateSpan.expression), TypeFlags.ESSymbolLike)) { - error(templateSpan.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String); - } - }); - - return stringType; - } - - function getContextNode(node: Expression): Node { - if (node.kind === SyntaxKind.JsxAttributes && !isJsxSelfClosingElement(node.parent)) { - return node.parent.parent; // Needs to be the root JsxElement, so it encompasses the attributes _and_ the children (which are essentially part of the attributes) - } - return node; - } - - function checkExpressionWithContextualType(node: Expression, contextualType: Type, inferenceContext: InferenceContext | undefined, checkMode: CheckMode): Type { - const context = getContextNode(node); - const saveContextualType = context.contextualType; - const saveInferenceContext = context.inferenceContext; - context.contextualType = contextualType; - context.inferenceContext = inferenceContext; - const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0)); - // We strip literal freshness when an appropriate contextual type is present such that contextually typed - // literals always preserve their literal types (otherwise they might widen during type inference). An alternative - // here would be to not mark contextually typed literals as fresh in the first place. - const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ? - getRegularTypeOfLiteralType(type) : type; - context.contextualType = saveContextualType; - context.inferenceContext = saveInferenceContext; - return result; - } - - function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - if (checkMode && checkMode !== CheckMode.Normal) { - return checkExpression(node, checkMode); - } - // When computing a type that we're going to cache, we need to ignore any ongoing control flow - // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart - // to the top of the stack ensures all transient types are computed from a known point. - const saveFlowLoopStart = flowLoopStart; - const saveFlowTypeCache = flowTypeCache; - flowLoopStart = flowLoopCount; - flowTypeCache = undefined; - links.resolvedType = checkExpression(node, checkMode); - flowTypeCache = saveFlowTypeCache; - flowLoopStart = saveFlowLoopStart; - } - return links.resolvedType; - } - - function isTypeAssertion(node: Expression) { - node = skipParentheses(node); - return node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression; - } - - function checkDeclarationInitializer(declaration: HasExpressionInitializer) { - const initializer = getEffectiveInitializer(declaration)!; - const type = getQuickTypeOfExpression(initializer) || checkExpressionCached(initializer); - const padded = isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern && - isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ? - padTupleType(type, declaration.name) : type; - const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const || - isDeclarationReadonly(declaration) || - isTypeAssertion(initializer) || - isLiteralOfContextualType(padded, getContextualType(initializer)) ? padded : getWidenedLiteralType(padded); - if (isInJSFile(declaration)) { - if (widened.flags & TypeFlags.Nullable) { - reportImplicitAny(declaration, anyType); - return anyType; - } - else if (isEmptyArrayLiteralType(widened)) { - reportImplicitAny(declaration, anyArrayType); - return anyArrayType; - } - } - return widened; - } - - function padTupleType(type: TupleTypeReference, pattern: ArrayBindingPattern) { - const patternElements = pattern.elements; - const arity = getTypeReferenceArity(type); - const elementTypes = arity ? getTypeArguments(type).slice() : []; - for (let i = arity; i < patternElements.length; i++) { - const e = patternElements[i]; - if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && e.dotDotDotToken)) { - elementTypes.push(!isOmittedExpression(e) && hasDefaultValue(e) ? getTypeFromBindingElement(e, /*includePatternInType*/ false, /*reportErrors*/ false) : anyType); - if (!isOmittedExpression(e) && !hasDefaultValue(e)) { - reportImplicitAny(e, anyType); - } - } - } - return createTupleType(elementTypes, type.target.minLength, /*hasRestElement*/ false, type.target.readonly); - } - - function isLiteralOfContextualType(candidateType: Type, contextualType: Type | undefined): boolean { - if (contextualType) { - if (contextualType.flags & TypeFlags.UnionOrIntersection) { - const types = (contextualType).types; - return some(types, t => isLiteralOfContextualType(candidateType, t)); - } - if (contextualType.flags & TypeFlags.InstantiableNonPrimitive) { - // If the contextual type is a type variable constrained to a primitive type, consider - // this a literal context for literals of that primitive type. For example, given a - // type parameter 'T extends string', infer string literal types for T. - const constraint = getBaseConstraintOfType(contextualType) || unknownType; - return maybeTypeOfKind(constraint, TypeFlags.String) && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) || - maybeTypeOfKind(constraint, TypeFlags.Number) && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) || - maybeTypeOfKind(constraint, TypeFlags.BigInt) && maybeTypeOfKind(candidateType, TypeFlags.BigIntLiteral) || - maybeTypeOfKind(constraint, TypeFlags.ESSymbol) && maybeTypeOfKind(candidateType, TypeFlags.UniqueESSymbol) || - isLiteralOfContextualType(candidateType, constraint); - } - // If the contextual type is a literal of a particular primitive type, we consider this a - // literal context for all literals of that primitive type. - return !!(contextualType.flags & (TypeFlags.StringLiteral | TypeFlags.Index) && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) || - contextualType.flags & TypeFlags.NumberLiteral && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) || - contextualType.flags & TypeFlags.BigIntLiteral && maybeTypeOfKind(candidateType, TypeFlags.BigIntLiteral) || - contextualType.flags & TypeFlags.BooleanLiteral && maybeTypeOfKind(candidateType, TypeFlags.BooleanLiteral) || - contextualType.flags & TypeFlags.UniqueESSymbol && maybeTypeOfKind(candidateType, TypeFlags.UniqueESSymbol)); - } - return false; - } - - function isConstContext(node: Expression): boolean { - const parent = node.parent; - return isAssertionExpression(parent) && isConstTypeReference(parent.type) || - (isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) || - (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent)) && isConstContext(parent.parent); - } - - function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type { - const type = checkExpression(node, checkMode, forceTuple); - return isConstContext(node) ? getRegularTypeOfLiteralType(type) : - isTypeAssertion(node) ? type : - getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(arguments.length === 2 ? getContextualType(node) : contextualType, node)); - } - - function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type { - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - } - - return checkExpressionForMutableLocation(node.initializer, checkMode); - } - - function checkObjectLiteralMethod(node: MethodDeclaration, checkMode?: CheckMode): Type { - // Grammar checking - checkGrammarMethod(node); - - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - } - - const uninstantiatedType = checkFunctionExpressionOrObjectLiteralMethod(node, checkMode); - return instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode); - } - - function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration | QualifiedName, type: Type, checkMode?: CheckMode) { - if (checkMode && checkMode & (CheckMode.Inferential | CheckMode.SkipGenericFunctions)) { - const callSignature = getSingleSignature(type, SignatureKind.Call, /*allowMembers*/ true); - const constructSignature = getSingleSignature(type, SignatureKind.Construct, /*allowMembers*/ true); - const signature = callSignature || constructSignature; - if (signature && signature.typeParameters) { - const contextualType = getApparentTypeOfContextualType(node, ContextFlags.NoConstraints); - if (contextualType) { - const contextualSignature = getSingleSignature(getNonNullableType(contextualType), callSignature ? SignatureKind.Call : SignatureKind.Construct, /*allowMembers*/ false); - if (contextualSignature && !contextualSignature.typeParameters) { - if (checkMode & CheckMode.SkipGenericFunctions) { - skippedGenericFunction(node, checkMode); - return anyFunctionType; - } - const context = getInferenceContext(node)!; - // We have an expression that is an argument of a generic function for which we are performing - // type argument inference. The expression is of a function type with a single generic call - // signature and a contextual function type with a single non-generic call signature. Now check - // if the outer function returns a function type with a single non-generic call signature and - // if some of the outer function type parameters have no inferences so far. If so, we can - // potentially add inferred type parameters to the outer function return type. - const returnType = context.signature && getReturnTypeOfSignature(context.signature); - const returnSignature = returnType && getSingleCallOrConstructSignature(returnType); - if (returnSignature && !returnSignature.typeParameters && !every(context.inferences, hasInferenceCandidates)) { - // Instantiate the signature with its own type parameters as type arguments, possibly - // renaming the type parameters to ensure they have unique names. - const uniqueTypeParameters = getUniqueTypeParameters(context, signature.typeParameters); - const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, uniqueTypeParameters); - // Infer from the parameters of the instantiated signature to the parameters of the - // contextual signature starting with an empty set of inference candidates. - const inferences = map(context.inferences, info => createInferenceInfo(info.typeParameter)); - applyToParameterTypes(instantiatedSignature, contextualSignature, (source, target) => { - inferTypes(inferences, source, target, /*priority*/ 0, /*contravariant*/ true); - }); - if (some(inferences, hasInferenceCandidates)) { - // We have inference candidates, indicating that one or more type parameters are referenced - // in the parameter types of the contextual signature. Now also infer from the return type. - applyToReturnTypes(instantiatedSignature, contextualSignature, (source, target) => { - inferTypes(inferences, source, target); - }); - // If the type parameters for which we produced candidates do not have any inferences yet, - // we adopt the new inference candidates and add the type parameters of the expression type - // to the set of inferred type parameters for the outer function return type. - if (!hasOverlappingInferences(context.inferences, inferences)) { - mergeInferences(context.inferences, inferences); - context.inferredTypeParameters = concatenate(context.inferredTypeParameters, uniqueTypeParameters); - return getOrCreateTypeFromSignature(instantiatedSignature); - } - } - } - return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, context)); - } - } - } - } - return type; - } - - function skippedGenericFunction(node: Node, checkMode: CheckMode) { - if (checkMode & CheckMode.Inferential) { - // We have skipped a generic function during inferential typing. Obtain the inference context and - // indicate this has occurred such that we know a second pass of inference is be needed. - const context = getInferenceContext(node)!; - context.flags |= InferenceFlags.SkippedGenericFunction; - } - } - - function hasInferenceCandidates(info: InferenceInfo) { - return !!(info.candidates || info.contraCandidates); - } - - function hasOverlappingInferences(a: InferenceInfo[], b: InferenceInfo[]) { - for (let i = 0; i < a.length; i++) { - if (hasInferenceCandidates(a[i]) && hasInferenceCandidates(b[i])) { - return true; - } - } - return false; - } - - function mergeInferences(target: InferenceInfo[], source: InferenceInfo[]) { - for (let i = 0; i < target.length; i++) { - if (!hasInferenceCandidates(target[i]) && hasInferenceCandidates(source[i])) { - target[i] = source[i]; - } - } - } - - function getUniqueTypeParameters(context: InferenceContext, typeParameters: readonly TypeParameter[]): readonly TypeParameter[] { - const result: TypeParameter[] = []; - let oldTypeParameters: TypeParameter[] | undefined; - let newTypeParameters: TypeParameter[] | undefined; - for (const tp of typeParameters) { - const name = tp.symbol.escapedName; - if (hasTypeParameterByName(context.inferredTypeParameters, name) || hasTypeParameterByName(result, name)) { - const newName = getUniqueTypeParameterName(concatenate(context.inferredTypeParameters, result), name); - const symbol = createSymbol(SymbolFlags.TypeParameter, newName); - const newTypeParameter = createTypeParameter(symbol); - newTypeParameter.target = tp; - oldTypeParameters = append(oldTypeParameters, tp); - newTypeParameters = append(newTypeParameters, newTypeParameter); - result.push(newTypeParameter); - } - else { - result.push(tp); - } - } - if (newTypeParameters) { - const mapper = createTypeMapper(oldTypeParameters!, newTypeParameters); - for (const tp of newTypeParameters) { - tp.mapper = mapper; - } - } - return result; - } - - function hasTypeParameterByName(typeParameters: readonly TypeParameter[] | undefined, name: __String) { - return some(typeParameters, tp => tp.symbol.escapedName === name); - } - - function getUniqueTypeParameterName(typeParameters: readonly TypeParameter[], baseName: __String) { - let len = (baseName).length; - while (len > 1 && (baseName).charCodeAt(len - 1) >= CharacterCodes._0 && (baseName).charCodeAt(len - 1) <= CharacterCodes._9) len--; - const s = (baseName).slice(0, len); - for (let index = 1; true; index++) { - const augmentedName = <__String>(s + index); - if (!hasTypeParameterByName(typeParameters, augmentedName)) { - return augmentedName; - } - } - } - - function getReturnTypeOfSingleNonGenericCallSignature(funcType: Type) { - const signature = getSingleCallSignature(funcType); - if (signature && !signature.typeParameters) { - return getReturnTypeOfSignature(signature); - } - } - - function getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr: CallChain) { - const funcType = checkExpression(expr.expression); - const nonOptionalType = getOptionalExpressionType(funcType, expr.expression); - const returnType = getReturnTypeOfSingleNonGenericCallSignature(funcType); - return returnType && propagateOptionalTypeMarker(returnType, expr, nonOptionalType !== funcType); - } - - /** - * Returns the type of an expression. Unlike checkExpression, this function is simply concerned - * with computing the type and may not fully check all contained sub-expressions for errors. - */ - function getTypeOfExpression(node: Expression) { - // Don't bother caching types that require no flow analysis and are quick to compute. - const quickType = getQuickTypeOfExpression(node); - if (quickType) { - return quickType; - } - // If a type has been cached for the node, return it. - if (node.flags & NodeFlags.TypeCached && flowTypeCache) { - const cachedType = flowTypeCache[getNodeId(node)]; - if (cachedType) { - return cachedType; - } - } - const startInvocationCount = flowInvocationCount; - const type = checkExpression(node); - // If control flow analysis was required to determine the type, it is worth caching. - if (flowInvocationCount !== startInvocationCount) { - const cache = flowTypeCache || (flowTypeCache = []); - cache[getNodeId(node)] = type; - node.flags |= NodeFlags.TypeCached; - } - return type; - } - - function getQuickTypeOfExpression(node: Expression) { - const expr = skipParentheses(node); - // Optimize for the common case of a call to a function with a single non-generic call - // signature where we can just fetch the return type without checking the arguments. - if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { - const type = isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) : - getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression)); - if (type) { - return type; - } - } - else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) { - return getTypeFromTypeNode((expr).type); - } - else if (node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral || - node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword) { - return checkExpression(node); - } - return undefined; - } - - /** - * Returns the type of an expression. Unlike checkExpression, this function is simply concerned - * with computing the type and may not fully check all contained sub-expressions for errors. - * It is intended for uses where you know there is no contextual type, - * and requesting the contextual type might cause a circularity or other bad behaviour. - * It sets the contextual type of the node to any before calling getTypeOfExpression. - */ - function getContextFreeTypeOfExpression(node: Expression) { - const links = getNodeLinks(node); - if (links.contextFreeType) { - return links.contextFreeType; - } - const saveContextualType = node.contextualType; - node.contextualType = anyType; - const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive); - node.contextualType = saveContextualType; - return type; - } - - function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { - const saveCurrentNode = currentNode; - currentNode = node; - instantiationCount = 0; - const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple); - const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode); - if (isConstEnumObjectType(type)) { - checkConstEnumAccess(node, type); - } - currentNode = saveCurrentNode; - return type; - } - - function checkConstEnumAccess(node: Expression | QualifiedName, type: Type) { - // enum object type for const enums are only permitted in: - // - 'left' in property access - // - 'object' in indexed access - // - target in rhs of import statement - const ok = - (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).expression === node) || - (node.parent.kind === SyntaxKind.ElementAccessExpression && (node.parent).expression === node) || - ((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(node) || - (node.parent.kind === SyntaxKind.TypeQuery && (node.parent).exprName === node)) || - (node.parent.kind === SyntaxKind.ExportSpecifier); // We allow reexporting const enums - - if (!ok) { - error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query); - } - - if (compilerOptions.isolatedModules) { - Debug.assert(!!(type.symbol.flags & SymbolFlags.ConstEnum)); - const constEnumDeclaration = type.symbol.valueDeclaration as EnumDeclaration; - if (constEnumDeclaration.flags & NodeFlags.Ambient) { - error(node, Diagnostics.Cannot_access_ambient_const_enums_when_the_isolatedModules_flag_is_provided); - } - } - } - - function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type { - const tag = isInJSFile(node) ? getJSDocTypeTag(node) : undefined; - if (tag) { - return checkAssertionWorker(tag, tag.typeExpression.type, node.expression, checkMode); - } - return checkExpression(node.expression, checkMode); - } - - function checkExpressionWorker(node: Expression | QualifiedName, checkMode: CheckMode | undefined, forceTuple?: boolean): Type { - const kind = node.kind; - if (cancellationToken) { - // Only bother checking on a few construct kinds. We don't want to be excessively - // hitting the cancellation token on every node we check. - switch (kind) { - case SyntaxKind.ClassExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - cancellationToken.throwIfCancellationRequested(); - } - } - switch (kind) { - case SyntaxKind.Identifier: - return checkIdentifier(node); - case SyntaxKind.ThisKeyword: - return checkThisExpression(node); - case SyntaxKind.SuperKeyword: - return checkSuperExpression(node); - case SyntaxKind.NullKeyword: - return nullWideningType; - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.StringLiteral: - return getFreshTypeOfLiteralType(getLiteralType((node as StringLiteralLike).text)); - case SyntaxKind.NumericLiteral: - checkGrammarNumericLiteral(node as NumericLiteral); - return getFreshTypeOfLiteralType(getLiteralType(+(node as NumericLiteral).text)); - case SyntaxKind.BigIntLiteral: - checkGrammarBigIntLiteral(node as BigIntLiteral); - return getFreshTypeOfLiteralType(getBigIntLiteralType(node as BigIntLiteral)); - case SyntaxKind.TrueKeyword: - return trueType; - case SyntaxKind.FalseKeyword: - return falseType; - case SyntaxKind.TemplateExpression: - return checkTemplateExpression(node); - case SyntaxKind.RegularExpressionLiteral: - return globalRegExpType; - case SyntaxKind.ArrayLiteralExpression: - return checkArrayLiteral(node, checkMode, forceTuple); - case SyntaxKind.ObjectLiteralExpression: - return checkObjectLiteral(node, checkMode); - case SyntaxKind.PropertyAccessExpression: - return checkPropertyAccessExpression(node); - case SyntaxKind.QualifiedName: - return checkQualifiedName(node); - case SyntaxKind.ElementAccessExpression: - return checkIndexedAccess(node); - case SyntaxKind.CallExpression: - if ((node).expression.kind === SyntaxKind.ImportKeyword) { - return checkImportCallExpression(node); - } - // falls through - case SyntaxKind.NewExpression: - return checkCallExpression(node, checkMode); - case SyntaxKind.TaggedTemplateExpression: - return checkTaggedTemplateExpression(node); - case SyntaxKind.ParenthesizedExpression: - return checkParenthesizedExpression(node, checkMode); - case SyntaxKind.ClassExpression: - return checkClassExpression(node); - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return checkFunctionExpressionOrObjectLiteralMethod(node, checkMode); - case SyntaxKind.TypeOfExpression: - return checkTypeOfExpression(node); - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return checkAssertion(node); - case SyntaxKind.NonNullExpression: - return checkNonNullAssertion(node); - case SyntaxKind.MetaProperty: - return checkMetaProperty(node); - case SyntaxKind.DeleteExpression: - return checkDeleteExpression(node); - case SyntaxKind.VoidExpression: - return checkVoidExpression(node); - case SyntaxKind.AwaitExpression: - return checkAwaitExpression(node); - case SyntaxKind.PrefixUnaryExpression: - return checkPrefixUnaryExpression(node); - case SyntaxKind.PostfixUnaryExpression: - return checkPostfixUnaryExpression(node); - case SyntaxKind.BinaryExpression: - return checkBinaryExpression(node, checkMode); - case SyntaxKind.ConditionalExpression: - return checkConditionalExpression(node, checkMode); - case SyntaxKind.SpreadElement: - return checkSpreadExpression(node, checkMode); - case SyntaxKind.OmittedExpression: - return undefinedWideningType; - case SyntaxKind.YieldExpression: - return checkYieldExpression(node); - case SyntaxKind.SyntheticExpression: - return (node).type; - case SyntaxKind.JsxExpression: - return checkJsxExpression(node, checkMode); - case SyntaxKind.JsxElement: - return checkJsxElement(node, checkMode); - case SyntaxKind.JsxSelfClosingElement: - return checkJsxSelfClosingElement(node, checkMode); - case SyntaxKind.JsxFragment: - return checkJsxFragment(node); - case SyntaxKind.JsxAttributes: - return checkJsxAttributes(node, checkMode); - case SyntaxKind.JsxOpeningElement: - Debug.fail("Shouldn't ever directly check a JsxOpeningElement"); - } - return errorType; - } - - // DECLARATION AND STATEMENT TYPE CHECKING - - function checkTypeParameter(node: TypeParameterDeclaration) { - // Grammar Checking - if (node.expression) { - grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected); - } - - checkSourceElement(node.constraint); - checkSourceElement(node.default); - const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node)); - // Resolve base constraint to reveal circularity errors - getBaseConstraintOfType(typeParameter); - if (!hasNonCircularTypeParameterDefault(typeParameter)) { - error(node.default, Diagnostics.Type_parameter_0_has_a_circular_default, typeToString(typeParameter)); - } - const constraintType = getConstraintOfTypeParameter(typeParameter); - const defaultType = getDefaultFromTypeParameter(typeParameter); - if (constraintType && defaultType) { - checkTypeAssignableTo(defaultType, getTypeWithThisArgument(instantiateType(constraintType, makeUnaryTypeMapper(typeParameter, defaultType)), defaultType), node.default, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); - } - if (produceDiagnostics) { - checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0); - } - } - - function checkParameter(node: ParameterDeclaration) { - // Grammar checking - // It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the - // Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code - // or if its FunctionBody is strict code(11.1.5). - checkGrammarDecoratorsAndModifiers(node); - - checkVariableLikeDeclaration(node); - const func = getContainingFunction(node)!; - if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { - if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) { - error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); - } - } - if (node.questionToken && isBindingPattern(node.name) && (func as FunctionLikeDeclaration).body) { - error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature); - } - if (node.name && isIdentifier(node.name) && (node.name.escapedText === "this" || node.name.escapedText === "new")) { - if (func.parameters.indexOf(node) !== 0) { - error(node, Diagnostics.A_0_parameter_must_be_the_first_parameter, node.name.escapedText as string); - } - if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) { - error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter); - } - if (func.kind === SyntaxKind.ArrowFunction) { - error(node, Diagnostics.An_arrow_function_cannot_have_a_this_parameter); - } - } - - // Only check rest parameter type if it's not a binding pattern. Since binding patterns are - // not allowed in a rest parameter, we already have an error from checkGrammarParameterList. - if (node.dotDotDotToken && !isBindingPattern(node.name) && !isTypeAssignableTo(getTypeOfSymbol(node.symbol), anyReadonlyArrayType)) { - error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type); - } - } - - function checkTypePredicate(node: TypePredicateNode): void { - const parent = getTypePredicateParent(node); - if (!parent) { - // The parent must not be valid. - error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); - return; - } - - const signature = getSignatureFromDeclaration(parent); - const typePredicate = getTypePredicateOfSignature(signature); - if (!typePredicate) { - return; - } - - checkSourceElement(node.type); - - const { parameterName } = node; - if (typePredicate.kind === TypePredicateKind.This || typePredicate.kind === TypePredicateKind.AssertsThis) { - getTypeFromThisTypeNode(parameterName as ThisTypeNode); - } - else { - if (typePredicate.parameterIndex >= 0) { - if (signatureHasRestParameter(signature) && typePredicate.parameterIndex === signature.parameters.length - 1) { - error(parameterName, Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); - } - else { - if (typePredicate.type) { - const leadingError = () => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type); - checkTypeAssignableTo(typePredicate.type, - getTypeOfSymbol(signature.parameters[typePredicate.parameterIndex]), - node.type, - /*headMessage*/ undefined, - leadingError); - } - } - } - else if (parameterName) { - let hasReportedError = false; - for (const { name } of parent.parameters) { - if (isBindingPattern(name) && - checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, parameterName, typePredicate.parameterName)) { - hasReportedError = true; - break; - } - } - if (!hasReportedError) { - error(node.parameterName, Diagnostics.Cannot_find_parameter_0, typePredicate.parameterName); - } - } - } - } - - function getTypePredicateParent(node: Node): SignatureDeclaration | undefined { - switch (node.parent.kind) { - case SyntaxKind.ArrowFunction: - case SyntaxKind.CallSignature: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionType: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - const parent = node.parent; - if (node === parent.type) { - return parent; - } - } - } - - function checkIfTypePredicateVariableIsDeclaredInBindingPattern( - pattern: BindingPattern, - predicateVariableNode: Node, - predicateVariableName: string) { - for (const element of pattern.elements) { - if (isOmittedExpression(element)) { - continue; - } - - const name = element.name; - if (name.kind === SyntaxKind.Identifier && name.escapedText === predicateVariableName) { - error(predicateVariableNode, - Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, - predicateVariableName); - return true; - } - else if (name.kind === SyntaxKind.ArrayBindingPattern || name.kind === SyntaxKind.ObjectBindingPattern) { - if (checkIfTypePredicateVariableIsDeclaredInBindingPattern( - name, - predicateVariableNode, - predicateVariableName)) { - return true; - } - } - } - } - - function checkSignatureDeclaration(node: SignatureDeclaration) { - // Grammar checking - if (node.kind === SyntaxKind.IndexSignature) { - checkGrammarIndexSignature(node); - } - // TODO (yuisu): Remove this check in else-if when SyntaxKind.Construct is moved and ambient context is handled - else if (node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.ConstructorType || - node.kind === SyntaxKind.CallSignature || node.kind === SyntaxKind.Constructor || - node.kind === SyntaxKind.ConstructSignature) { - checkGrammarFunctionLikeDeclaration(node); - } - - const functionFlags = getFunctionFlags(node); - if (!(functionFlags & FunctionFlags.Invalid)) { - // Async generators prior to ESNext require the __await and __asyncGenerator helpers - if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && languageVersion < ScriptTarget.ESNext) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncGeneratorIncludes); - } - - // Async functions prior to ES2017 require the __awaiter helper - if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async && languageVersion < ScriptTarget.ES2017) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter); - } - - // Generator functions, Async functions, and Async Generator functions prior to - // ES2015 require the __generator helper - if ((functionFlags & FunctionFlags.AsyncGenerator) !== FunctionFlags.Normal && languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator); - } - } - - checkTypeParameters(node.typeParameters); - - forEach(node.parameters, checkParameter); - - // TODO(rbuckton): Should we start checking JSDoc types? - if (node.type) { - checkSourceElement(node.type); - } - - if (produceDiagnostics) { - checkCollisionWithArgumentsInGeneratedCode(node); - const returnTypeNode = getEffectiveReturnTypeNode(node); - if (noImplicitAny && !returnTypeNode) { - switch (node.kind) { - case SyntaxKind.ConstructSignature: - error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); - break; - case SyntaxKind.CallSignature: - error(node, Diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); - break; - } - } - - if (returnTypeNode) { - const functionFlags = getFunctionFlags(node); - if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Generator)) === FunctionFlags.Generator) { - const returnType = getTypeFromTypeNode(returnTypeNode); - if (returnType === voidType) { - error(returnTypeNode, Diagnostics.A_generator_cannot_have_a_void_type_annotation); - } - else { - // Naively, one could check that Generator is assignable to the return type annotation. - // However, that would not catch the error in the following case. - // - // interface BadGenerator extends Iterable, Iterator { } - // function* g(): BadGenerator { } // Iterable and Iterator have different types! - // - const generatorYieldType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, returnType, (functionFlags & FunctionFlags.Async) !== 0) || anyType; - const generatorReturnType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, (functionFlags & FunctionFlags.Async) !== 0) || generatorYieldType; - const generatorNextType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, (functionFlags & FunctionFlags.Async) !== 0) || unknownType; - const generatorInstantiation = createGeneratorReturnType(generatorYieldType, generatorReturnType, generatorNextType, !!(functionFlags & FunctionFlags.Async)); - checkTypeAssignableTo(generatorInstantiation, returnType, returnTypeNode); - } - } - else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { - checkAsyncFunctionReturnType(node, returnTypeNode); - } - } - if (node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.JSDocFunctionType) { - registerForUnusedIdentifiersCheck(node); - } - } - } - - function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) { - const instanceNames = createUnderscoreEscapedMap(); - const staticNames = createUnderscoreEscapedMap(); - // instance and static private identifiers share the same scope - const privateIdentifiers = createUnderscoreEscapedMap(); - for (const member of node.members) { - if (member.kind === SyntaxKind.Constructor) { - for (const param of (member as ConstructorDeclaration).parameters) { - if (isParameterPropertyDeclaration(param, member) && !isBindingPattern(param.name)) { - addName(instanceNames, param.name, param.name.escapedText, DeclarationMeaning.GetOrSetAccessor); - } - } - } - else { - const isStatic = hasModifier(member, ModifierFlags.Static); - const name = member.name; - if (!name) { - return; - } - const names = - isPrivateIdentifier(name) ? privateIdentifiers : - isStatic ? staticNames : - instanceNames; - const memberName = name && getPropertyNameForPropertyNameNode(name); - if (memberName) { - switch (member.kind) { - case SyntaxKind.GetAccessor: - addName(names, name, memberName, DeclarationMeaning.GetAccessor); - break; - - case SyntaxKind.SetAccessor: - addName(names, name, memberName, DeclarationMeaning.SetAccessor); - break; - - case SyntaxKind.PropertyDeclaration: - addName(names, name, memberName, DeclarationMeaning.GetOrSetAccessor); - break; - - case SyntaxKind.MethodDeclaration: - addName(names, name, memberName, DeclarationMeaning.Method); - break; - } - } - } - } - - function addName(names: UnderscoreEscapedMap, location: Node, name: __String, meaning: DeclarationMeaning) { - const prev = names.get(name); - if (prev) { - if (prev & DeclarationMeaning.Method) { - if (meaning !== DeclarationMeaning.Method) { - error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); - } - } - else if (prev & meaning) { - error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); - } - else { - names.set(name, prev | meaning); - } - } - else { - names.set(name, meaning); - } - } - } - - /** - * Static members being set on a constructor function may conflict with built-in properties - * of Function. Esp. in ECMAScript 5 there are non-configurable and non-writable - * built-in properties. This check issues a transpile error when a class has a static - * member with the same name as a non-writable built-in property. - * - * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3 - * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5 - * @see http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-constructor - * @see http://www.ecma-international.org/ecma-262/6.0/#sec-function-instances - */ - function checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) { - for (const member of node.members) { - const memberNameNode = member.name; - const isStatic = hasModifier(member, ModifierFlags.Static); - if (isStatic && memberNameNode) { - const memberName = getPropertyNameForPropertyNameNode(memberNameNode); - switch (memberName) { - case "name": - case "length": - case "caller": - case "arguments": - case "prototype": - const message = Diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1; - const className = getNameOfSymbolAsWritten(getSymbolOfNode(node)); - error(memberNameNode, message, memberName, className); - break; - } - } - } - } - - function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { - const names = createMap(); - for (const member of node.members) { - if (member.kind === SyntaxKind.PropertySignature) { - let memberName: string; - const name = member.name!; - switch (name.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - memberName = name.text; - break; - case SyntaxKind.Identifier: - memberName = idText(name); - break; - default: - continue; - } - - if (names.get(memberName)) { - error(getNameOfDeclaration(member.symbol.valueDeclaration), Diagnostics.Duplicate_identifier_0, memberName); - error(member.name, Diagnostics.Duplicate_identifier_0, memberName); - } - else { - names.set(memberName, true); - } - } - } - } - - function checkTypeForDuplicateIndexSignatures(node: Node) { - if (node.kind === SyntaxKind.InterfaceDeclaration) { - const nodeSymbol = getSymbolOfNode(node as InterfaceDeclaration); - // in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration - // to prevent this run check only for the first declaration of a given kind - if (nodeSymbol.declarations.length > 0 && nodeSymbol.declarations[0] !== node) { - return; - } - } - - // TypeScript 1.0 spec (April 2014) - // 3.7.4: An object type can contain at most one string index signature and one numeric index signature. - // 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration - const indexSymbol = getIndexSymbol(getSymbolOfNode(node)!); - if (indexSymbol) { - let seenNumericIndexer = false; - let seenStringIndexer = false; - for (const decl of indexSymbol.declarations) { - const declaration = decl; - if (declaration.parameters.length === 1 && declaration.parameters[0].type) { - switch (declaration.parameters[0].type.kind) { - case SyntaxKind.StringKeyword: - if (!seenStringIndexer) { - seenStringIndexer = true; - } - else { - error(declaration, Diagnostics.Duplicate_string_index_signature); - } - break; - case SyntaxKind.NumberKeyword: - if (!seenNumericIndexer) { - seenNumericIndexer = true; - } - else { - error(declaration, Diagnostics.Duplicate_number_index_signature); - } - break; - } - } - } - } - } - - function checkPropertyDeclaration(node: PropertyDeclaration | PropertySignature) { - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node) && !checkGrammarProperty(node)) checkGrammarComputedPropertyName(node.name); - checkVariableLikeDeclaration(node); - - // Private class fields transformation relies on WeakMaps. - if (isPrivateIdentifier(node.name) && languageVersion < ScriptTarget.ESNext) { - for (let lexicalScope = getEnclosingBlockScopeContainer(node); !!lexicalScope; lexicalScope = getEnclosingBlockScopeContainer(lexicalScope)) { - getNodeLinks(lexicalScope).flags |= NodeCheckFlags.ContainsClassWithPrivateIdentifiers; - } - } - } - - function checkPropertySignature(node: PropertySignature) { - if (isPrivateIdentifier(node.name)) { - error(node, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - } - return checkPropertyDeclaration(node); - } - - function checkMethodDeclaration(node: MethodDeclaration | MethodSignature) { - // Grammar checking - if (!checkGrammarMethod(node)) checkGrammarComputedPropertyName(node.name); - - if (isPrivateIdentifier(node.name)) { - error(node, Diagnostics.A_method_cannot_be_named_with_a_private_identifier); - } - - // Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration - checkFunctionOrMethodDeclaration(node); - - // Abstract methods cannot have an implementation. - // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. - if (hasModifier(node, ModifierFlags.Abstract) && node.kind === SyntaxKind.MethodDeclaration && node.body) { - error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name)); - } - } - - function checkConstructorDeclaration(node: ConstructorDeclaration) { - // Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function. - checkSignatureDeclaration(node); - // Grammar check for checking only related to constructorDeclaration - if (!checkGrammarConstructorTypeParameters(node)) checkGrammarConstructorTypeAnnotation(node); - - checkSourceElement(node.body); - - const symbol = getSymbolOfNode(node); - const firstDeclaration = getDeclarationOfKind(symbol, node.kind); - - // Only type check the symbol once - if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(symbol); - } - - // exit early in the case of signature - super checks are not relevant to them - if (nodeIsMissing(node.body)) { - return; - } - - if (!produceDiagnostics) { - return; - } - - function isInstancePropertyWithInitializerOrPrivateIdentifierProperty(n: Node): boolean { - if (isPrivateIdentifierPropertyDeclaration(n)) { - return true; - } - return n.kind === SyntaxKind.PropertyDeclaration && - !hasModifier(n, ModifierFlags.Static) && - !!(n).initializer; - } - - // TS 1.0 spec (April 2014): 8.3.2 - // Constructors of classes with no extends clause may not contain super calls, whereas - // constructors of derived classes must contain at least one super call somewhere in their function body. - const containingClassDecl = node.parent; - if (getClassExtendsHeritageElement(containingClassDecl)) { - captureLexicalThis(node.parent, containingClassDecl); - const classExtendsNull = classDeclarationExtendsNull(containingClassDecl); - const superCall = getSuperCallInConstructor(node); - if (superCall) { - if (classExtendsNull) { - error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null); - } - - // The first statement in the body of a constructor (excluding prologue directives) must be a super call - // if both of the following are true: - // - The containing class is a derived class. - // - The constructor declares parameter properties - // or the containing class declares instance member variables with initializers. - const superCallShouldBeFirst = - some((node.parent).members, isInstancePropertyWithInitializerOrPrivateIdentifierProperty) || - some(node.parameters, p => hasModifier(p, ModifierFlags.ParameterPropertyModifier)); - - // Skip past any prologue directives to find the first statement - // to ensure that it was a super call. - if (superCallShouldBeFirst) { - const statements = node.body!.statements; - let superCallStatement: ExpressionStatement | undefined; - - for (const statement of statements) { - if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((statement).expression)) { - superCallStatement = statement; - break; - } - if (!isPrologueDirective(statement)) { - break; - } - } - if (!superCallStatement) { - error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_parameter_properties_or_private_identifiers); - } - } - } - else if (!classExtendsNull) { - error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call); - } - } - } - - function checkAccessorDeclaration(node: AccessorDeclaration) { - if (produceDiagnostics) { - // Grammar checking accessors - if (!checkGrammarFunctionLikeDeclaration(node) && !checkGrammarAccessor(node)) checkGrammarComputedPropertyName(node.name); - - checkDecorators(node); - checkSignatureDeclaration(node); - if (node.kind === SyntaxKind.GetAccessor) { - if (!(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) { - if (!(node.flags & NodeFlags.HasExplicitReturn)) { - error(node.name, Diagnostics.A_get_accessor_must_return_a_value); - } - } - } - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - } - if (isPrivateIdentifier(node.name)) { - error(node.name, Diagnostics.An_accessor_cannot_be_named_with_a_private_identifier); - } - if (!hasNonBindableDynamicName(node)) { - // TypeScript 1.0 spec (April 2014): 8.4.3 - // Accessors for the same member name must specify the same accessibility. - const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - const otherAccessor = getDeclarationOfKind(getSymbolOfNode(node), otherKind); - if (otherAccessor) { - const nodeFlags = getModifierFlags(node); - const otherFlags = getModifierFlags(otherAccessor); - if ((nodeFlags & ModifierFlags.AccessibilityModifier) !== (otherFlags & ModifierFlags.AccessibilityModifier)) { - error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); - } - if ((nodeFlags & ModifierFlags.Abstract) !== (otherFlags & ModifierFlags.Abstract)) { - error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract); - } - - // TypeScript 1.0 spec (April 2014): 4.5 - // If both accessors include type annotations, the specified types must be identical. - checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_same_type); - checkAccessorDeclarationTypesIdentical(node, otherAccessor, getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); - } - } - const returnType = getTypeOfAccessors(getSymbolOfNode(node)); - if (node.kind === SyntaxKind.GetAccessor) { - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); - } - } - checkSourceElement(node.body); - } - - function checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type | undefined, message: DiagnosticMessage) { - const firstType = getAnnotatedType(first); - const secondType = getAnnotatedType(second); - if (firstType && secondType && !isTypeIdenticalTo(firstType, secondType)) { - error(first, message); - } - } - - function checkMissingDeclaration(node: Node) { - checkDecorators(node); - } - - function getEffectiveTypeArguments(node: TypeReferenceNode | ExpressionWithTypeArguments, typeParameters: readonly TypeParameter[]): Type[] { - return fillMissingTypeArguments(map(node.typeArguments!, getTypeFromTypeNode), typeParameters, - getMinTypeArgumentCount(typeParameters), isInJSFile(node)); - } - - function checkTypeArgumentConstraints(node: TypeReferenceNode | ExpressionWithTypeArguments, typeParameters: readonly TypeParameter[]): boolean { - let typeArguments: Type[] | undefined; - let mapper: TypeMapper | undefined; - let result = true; - for (let i = 0; i < typeParameters.length; i++) { - const constraint = getConstraintOfTypeParameter(typeParameters[i]); - if (constraint) { - if (!typeArguments) { - typeArguments = getEffectiveTypeArguments(node, typeParameters); - mapper = createTypeMapper(typeParameters, typeArguments); - } - result = result && checkTypeAssignableTo( - typeArguments[i], - instantiateType(constraint, mapper), - node.typeArguments![i], - Diagnostics.Type_0_does_not_satisfy_the_constraint_1); - } - } - return result; - } - - function getTypeParametersForTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments) { - const type = getTypeFromTypeReference(node); - if (type !== errorType) { - const symbol = getNodeLinks(node).resolvedSymbol; - if (symbol) { - return symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters || - (getObjectFlags(type) & ObjectFlags.Reference ? (type).target.localTypeParameters : undefined); - } - } - return undefined; - } - - function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) { - checkGrammarTypeArguments(node, node.typeArguments); - if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJSFile(node) && !isInJSDoc(node)) { - grammarErrorAtPos(node, node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); - } - forEach(node.typeArguments, checkSourceElement); - const type = getTypeFromTypeReference(node); - if (type !== errorType) { - if (node.typeArguments && produceDiagnostics) { - const typeParameters = getTypeParametersForTypeReference(node); - if (typeParameters) { - checkTypeArgumentConstraints(node, typeParameters); - } - } - if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol!.flags & SymbolFlags.EnumMember) { - error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type)); - } - } - } - - function getTypeArgumentConstraint(node: TypeNode): Type | undefined { - const typeReferenceNode = tryCast(node.parent, isTypeReferenceType); - if (!typeReferenceNode) return undefined; - const typeParameters = getTypeParametersForTypeReference(typeReferenceNode)!; // TODO: GH#18217 - const constraint = getConstraintOfTypeParameter(typeParameters[typeReferenceNode.typeArguments!.indexOf(node)]); - return constraint && instantiateType(constraint, createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReferenceNode, typeParameters))); - } - - function checkTypeQuery(node: TypeQueryNode) { - getTypeFromTypeQueryNode(node); - } - - function checkTypeLiteral(node: TypeLiteralNode) { - forEach(node.members, checkSourceElement); - if (produceDiagnostics) { - const type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); - checkIndexConstraints(type); - checkTypeForDuplicateIndexSignatures(node); - checkObjectTypeForDuplicateDeclarations(node); - } - } - - function checkArrayType(node: ArrayTypeNode) { - checkSourceElement(node.elementType); - } - - function checkTupleType(node: TupleTypeNode) { - const elementTypes = node.elementTypes; - let seenOptionalElement = false; - for (let i = 0; i < elementTypes.length; i++) { - const e = elementTypes[i]; - if (e.kind === SyntaxKind.RestType) { - if (i !== elementTypes.length - 1) { - grammarErrorOnNode(e, Diagnostics.A_rest_element_must_be_last_in_a_tuple_type); - break; - } - if (!isArrayType(getTypeFromTypeNode((e).type))) { - error(e, Diagnostics.A_rest_element_type_must_be_an_array_type); - } - } - else if (e.kind === SyntaxKind.OptionalType) { - seenOptionalElement = true; - } - else if (seenOptionalElement) { - grammarErrorOnNode(e, Diagnostics.A_required_element_cannot_follow_an_optional_element); - break; - } - } - forEach(node.elementTypes, checkSourceElement); - } - - function checkUnionOrIntersectionType(node: UnionOrIntersectionTypeNode) { - forEach(node.types, checkSourceElement); - } - - function checkIndexedAccessIndexType(type: Type, accessNode: IndexedAccessTypeNode | ElementAccessExpression) { - if (!(type.flags & TypeFlags.IndexedAccess)) { - return type; - } - // Check if the index type is assignable to 'keyof T' for the object type. - const objectType = (type).objectType; - const indexType = (type).indexType; - if (isTypeAssignableTo(indexType, getIndexType(objectType, /*stringsOnly*/ false))) { - if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && - getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType) & MappedTypeModifiers.IncludeReadonly) { - error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType)); - } - return type; - } - // Check if we're indexing with a numeric type and if either object or index types - // is a generic type with a constraint that has a numeric index signature. - const apparentObjectType = getApparentType(objectType); - if (getIndexInfoOfType(apparentObjectType, IndexKind.Number) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { - return type; - } - if (isGenericObjectType(objectType)) { - const propertyName = getPropertyNameFromIndex(indexType, accessNode); - if (propertyName) { - const propertySymbol = forEachType(apparentObjectType, t => getPropertyOfType(t, propertyName)); - if (propertySymbol && getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.NonPublicAccessibilityModifier) { - error(accessNode, Diagnostics.Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, unescapeLeadingUnderscores(propertyName)); - return errorType; - } - } - } - error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType)); - return errorType; - } - - function checkIndexedAccessType(node: IndexedAccessTypeNode) { - checkSourceElement(node.objectType); - checkSourceElement(node.indexType); - checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node); - } - - function checkMappedType(node: MappedTypeNode) { - checkSourceElement(node.typeParameter); - checkSourceElement(node.type); - - if (!node.type) { - reportImplicitAny(node, anyType); - } - - const type = getTypeFromMappedTypeNode(node); - const constraintType = getConstraintTypeFromMappedType(type); - checkTypeAssignableTo(constraintType, keyofConstraintType, getEffectiveConstraintOfTypeParameter(node.typeParameter)); - } - - function checkThisType(node: ThisTypeNode) { - getTypeFromThisTypeNode(node); - } - - function checkTypeOperator(node: TypeOperatorNode) { - checkGrammarTypeOperatorNode(node); - checkSourceElement(node.type); - } - - function checkConditionalType(node: ConditionalTypeNode) { - forEachChild(node, checkSourceElement); - } - - function checkInferType(node: InferTypeNode) { - if (!findAncestor(node, n => n.parent && n.parent.kind === SyntaxKind.ConditionalType && (n.parent).extendsType === n)) { - grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type); - } - checkSourceElement(node.typeParameter); - registerForUnusedIdentifiersCheck(node); - } - - function checkImportType(node: ImportTypeNode) { - checkSourceElement(node.argument); - getTypeFromTypeNode(node); - } - - function isPrivateWithinAmbient(node: Node): boolean { - return hasModifier(node, ModifierFlags.Private) && !!(node.flags & NodeFlags.Ambient); - } - - function getEffectiveDeclarationFlags(n: Declaration, flagsToCheck: ModifierFlags): ModifierFlags { - let flags = getCombinedModifierFlags(n); - - // children of classes (even ambient classes) should not be marked as ambient or export - // because those flags have no useful semantics there. - if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && - n.parent.kind !== SyntaxKind.ClassDeclaration && - n.parent.kind !== SyntaxKind.ClassExpression && - n.flags & NodeFlags.Ambient) { - if (!(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) { - // It is nested in an ambient context, which means it is automatically exported - flags |= ModifierFlags.Export; - } - flags |= ModifierFlags.Ambient; - } - - return flags & flagsToCheck; - } - - function checkFunctionOrConstructorSymbol(symbol: Symbol): void { - if (!produceDiagnostics) { - return; - } - - function getCanonicalOverload(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined): Declaration { - // Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration - // Error on all deviations from this canonical set of flags - // The caveat is that if some overloads are defined in lib.d.ts, we don't want to - // report the errors on those. To achieve this, we will say that the implementation is - // the canonical signature only if it is in the same container as the first overload - const implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent; - return implementationSharesContainerWithFirstOverload ? implementation! : overloads[0]; - } - - function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void { - // Error if some overloads have a flag that is not shared by all overloads. To find the - // deviations, we XOR someOverloadFlags with allOverloadFlags - const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags; - if (someButNotAllOverloadFlags !== 0) { - const canonicalFlags = getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck); - - forEach(overloads, o => { - const deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags; - if (deviation & ModifierFlags.Export) { - error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_exported_or_non_exported); - } - else if (deviation & ModifierFlags.Ambient) { - error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient); - } - else if (deviation & (ModifierFlags.Private | ModifierFlags.Protected)) { - error(getNameOfDeclaration(o) || o, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected); - } - else if (deviation & ModifierFlags.Abstract) { - error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract); - } - }); - } - } - - function checkQuestionTokenAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, someHaveQuestionToken: boolean, allHaveQuestionToken: boolean): void { - if (someHaveQuestionToken !== allHaveQuestionToken) { - const canonicalHasQuestionToken = hasQuestionToken(getCanonicalOverload(overloads, implementation)); - forEach(overloads, o => { - const deviation = hasQuestionToken(o) !== canonicalHasQuestionToken; - if (deviation) { - error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_optional_or_required); - } - }); - } - } - - const flagsToCheck: ModifierFlags = ModifierFlags.Export | ModifierFlags.Ambient | ModifierFlags.Private | ModifierFlags.Protected | ModifierFlags.Abstract; - let someNodeFlags: ModifierFlags = ModifierFlags.None; - let allNodeFlags = flagsToCheck; - let someHaveQuestionToken = false; - let allHaveQuestionToken = true; - let hasOverloads = false; - let bodyDeclaration: FunctionLikeDeclaration | undefined; - let lastSeenNonAmbientDeclaration: FunctionLikeDeclaration | undefined; - let previousDeclaration: SignatureDeclaration | undefined; - - const declarations = symbol.declarations; - const isConstructor = (symbol.flags & SymbolFlags.Constructor) !== 0; - - function reportImplementationExpectedError(node: SignatureDeclaration): void { - if (node.name && nodeIsMissing(node.name)) { - return; - } - - let seen = false; - const subsequentNode = forEachChild(node.parent, c => { - if (seen) { - return c; - } - else { - seen = c === node; - } - }); - // We may be here because of some extra nodes between overloads that could not be parsed into a valid node. - // In this case the subsequent node is not really consecutive (.pos !== node.end), and we must ignore it here. - if (subsequentNode && subsequentNode.pos === node.end) { - if (subsequentNode.kind === node.kind) { - const errorNode: Node = (subsequentNode).name || subsequentNode; - const subsequentName = (subsequentNode).name; - if (node.name && subsequentName && ( - // both are private identifiers - isPrivateIdentifier(node.name) && isPrivateIdentifier(subsequentName) && node.name.escapedText === subsequentName.escapedText || - // Both are computed property names - // TODO: GH#17345: These are methods, so handle computed name case. (`Always allowing computed property names is *not* the correct behavior!) - isComputedPropertyName(node.name) && isComputedPropertyName(subsequentName) || - // Both are literal property names that are the same. - isPropertyNameLiteral(node.name) && isPropertyNameLiteral(subsequentName) && - getEscapedTextOfIdentifierOrLiteral(node.name) === getEscapedTextOfIdentifierOrLiteral(subsequentName) - )) { - const reportError = - (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && - hasModifier(node, ModifierFlags.Static) !== hasModifier(subsequentNode, ModifierFlags.Static); - // we can get here in two cases - // 1. mixed static and instance class members - // 2. something with the same name was defined before the set of overloads that prevents them from merging - // here we'll report error only for the first case since for second we should already report error in binder - if (reportError) { - const diagnostic = hasModifier(node, ModifierFlags.Static) ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; - error(errorNode, diagnostic); - } - return; - } - if (nodeIsPresent((subsequentNode).body)) { - error(errorNode, Diagnostics.Function_implementation_name_must_be_0, declarationNameToString(node.name)); - return; - } - } - } - const errorNode: Node = node.name || node; - if (isConstructor) { - error(errorNode, Diagnostics.Constructor_implementation_is_missing); - } - else { - // Report different errors regarding non-consecutive blocks of declarations depending on whether - // the node in question is abstract. - if (hasModifier(node, ModifierFlags.Abstract)) { - error(errorNode, Diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive); - } - else { - error(errorNode, Diagnostics.Function_implementation_is_missing_or_not_immediately_following_the_declaration); - } - } - } - - let duplicateFunctionDeclaration = false; - let multipleConstructorImplementation = false; - let hasNonAmbientClass = false; - for (const current of declarations) { - const node = current; - const inAmbientContext = node.flags & NodeFlags.Ambient; - const inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext; - if (inAmbientContextOrInterface) { - // check if declarations are consecutive only if they are non-ambient - // 1. ambient declarations can be interleaved - // i.e. this is legal - // declare function foo(); - // declare function bar(); - // declare function foo(); - // 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one - previousDeclaration = undefined; - } - - if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && !inAmbientContext) { - hasNonAmbientClass = true; - } - - if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) { - const currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck); - someNodeFlags |= currentNodeFlags; - allNodeFlags &= currentNodeFlags; - someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node); - allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node); - - if (nodeIsPresent((node as FunctionLikeDeclaration).body) && bodyDeclaration) { - if (isConstructor) { - multipleConstructorImplementation = true; - } - else { - duplicateFunctionDeclaration = true; - } - } - else if (previousDeclaration && previousDeclaration.parent === node.parent && previousDeclaration.end !== node.pos) { - reportImplementationExpectedError(previousDeclaration); - } - - if (nodeIsPresent((node as FunctionLikeDeclaration).body)) { - if (!bodyDeclaration) { - bodyDeclaration = node as FunctionLikeDeclaration; - } - } - else { - hasOverloads = true; - } - - previousDeclaration = node; - - if (!inAmbientContextOrInterface) { - lastSeenNonAmbientDeclaration = node as FunctionLikeDeclaration; - } - } - } - - if (multipleConstructorImplementation) { - forEach(declarations, declaration => { - error(declaration, Diagnostics.Multiple_constructor_implementations_are_not_allowed); - }); - } - - if (duplicateFunctionDeclaration) { - forEach(declarations, declaration => { - error(getNameOfDeclaration(declaration), Diagnostics.Duplicate_function_implementation); - }); - } - - if (hasNonAmbientClass && !isConstructor && symbol.flags & SymbolFlags.Function) { - // A non-ambient class cannot be an implementation for a non-constructor function/class merge - // TODO: The below just replicates our older error from when classes and functions were - // entirely unable to merge - a more helpful message like "Class declaration cannot implement overload list" - // might be warranted. :shrug: - forEach(declarations, declaration => { - addDuplicateDeclarationError(getNameOfDeclaration(declaration) || declaration, Diagnostics.Duplicate_identifier_0, symbolName(symbol), filter(declarations, d => d !== declaration)); - }); - } - - // Abstract methods can't have an implementation -- in particular, they don't need one. - if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && - !hasModifier(lastSeenNonAmbientDeclaration, ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) { - reportImplementationExpectedError(lastSeenNonAmbientDeclaration); - } - - if (hasOverloads) { - checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags); - checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken); - - if (bodyDeclaration) { - const signatures = getSignaturesOfSymbol(symbol); - const bodySignature = getSignatureFromDeclaration(bodyDeclaration); - for (const signature of signatures) { - if (!isImplementationCompatibleWithOverload(bodySignature, signature)) { - addRelatedInfo( - error(signature.declaration, Diagnostics.This_overload_signature_is_not_compatible_with_its_implementation_signature), - createDiagnosticForNode(bodyDeclaration, Diagnostics.The_implementation_signature_is_declared_here) - ); - break; - } - } - } - } - } - - function checkExportsOnMergedDeclarations(node: Node): void { - if (!produceDiagnostics) { - return; - } - - // if localSymbol is defined on node then node itself is exported - check is required - let symbol = node.localSymbol; - if (!symbol) { - // local symbol is undefined => this declaration is non-exported. - // however symbol might contain other declarations that are exported - symbol = getSymbolOfNode(node)!; - if (!symbol.exportSymbol) { - // this is a pure local symbol (all declarations are non-exported) - no need to check anything - return; - } - } - - // run the check only for the first declaration in the list - if (getDeclarationOfKind(symbol, node.kind) !== node) { - return; - } - - let exportedDeclarationSpaces = DeclarationSpaces.None; - let nonExportedDeclarationSpaces = DeclarationSpaces.None; - let defaultExportedDeclarationSpaces = DeclarationSpaces.None; - for (const d of symbol.declarations) { - const declarationSpaces = getDeclarationSpaces(d); - const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default); - - if (effectiveDeclarationFlags & ModifierFlags.Export) { - if (effectiveDeclarationFlags & ModifierFlags.Default) { - defaultExportedDeclarationSpaces |= declarationSpaces; - } - else { - exportedDeclarationSpaces |= declarationSpaces; - } - } - else { - nonExportedDeclarationSpaces |= declarationSpaces; - } - } - - // Spaces for anything not declared a 'default export'. - const nonDefaultExportedDeclarationSpaces = exportedDeclarationSpaces | nonExportedDeclarationSpaces; - - const commonDeclarationSpacesForExportsAndLocals = exportedDeclarationSpaces & nonExportedDeclarationSpaces; - const commonDeclarationSpacesForDefaultAndNonDefault = defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces; - - if (commonDeclarationSpacesForExportsAndLocals || commonDeclarationSpacesForDefaultAndNonDefault) { - // declaration spaces for exported and non-exported declarations intersect - for (const d of symbol.declarations) { - const declarationSpaces = getDeclarationSpaces(d); - - const name = getNameOfDeclaration(d); - // Only error on the declarations that contributed to the intersecting spaces. - if (declarationSpaces & commonDeclarationSpacesForDefaultAndNonDefault) { - error(name, Diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, declarationNameToString(name)); - } - else if (declarationSpaces & commonDeclarationSpacesForExportsAndLocals) { - error(name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, declarationNameToString(name)); - } - } - } - - function getDeclarationSpaces(decl: Declaration): DeclarationSpaces { - let d = decl as Node; - switch (d.kind) { - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - - // A jsdoc typedef and callback are, by definition, type aliases. - // falls through - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - return DeclarationSpaces.ExportType; - case SyntaxKind.ModuleDeclaration: - return isAmbientModule(d as ModuleDeclaration) || getModuleInstanceState(d as ModuleDeclaration) !== ModuleInstanceState.NonInstantiated - ? DeclarationSpaces.ExportNamespace | DeclarationSpaces.ExportValue - : DeclarationSpaces.ExportNamespace; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - return DeclarationSpaces.ExportType | DeclarationSpaces.ExportValue; - case SyntaxKind.SourceFile: - return DeclarationSpaces.ExportType | DeclarationSpaces.ExportValue | DeclarationSpaces.ExportNamespace; - case SyntaxKind.ExportAssignment: - // Export assigned entity name expressions act as aliases and should fall through, otherwise they export values - if (!isEntityNameExpression((d as ExportAssignment).expression)) { - return DeclarationSpaces.ExportValue; - } - d = (d as ExportAssignment).expression; - - // The below options all declare an Alias, which is allowed to merge with other values within the importing module. - // falls through - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportClause: - let result = DeclarationSpaces.None; - const target = resolveAlias(getSymbolOfNode(d)!); - forEach(target.declarations, d => { result |= getDeclarationSpaces(d); }); - return result; - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ImportSpecifier: // https://github.com/Microsoft/TypeScript/pull/7591 - return DeclarationSpaces.ExportValue; - default: - return Debug.failBadSyntaxKind(d); - } - } - } - - function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined { - const promisedType = getPromisedTypeOfPromise(type, errorNode); - return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0); - } - - /** - * Gets the "promised type" of a promise. - * @param type The type of the promise. - * @remarks The "promised type" of a type is the type of the "value" parameter of the "onfulfilled" callback. - */ - function getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined { - // - // { // promise - // then( // thenFunction - // onfulfilled: ( // onfulfilledParameterType - // value: T // valueParameterType - // ) => any - // ): any; - // } - // - - if (isTypeAny(promise)) { - return undefined; - } - - const typeAsPromise = promise; - if (typeAsPromise.promisedTypeOfPromise) { - return typeAsPromise.promisedTypeOfPromise; - } - - if (isReferenceToType(promise, getGlobalPromiseType(/*reportErrors*/ false))) { - return typeAsPromise.promisedTypeOfPromise = getTypeArguments(promise)[0]; - } - - const thenFunction = getTypeOfPropertyOfType(promise, "then" as __String)!; // TODO: GH#18217 - if (isTypeAny(thenFunction)) { - return undefined; - } - - const thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray; - if (thenSignatures.length === 0) { - if (errorNode) { - error(errorNode, Diagnostics.A_promise_must_have_a_then_method); - } - return undefined; - } - - const onfulfilledParameterType = getTypeWithFacts(getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature)), TypeFacts.NEUndefinedOrNull); - if (isTypeAny(onfulfilledParameterType)) { - return undefined; - } - - const onfulfilledParameterSignatures = getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call); - if (onfulfilledParameterSignatures.length === 0) { - if (errorNode) { - error(errorNode, Diagnostics.The_first_parameter_of_the_then_method_of_a_promise_must_be_a_callback); - } - return undefined; - } - - return typeAsPromise.promisedTypeOfPromise = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature), UnionReduction.Subtype); - } - - /** - * Gets the "awaited type" of a type. - * @param type The type to await. - * @remarks The "awaited type" of an expression is its "promised type" if the expression is a - * Promise-like type; otherwise, it is the type of the expression. This is used to reflect - * The runtime behavior of the `await` keyword. - */ - function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type { - const awaitedType = getAwaitedType(type, errorNode, diagnosticMessage, arg0); - return awaitedType || errorType; - } - - function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined { - const typeAsAwaitable = type; - if (typeAsAwaitable.awaitedTypeOfType) { - return typeAsAwaitable.awaitedTypeOfType; - } - - if (isTypeAny(type)) { - return typeAsAwaitable.awaitedTypeOfType = type; - } - - if (type.flags & TypeFlags.Union) { - let types: Type[] | undefined; - for (const constituentType of (type).types) { - types = append(types, getAwaitedType(constituentType, errorNode, diagnosticMessage, arg0)); - } - - if (!types) { - return undefined; - } - - return typeAsAwaitable.awaitedTypeOfType = getUnionType(types); - } - - const promisedType = getPromisedTypeOfPromise(type); - if (promisedType) { - if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) { - // Verify that we don't have a bad actor in the form of a promise whose - // promised type is the same as the promise type, or a mutually recursive - // promise. If so, we return undefined as we cannot guess the shape. If this - // were the actual case in the JavaScript, this Promise would never resolve. - // - // An example of a bad actor with a singly-recursive promise type might - // be: - // - // interface BadPromise { - // then( - // onfulfilled: (value: BadPromise) => any, - // onrejected: (error: any) => any): BadPromise; - // } - // The above interface will pass the PromiseLike check, and return a - // promised type of `BadPromise`. Since this is a self reference, we - // don't want to keep recursing ad infinitum. - // - // An example of a bad actor in the form of a mutually-recursive - // promise type might be: - // - // interface BadPromiseA { - // then( - // onfulfilled: (value: BadPromiseB) => any, - // onrejected: (error: any) => any): BadPromiseB; - // } - // - // interface BadPromiseB { - // then( - // onfulfilled: (value: BadPromiseA) => any, - // onrejected: (error: any) => any): BadPromiseA; - // } - // - if (errorNode) { - error(errorNode, Diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method); - } - return undefined; - } - - // Keep track of the type we're about to unwrap to avoid bad recursive promise types. - // See the comments above for more information. - awaitedTypeStack.push(type.id); - const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0); - awaitedTypeStack.pop(); - - if (!awaitedType) { - return undefined; - } - - return typeAsAwaitable.awaitedTypeOfType = awaitedType; - } - - // The type was not a promise, so it could not be unwrapped any further. - // As long as the type does not have a callable "then" property, it is - // safe to return the type; otherwise, an error will be reported in - // the call to getNonThenableType and we will return undefined. - // - // An example of a non-promise "thenable" might be: - // - // await { then(): void {} } - // - // The "thenable" does not match the minimal definition for a promise. When - // a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise - // will never settle. We treat this as an error to help flag an early indicator - // of a runtime problem. If the user wants to return this value from an async - // function, they would need to wrap it in some other value. If they want it to - // be treated as a promise, they can cast to . - const thenFunction = getTypeOfPropertyOfType(type, "then" as __String); - if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) { - if (errorNode) { - if (!diagnosticMessage) return Debug.fail(); - error(errorNode, diagnosticMessage, arg0); - } - return undefined; - } - - return typeAsAwaitable.awaitedTypeOfType = type; - } - - /** - * Checks the return type of an async function to ensure it is a compatible - * Promise implementation. - * - * This checks that an async function has a valid Promise-compatible return type. - * An async function has a valid Promise-compatible return type if the resolved value - * of the return type has a construct signature that takes in an `initializer` function - * that in turn supplies a `resolve` function as one of its arguments and results in an - * object with a callable `then` signature. - * - * @param node The signature to check - */ - function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration | MethodSignature, returnTypeNode: TypeNode) { - // As part of our emit for an async function, we will need to emit the entity name of - // the return type annotation as an expression. To meet the necessary runtime semantics - // for __awaiter, we must also check that the type of the declaration (e.g. the static - // side or "constructor" of the promise type) is compatible `PromiseConstructorLike`. - // - // An example might be (from lib.es6.d.ts): - // - // interface Promise { ... } - // interface PromiseConstructor { - // new (...): Promise; - // } - // declare var Promise: PromiseConstructor; - // - // When an async function declares a return type annotation of `Promise`, we - // need to get the type of the `Promise` variable declaration above, which would - // be `PromiseConstructor`. - // - // The same case applies to a class: - // - // declare class Promise { - // constructor(...); - // then(...): Promise; - // } - // - const returnType = getTypeFromTypeNode(returnTypeNode); - - if (languageVersion >= ScriptTarget.ES2015) { - if (returnType === errorType) { - return; - } - const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true); - if (globalPromiseType !== emptyGenericType && !isReferenceToType(returnType, globalPromiseType)) { - // The promise type was not a valid type reference to the global promise type, so we - // report an error and return the unknown type. - error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type); - return; - } - } - else { - // Always mark the type node as referenced if it points to a value - markTypeNodeAsReferenced(returnTypeNode); - - if (returnType === errorType) { - return; - } - - const promiseConstructorName = getEntityNameFromTypeNode(returnTypeNode); - if (promiseConstructorName === undefined) { - error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(returnType)); - return; - } - - const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true); - const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : errorType; - if (promiseConstructorType === errorType) { - if (promiseConstructorName.kind === SyntaxKind.Identifier && promiseConstructorName.escapedText === "Promise" && getTargetType(returnType) === getGlobalPromiseType(/*reportErrors*/ false)) { - error(returnTypeNode, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option); - } - else { - error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); - } - return; - } - - const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(/*reportErrors*/ true); - if (globalPromiseConstructorLikeType === emptyObjectType) { - // If we couldn't resolve the global PromiseConstructorLike type we cannot verify - // compatibility with __awaiter. - error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); - return; - } - - if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, returnTypeNode, - Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) { - return; - } - - // Verify there is no local declaration that could collide with the promise constructor. - const rootName = promiseConstructorName && getFirstIdentifier(promiseConstructorName); - const collidingSymbol = getSymbol(node.locals!, rootName.escapedText, SymbolFlags.Value); - if (collidingSymbol) { - error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, - idText(rootName), - entityNameToString(promiseConstructorName)); - return; - } - } - checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - } - - /** Check a decorator */ - function checkDecorator(node: Decorator): void { - const signature = getResolvedSignature(node); - const returnType = getReturnTypeOfSignature(signature); - if (returnType.flags & TypeFlags.Any) { - return; - } - - let expectedReturnType: Type; - const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node); - let errorInfo: DiagnosticMessageChain | undefined; - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - const classSymbol = getSymbolOfNode(node.parent); - const classConstructorType = getTypeOfSymbol(classSymbol); - expectedReturnType = getUnionType([classConstructorType, voidType]); - break; - - case SyntaxKind.Parameter: - expectedReturnType = voidType; - errorInfo = chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics.The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any); - - break; - - case SyntaxKind.PropertyDeclaration: - expectedReturnType = voidType; - errorInfo = chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics.The_return_type_of_a_property_decorator_function_must_be_either_void_or_any); - break; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - const methodType = getTypeOfNode(node.parent); - const descriptorType = createTypedPropertyDescriptorType(methodType); - expectedReturnType = getUnionType([descriptorType, voidType]); - break; - - default: - return Debug.fail(); - } - - checkTypeAssignableTo( - returnType, - expectedReturnType, - node, - headMessage, - () => errorInfo); - } - - /** - * If a TypeNode can be resolved to a value symbol imported from an external module, it is - * marked as referenced to prevent import elision. - */ - function markTypeNodeAsReferenced(node: TypeNode) { - markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node)); - } - - function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression | undefined) { - if (!typeName) return; - - const rootName = getFirstIdentifier(typeName); - const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias; - const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isRefernce*/ true); - if (rootSymbol - && rootSymbol.flags & SymbolFlags.Alias - && symbolIsValue(rootSymbol) - && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) { - markAliasSymbolAsReferenced(rootSymbol); - } - } - - /** - * This function marks the type used for metadata decorator as referenced if it is import - * from external module. - * This is different from markTypeNodeAsReferenced because it tries to simplify type nodes in - * union and intersection type - * @param node - */ - function markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode | undefined): void { - const entityName = getEntityNameForDecoratorMetadata(node); - if (entityName && isEntityName(entityName)) { - markEntityNameOrEntityExpressionAsReference(entityName); - } - } - - function getEntityNameForDecoratorMetadata(node: TypeNode | undefined): EntityName | undefined { - if (node) { - switch (node.kind) { - case SyntaxKind.IntersectionType: - case SyntaxKind.UnionType: - return getEntityNameForDecoratorMetadataFromTypeList((node).types); - - case SyntaxKind.ConditionalType: - return getEntityNameForDecoratorMetadataFromTypeList([(node).trueType, (node).falseType]); - - case SyntaxKind.ParenthesizedType: - return getEntityNameForDecoratorMetadata((node).type); - - case SyntaxKind.TypeReference: - return (node).typeName; - } - } - } - - function getEntityNameForDecoratorMetadataFromTypeList(types: readonly TypeNode[]): EntityName | undefined { - let commonEntityName: EntityName | undefined; - for (let typeNode of types) { - while (typeNode.kind === SyntaxKind.ParenthesizedType) { - typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be - } - if (typeNode.kind === SyntaxKind.NeverKeyword) { - continue; // Always elide `never` from the union/intersection if possible - } - if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { - continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks - } - const individualEntityName = getEntityNameForDecoratorMetadata(typeNode); - if (!individualEntityName) { - // Individual is something like string number - // So it would be serialized to either that type or object - // Safe to return here - return undefined; - } - - if (commonEntityName) { - // Note this is in sync with the transformation that happens for type node. - // Keep this in sync with serializeUnionOrIntersectionType - // Verify if they refer to same entity and is identifier - // return undefined if they dont match because we would emit object - if (!isIdentifier(commonEntityName) || - !isIdentifier(individualEntityName) || - commonEntityName.escapedText !== individualEntityName.escapedText) { - return undefined; - } - } - else { - commonEntityName = individualEntityName; - } - } - return commonEntityName; - } - - function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode | undefined { - const typeNode = getEffectiveTypeAnnotationNode(node); - return isRestParameter(node) ? getRestParameterElementType(typeNode) : typeNode; - } - - /** Check the decorators of a node */ - function checkDecorators(node: Node): void { - if (!node.decorators) { - return; - } - - // skip this check for nodes that cannot have decorators. These should have already had an error reported by - // checkGrammarDecorators. - if (!nodeCanBeDecorated(node, node.parent, node.parent.parent)) { - return; - } - - if (!compilerOptions.experimentalDecorators) { - error(node, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_in_your_tsconfig_or_jsconfig_to_remove_this_warning); - } - - const firstDecorator = node.decorators[0]; - checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Decorate); - if (node.kind === SyntaxKind.Parameter) { - checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Param); - } - - if (compilerOptions.emitDecoratorMetadata) { - checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Metadata); - - // we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator. - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - const constructor = getFirstConstructorWithBody(node); - if (constructor) { - for (const parameter of constructor.parameters) { - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); - } - } - break; - - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - const otherAccessor = getDeclarationOfKind(getSymbolOfNode(node as AccessorDeclaration), otherKind); - markDecoratorMedataDataTypeNodeAsReferenced(getAnnotatedAccessorTypeNode(node as AccessorDeclaration) || otherAccessor && getAnnotatedAccessorTypeNode(otherAccessor)); - break; - case SyntaxKind.MethodDeclaration: - for (const parameter of (node).parameters) { - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); - } - - markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveReturnTypeNode(node)); - break; - - case SyntaxKind.PropertyDeclaration: - markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveTypeAnnotationNode(node)); - break; - - case SyntaxKind.Parameter: - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(node)); - const containingSignature = (node as ParameterDeclaration).parent; - for (const parameter of containingSignature.parameters) { - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); - } - break; - } - } - - forEach(node.decorators, checkDecorator); - } - - function checkFunctionDeclaration(node: FunctionDeclaration): void { - if (produceDiagnostics) { - checkFunctionOrMethodDeclaration(node); - checkGrammarForGenerator(node); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name!); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name!); - } - } - - function checkJSDocTypeAliasTag(node: JSDocTypedefTag | JSDocCallbackTag) { - if (!node.typeExpression) { - // If the node had `@property` tags, `typeExpression` would have been set to the first property tag. - error(node.name, Diagnostics.JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags); - } - - if (node.name) { - checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); - } - checkSourceElement(node.typeExpression); - } - - function checkJSDocTemplateTag(node: JSDocTemplateTag): void { - checkSourceElement(node.constraint); - for (const tp of node.typeParameters) { - checkSourceElement(tp); - } - } - - function checkJSDocTypeTag(node: JSDocTypeTag) { - checkSourceElement(node.typeExpression); - } - - function checkJSDocParameterTag(node: JSDocParameterTag) { - checkSourceElement(node.typeExpression); - if (!getParameterSymbolFromJSDoc(node)) { - const decl = getHostSignatureFromJSDoc(node); - // don't issue an error for invalid hosts -- just functions -- - // and give a better error message when the host function mentions `arguments` - // but the tag doesn't have an array type - if (decl) { - const i = getJSDocTags(decl).filter(isJSDocParameterTag).indexOf(node); - if (i > -1 && i < decl.parameters.length && isBindingPattern(decl.parameters[i].name)) { - return; - } - if (!containsArgumentsReference(decl)) { - if (isQualifiedName(node.name)) { - error(node.name, - Diagnostics.Qualified_name_0_is_not_allowed_without_a_leading_param_object_1, - entityNameToString(node.name), - entityNameToString(node.name.left)); - } - else { - error(node.name, - Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, - idText(node.name)); - } - } - else if (findLast(getJSDocTags(decl), isJSDocParameterTag) === node && - node.typeExpression && node.typeExpression.type && - !isArrayType(getTypeFromTypeNode(node.typeExpression.type))) { - error(node.name, - Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type, - idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name)); - } - } - } - } - - function checkJSDocFunctionType(node: JSDocFunctionType): void { - if (produceDiagnostics && !node.type && !isJSDocConstructSignature(node)) { - reportImplicitAny(node, anyType); - } - checkSignatureDeclaration(node); - } - - function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { - const classLike = getJSDocHost(node); - if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { - error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName)); - return; - } - - const augmentsTags = getJSDocTags(classLike).filter(isJSDocAugmentsTag); - Debug.assert(augmentsTags.length > 0); - if (augmentsTags.length > 1) { - error(augmentsTags[1], Diagnostics.Class_declarations_cannot_have_more_than_one_augments_or_extends_tag); - } - - const name = getIdentifierFromEntityNameExpression(node.class.expression); - const extend = getClassExtendsHeritageElement(classLike); - if (extend) { - const className = getIdentifierFromEntityNameExpression(extend.expression); - if (className && name.escapedText !== className.escapedText) { - error(name, Diagnostics.JSDoc_0_1_does_not_match_the_extends_2_clause, idText(node.tagName), idText(name), idText(className)); - } - } - } - - function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateIdentifier; - function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateIdentifier | undefined; - function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateIdentifier | undefined { - switch (node.kind) { - case SyntaxKind.Identifier: - return node as Identifier; - case SyntaxKind.PropertyAccessExpression: - return (node as PropertyAccessExpression).name; - default: - return undefined; - } - } - - function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration | MethodSignature): void { - checkDecorators(node); - checkSignatureDeclaration(node); - const functionFlags = getFunctionFlags(node); - - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { - // This check will account for methods in class/interface declarations, - // as well as accessors in classes/object literals - checkComputedPropertyName(node.name); - } - - if (!hasNonBindableDynamicName(node)) { - // first we want to check the local symbol that contain this declaration - // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol - // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode - const symbol = getSymbolOfNode(node); - const localSymbol = node.localSymbol || symbol; - - // Since the javascript won't do semantic analysis like typescript, - // if the javascript file comes before the typescript file and both contain same name functions, - // checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function. - const firstDeclaration = find(localSymbol.declarations, - // Get first non javascript function declaration - declaration => declaration.kind === node.kind && !(declaration.flags & NodeFlags.JavaScriptFile)); - - // Only type check the symbol once - if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(localSymbol); - } - - if (symbol.parent) { - // run check once for the first declaration - if (getDeclarationOfKind(symbol, node.kind) === node) { - // run check on export symbol to check that modifiers agree across all exported declarations - checkFunctionOrConstructorSymbol(symbol); - } - } - } - - const body = node.kind === SyntaxKind.MethodSignature ? undefined : node.body; - checkSourceElement(body); - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, getReturnTypeFromAnnotation(node)); - - if (produceDiagnostics && !getEffectiveReturnTypeNode(node)) { - // Report an implicit any error if there is no body, no explicit return type, and node is not a private method - // in an ambient context - if (nodeIsMissing(body) && !isPrivateWithinAmbient(node)) { - reportImplicitAny(node, anyType); - } - - if (functionFlags & FunctionFlags.Generator && nodeIsPresent(body)) { - // A generator with a body and no type annotation can still cause errors. It can error if the - // yielded values have no common supertype, or it can give an implicit any error if it has no - // yielded values. The only way to trigger these errors is to try checking its return type. - getReturnTypeOfSignature(getSignatureFromDeclaration(node)); - } - } - - // A js function declaration can have a @type tag instead of a return type node, but that type must have a call signature - if (isInJSFile(node)) { - const typeTag = getJSDocTypeTag(node); - if (typeTag && typeTag.typeExpression && !getContextualCallSignature(getTypeFromTypeNode(typeTag.typeExpression), node)) { - error(typeTag, Diagnostics.The_type_of_a_function_declaration_must_match_the_function_s_signature); - } - } - } - - function registerForUnusedIdentifiersCheck(node: PotentiallyUnusedIdentifier): void { - // May be in a call such as getTypeOfNode that happened to call this. But potentiallyUnusedIdentifiers is only defined in the scope of `checkSourceFile`. - if (produceDiagnostics) { - const sourceFile = getSourceFileOfNode(node); - let potentiallyUnusedIdentifiers = allPotentiallyUnusedIdentifiers.get(sourceFile.path); - if (!potentiallyUnusedIdentifiers) { - potentiallyUnusedIdentifiers = []; - allPotentiallyUnusedIdentifiers.set(sourceFile.path, potentiallyUnusedIdentifiers); - } - // TODO: GH#22580 - // Debug.assert(addToSeen(seenPotentiallyUnusedIdentifiers, getNodeId(node)), "Adding potentially-unused identifier twice"); - potentiallyUnusedIdentifiers.push(node); - } - } - - type PotentiallyUnusedIdentifier = - | SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration - | Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement - | Exclude | TypeAliasDeclaration - | InferTypeNode; - - function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: readonly PotentiallyUnusedIdentifier[], addDiagnostic: AddUnusedDiagnostic) { - for (const node of potentiallyUnusedIdentifiers) { - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - checkUnusedClassMembers(node, addDiagnostic); - checkUnusedTypeParameters(node, addDiagnostic); - break; - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.Block: - case SyntaxKind.CaseBlock: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - checkUnusedLocalsAndParameters(node, addDiagnostic); - break; - case SyntaxKind.Constructor: - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - if (node.body) { // Don't report unused parameters in overloads - checkUnusedLocalsAndParameters(node, addDiagnostic); - } - checkUnusedTypeParameters(node, addDiagnostic); - break; - case SyntaxKind.MethodSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.InterfaceDeclaration: - checkUnusedTypeParameters(node, addDiagnostic); - break; - case SyntaxKind.InferType: - checkUnusedInferTypeParameter(node, addDiagnostic); - break; - default: - Debug.assertNever(node, "Node should not have been registered for unused identifiers check"); - } - } - } - - function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) { - const node = getNameOfDeclaration(declaration) || declaration; - const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read; - addDiagnostic(declaration, UnusedKind.Local, createDiagnosticForNode(node, message, name)); - } - - function isIdentifierThatStartsWithUnderscore(node: Node) { - return isIdentifier(node) && idText(node).charCodeAt(0) === CharacterCodes._; - } - - function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression, addDiagnostic: AddUnusedDiagnostic): void { - for (const member of node.members) { - switch (member.kind) { - case SyntaxKind.MethodDeclaration: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - if (member.kind === SyntaxKind.SetAccessor && member.symbol.flags & SymbolFlags.GetAccessor) { - // Already would have reported an error on the getter. - break; - } - const symbol = getSymbolOfNode(member); - if (!symbol.isReferenced && (hasModifier(member, ModifierFlags.Private) || isNamedDeclaration(member) && isPrivateIdentifier(member.name))) { - addDiagnostic(member, UnusedKind.Local, createDiagnosticForNode(member.name!, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolToString(symbol))); - } - break; - case SyntaxKind.Constructor: - for (const parameter of (member).parameters) { - if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) { - addDiagnostic(parameter, UnusedKind.Local, createDiagnosticForNode(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol))); - } - } - break; - case SyntaxKind.IndexSignature: - case SyntaxKind.SemicolonClassElement: - // Can't be private - break; - default: - Debug.fail(); - } - } - } - - function checkUnusedInferTypeParameter(node: InferTypeNode, addDiagnostic: AddUnusedDiagnostic): void { - const { typeParameter } = node; - if (isTypeParameterUnused(typeParameter)) { - addDiagnostic(node, UnusedKind.Parameter, createDiagnosticForNode(node, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(typeParameter.name))); - } - } - - function checkUnusedTypeParameters(node: ClassLikeDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration, addDiagnostic: AddUnusedDiagnostic): void { - // Only report errors on the last declaration for the type parameter container; - // this ensures that all uses have been accounted for. - if (last(getSymbolOfNode(node).declarations) !== node) return; - - const typeParameters = getEffectiveTypeParameterDeclarations(node); - const seenParentsWithEveryUnused = new NodeSet(); - - for (const typeParameter of typeParameters) { - if (!isTypeParameterUnused(typeParameter)) continue; - - const name = idText(typeParameter.name); - const { parent } = typeParameter; - if (parent.kind !== SyntaxKind.InferType && parent.typeParameters!.every(isTypeParameterUnused)) { - if (seenParentsWithEveryUnused.tryAdd(parent)) { - const range = isJSDocTemplateTag(parent) - // Whole @template tag - ? rangeOfNode(parent) - // Include the `<>` in the error message - : rangeOfTypeParameters(parent.typeParameters!); - const only = typeParameters.length === 1; - const message = only ? Diagnostics._0_is_declared_but_its_value_is_never_read : Diagnostics.All_type_parameters_are_unused; - const arg0 = only ? name : undefined; - addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(getSourceFileOfNode(parent), range.pos, range.end - range.pos, message, arg0)); - } - } - else { - addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter, Diagnostics._0_is_declared_but_its_value_is_never_read, name)); - } - } - } - function isTypeParameterUnused(typeParameter: TypeParameterDeclaration): boolean { - return !(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name); - } - - function addToGroup(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void { - const keyString = String(getKey(key)); - const group = map.get(keyString); - if (group) { - group[1].push(value); - } - else { - map.set(keyString, [key, [value]]); - } - } - - function tryGetRootParameterDeclaration(node: Node): ParameterDeclaration | undefined { - return tryCast(getRootDeclaration(node), isParameter); - } - - function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void { - // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value. - const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>(); - const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>(); - const unusedVariables = createMap<[VariableDeclarationList, VariableDeclaration[]]>(); - nodeWithLocals.locals!.forEach(local => { - // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. - // If it's a type parameter merged with a parameter, check if the parameter-side is used. - if (local.flags & SymbolFlags.TypeParameter ? !(local.flags & SymbolFlags.Variable && !(local.isReferenced! & SymbolFlags.Variable)) : local.isReferenced || local.exportSymbol) { - return; - } - - for (const declaration of local.declarations) { - if (isAmbientModule(declaration) || - (isVariableDeclaration(declaration) && isForInOrOfStatement(declaration.parent.parent) || isImportedDeclaration(declaration)) && isIdentifierThatStartsWithUnderscore(declaration.name!)) { - continue; - } - - if (isImportedDeclaration(declaration)) { - addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId); - } - else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) { - // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. - const lastElement = last(declaration.parent.elements); - if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) { - addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); - } - } - else if (isVariableDeclaration(declaration)) { - addToGroup(unusedVariables, declaration.parent, declaration, getNodeId); - } - else { - const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration); - const name = local.valueDeclaration && getNameOfDeclaration(local.valueDeclaration); - if (parameter && name) { - if (!isParameterPropertyDeclaration(parameter, parameter.parent) && !parameterIsThisKeyword(parameter) && !isIdentifierThatStartsWithUnderscore(name)) { - addDiagnostic(parameter, UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); - } - } - else { - errorUnusedLocal(declaration, symbolName(local), addDiagnostic); - } - } - } - }); - unusedImports.forEach(([importClause, unuseds]) => { - const importDecl = importClause.parent; - const nDeclarations = (importClause.name ? 1 : 0) + - (importClause.namedBindings ? - (importClause.namedBindings.kind === SyntaxKind.NamespaceImport ? 1 : importClause.namedBindings.elements.length) - : 0); - if (nDeclarations === unuseds.length) { - addDiagnostic(importDecl, UnusedKind.Local, unuseds.length === 1 - ? createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name!)) - : createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused)); - } - else { - for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name!), addDiagnostic); - } - }); - unusedDestructures.forEach(([bindingPattern, bindingElements]) => { - const kind = tryGetRootParameterDeclaration(bindingPattern.parent) ? UnusedKind.Parameter : UnusedKind.Local; - if (bindingPattern.elements.length === bindingElements.length) { - if (bindingElements.length === 1 && bindingPattern.parent.kind === SyntaxKind.VariableDeclaration && bindingPattern.parent.parent.kind === SyntaxKind.VariableDeclarationList) { - addToGroup(unusedVariables, bindingPattern.parent.parent, bindingPattern.parent, getNodeId); - } - else { - addDiagnostic(bindingPattern, kind, bindingElements.length === 1 - ? createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, bindingNameText(first(bindingElements).name)) - : createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused)); - } - } - else { - for (const e of bindingElements) { - addDiagnostic(e, kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, bindingNameText(e.name))); - } - } - }); - unusedVariables.forEach(([declarationList, declarations]) => { - if (declarationList.declarations.length === declarations.length) { - addDiagnostic(declarationList, UnusedKind.Local, declarations.length === 1 - ? createDiagnosticForNode(first(declarations).name, Diagnostics._0_is_declared_but_its_value_is_never_read, bindingNameText(first(declarations).name)) - : createDiagnosticForNode(declarationList.parent.kind === SyntaxKind.VariableStatement ? declarationList.parent : declarationList, Diagnostics.All_variables_are_unused)); - } - else { - for (const decl of declarations) { - addDiagnostic(decl, UnusedKind.Local, createDiagnosticForNode(decl, Diagnostics._0_is_declared_but_its_value_is_never_read, bindingNameText(decl.name))); - } - } - }); - } - - function bindingNameText(name: BindingName): string { - switch (name.kind) { - case SyntaxKind.Identifier: - return idText(name); - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ObjectBindingPattern: - return bindingNameText(cast(first(name.elements), isBindingElement).name); - default: - return Debug.assertNever(name); - } - } - - type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport; - function isImportedDeclaration(node: Node): node is ImportedDeclaration { - return node.kind === SyntaxKind.ImportClause || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.NamespaceImport; - } - function importClauseFromImported(decl: ImportedDeclaration): ImportClause { - return decl.kind === SyntaxKind.ImportClause ? decl : decl.kind === SyntaxKind.NamespaceImport ? decl.parent : decl.parent.parent; - } - - function checkBlock(node: Block) { - // Grammar checking for SyntaxKind.Block - if (node.kind === SyntaxKind.Block) { - checkGrammarStatementInAmbientContext(node); - } - if (isFunctionOrModuleBlock(node)) { - const saveFlowAnalysisDisabled = flowAnalysisDisabled; - forEach(node.statements, checkSourceElement); - flowAnalysisDisabled = saveFlowAnalysisDisabled; - } - else { - forEach(node.statements, checkSourceElement); - } - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) { - // no rest parameters \ declaration context \ overload - no codegen impact - if (languageVersion >= ScriptTarget.ES2015 || compilerOptions.noEmit || !hasRestParameter(node) || node.flags & NodeFlags.Ambient || nodeIsMissing((node).body)) { - return; - } - - forEach(node.parameters, p => { - if (p.name && !isBindingPattern(p.name) && p.name.escapedText === argumentsSymbol.escapedName) { - error(p, Diagnostics.Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters); - } - }); - } - - function needCollisionCheckForIdentifier(node: Node, identifier: Identifier | undefined, name: string): boolean { - if (!(identifier && identifier.escapedText === name)) { - return false; - } - - if (node.kind === SyntaxKind.PropertyDeclaration || - node.kind === SyntaxKind.PropertySignature || - node.kind === SyntaxKind.MethodDeclaration || - node.kind === SyntaxKind.MethodSignature || - node.kind === SyntaxKind.GetAccessor || - node.kind === SyntaxKind.SetAccessor) { - // it is ok to have member named '_super' or '_this' - member access is always qualified - return false; - } - - if (node.flags & NodeFlags.Ambient) { - // ambient context - no codegen impact - return false; - } - - const root = getRootDeclaration(node); - if (root.kind === SyntaxKind.Parameter && nodeIsMissing((root.parent).body)) { - // just an overload - no codegen impact - return false; - } - - return true; - } - - // this function will run after checking the source file so 'CaptureThis' is correct for all nodes - function checkIfThisIsCapturedInEnclosingScope(node: Node): void { - findAncestor(node, current => { - if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) { - const isDeclaration = node.kind !== SyntaxKind.Identifier; - if (isDeclaration) { - error(getNameOfDeclaration(node), Diagnostics.Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference); - } - else { - error(node, Diagnostics.Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference); - } - return true; - } - return false; - }); - } - - function checkIfNewTargetIsCapturedInEnclosingScope(node: Node): void { - findAncestor(node, current => { - if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureNewTarget) { - const isDeclaration = node.kind !== SyntaxKind.Identifier; - if (isDeclaration) { - error(getNameOfDeclaration(node), Diagnostics.Duplicate_identifier_newTarget_Compiler_uses_variable_declaration_newTarget_to_capture_new_target_meta_property_reference); - } - else { - error(node, Diagnostics.Expression_resolves_to_variable_declaration_newTarget_that_compiler_uses_to_capture_new_target_meta_property_reference); - } - return true; - } - return false; - }); - } - - function checkWeakMapCollision(node: Node) { - const enclosingBlockScope = getEnclosingBlockScopeContainer(node); - if (getNodeCheckFlags(enclosingBlockScope) & NodeCheckFlags.ContainsClassWithPrivateIdentifiers) { - error(node, Diagnostics.Compiler_reserves_name_0_when_emitting_private_identifier_downlevel, "WeakMap"); - } - } - - function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) { - // No need to check for require or exports for ES6 modules and later - if (moduleKind >= ModuleKind.ES2015 || compilerOptions.noEmit) { - return; - } - - if (!needCollisionCheckForIdentifier(node, name, "require") && !needCollisionCheckForIdentifier(node, name, "exports")) { - return; - } - - // Uninstantiated modules shouldnt do this check - if (isModuleDeclaration(node) && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { - return; - } - - // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent - const parent = getDeclarationContainer(node); - if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(parent)) { - // If the declaration happens to be in external module, report error that require and exports are reserved keywords - error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, - declarationNameToString(name), declarationNameToString(name)); - } - } - - function checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void { - if (languageVersion >= ScriptTarget.ES2017 || compilerOptions.noEmit || !needCollisionCheckForIdentifier(node, name, "Promise")) { - return; - } - - // Uninstantiated modules shouldnt do this check - if (isModuleDeclaration(node) && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { - return; - } - - // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent - const parent = getDeclarationContainer(node); - if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(parent) && parent.flags & NodeFlags.HasAsyncFunctions) { - // If the declaration happens to be in external module, report error that Promise is a reserved identifier. - error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions, - declarationNameToString(name), declarationNameToString(name)); - } - } - - function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) { - // - ScriptBody : StatementList - // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList - // also occurs in the VarDeclaredNames of StatementList. - - // - Block : { StatementList } - // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList - // also occurs in the VarDeclaredNames of StatementList. - - // Variable declarations are hoisted to the top of their function scope. They can shadow - // block scoped declarations, which bind tighter. this will not be flagged as duplicate definition - // by the binder as the declaration scope is different. - // A non-initialized declaration is a no-op as the block declaration will resolve before the var - // declaration. the problem is if the declaration has an initializer. this will act as a write to the - // block declared value. this is fine for let, but not const. - // Only consider declarations with initializers, uninitialized const declarations will not - // step on a let/const variable. - // Do not consider const and const declarations, as duplicate block-scoped declarations - // are handled by the binder. - // We are only looking for const declarations that step on let\const declarations from a - // different scope. e.g.: - // { - // const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration - // const x = 0; // symbol for this declaration will be 'symbol' - // } - - // skip block-scoped variables and parameters - if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) { - return; - } - - // skip variable declarations that don't have initializers - // NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern - // so we'll always treat binding elements as initialized - if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) { - return; - } - - const symbol = getSymbolOfNode(node); - if (symbol.flags & SymbolFlags.FunctionScopedVariable) { - if (!isIdentifier(node.name)) return Debug.fail(); - const localDeclarationSymbol = resolveName(node, node.name.escapedText, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false); - if (localDeclarationSymbol && - localDeclarationSymbol !== symbol && - localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) { - if (getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) { - const varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList)!; - const container = - varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent - ? varDeclList.parent.parent - : undefined; - - // names of block-scoped and function scoped variables can collide only - // if block scoped variable is defined in the function\module\source file scope (because of variable hoisting) - const namesShareScope = - container && - (container.kind === SyntaxKind.Block && isFunctionLike(container.parent) || - container.kind === SyntaxKind.ModuleBlock || - container.kind === SyntaxKind.ModuleDeclaration || - container.kind === SyntaxKind.SourceFile); - - // here we know that function scoped variable is shadowed by block scoped one - // if they are defined in the same scope - binder has already reported redeclaration error - // otherwise if variable has an initializer - show error that initialization will fail - // since LHS will be block scoped name instead of function scoped - if (!namesShareScope) { - const name = symbolToString(localDeclarationSymbol); - error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name); - } - } - } - } - } - - function convertAutoToAny(type: Type) { - return type === autoType ? anyType : type === autoArrayType ? anyArrayType : type; - } - - // Check variable, parameter, or property declaration - function checkVariableLikeDeclaration(node: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement) { - checkDecorators(node); - if (!isBindingElement(node)) { - checkSourceElement(node.type); - } - - // JSDoc `function(string, string): string` syntax results in parameters with no name - if (!node.name) { - return; - } - // For a computed property, just check the initializer and exit - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - if (node.initializer) { - checkExpressionCached(node.initializer); - } - } - - if (node.kind === SyntaxKind.BindingElement) { - if (node.parent.kind === SyntaxKind.ObjectBindingPattern && languageVersion < ScriptTarget.ESNext) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest); - } - // check computed properties inside property names of binding elements - if (node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.propertyName); - } - - // check private/protected variable access - const parent = node.parent.parent; - const parentType = getTypeForBindingElementParent(parent); - const name = node.propertyName || node.name; - if (parentType && !isBindingPattern(name)) { - const exprType = getLiteralTypeFromPropertyName(name); - if (isTypeUsableAsPropertyName(exprType)) { - const nameText = getPropertyNameFromType(exprType); - const property = getPropertyOfType(parentType, nameText); - if (property) { - markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference. - checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType, property); - } - } - } - } - - // For a binding pattern, check contained binding elements - if (isBindingPattern(node.name)) { - if (node.name.kind === SyntaxKind.ArrayBindingPattern && languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Read); - } - - forEach(node.name.elements, checkSourceElement); - } - // For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body - if (node.initializer && getRootDeclaration(node).kind === SyntaxKind.Parameter && nodeIsMissing((getContainingFunction(node) as FunctionLikeDeclaration).body)) { - error(node, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation); - return; - } - // For a binding pattern, validate the initializer and exit - if (isBindingPattern(node.name)) { - const needCheckInitializer = node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement; - const needCheckWidenedType = node.name.elements.length === 0; - if (needCheckInitializer || needCheckWidenedType) { - // Don't validate for-in initializer as it is already an error - const widenedType = getWidenedTypeForVariableLikeDeclaration(node); - if (needCheckInitializer) { - const initializerType = checkExpressionCached(node.initializer!); - if (strictNullChecks && needCheckWidenedType) { - checkNonNullNonVoidType(initializerType, node); - } - else { - checkTypeAssignableToAndOptionallyElaborate(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer); - } - } - // check the binding pattern with empty elements - if (needCheckWidenedType) { - if (isArrayBindingPattern(node.name)) { - checkIteratedTypeOrElementType(IterationUse.Destructuring, widenedType, undefinedType, node); - } - else if (strictNullChecks) { - checkNonNullNonVoidType(widenedType, node); - } - } - } - return; - } - const symbol = getSymbolOfNode(node); - const type = convertAutoToAny(getTypeOfSymbol(symbol)); - if (node === symbol.valueDeclaration) { - // Node is the primary declaration of the symbol, just validate the initializer - // Don't validate for-in initializer as it is already an error - const initializer = getEffectiveInitializer(node); - if (initializer) { - const isJSObjectLiteralInitializer = isInJSFile(node) && - isObjectLiteralExpression(initializer) && - (initializer.properties.length === 0 || isPrototypeAccess(node.name)) && - hasEntries(symbol.exports); - if (!isJSObjectLiteralInitializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) { - checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(initializer), type, node, initializer, /*headMessage*/ undefined); - } - } - if (symbol.declarations.length > 1) { - if (some(symbol.declarations, d => d !== node && isVariableLike(d) && !areDeclarationFlagsIdentical(d, node))) { - error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name)); - } - } - } - else { - // Node is a secondary declaration, check that type is identical to primary declaration and check that - // initializer is consistent with type associated with the node - const declarationType = convertAutoToAny(getWidenedTypeForVariableLikeDeclaration(node)); - - if (type !== errorType && declarationType !== errorType && - !isTypeIdenticalTo(type, declarationType) && - !(symbol.flags & SymbolFlags.Assignment)) { - errorNextVariableOrPropertyDeclarationMustHaveSameType(symbol.valueDeclaration, type, node, declarationType); - } - if (node.initializer) { - checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(node.initializer), declarationType, node, node.initializer, /*headMessage*/ undefined); - } - if (!areDeclarationFlagsIdentical(node, symbol.valueDeclaration)) { - error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name)); - } - } - if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature) { - // We know we don't have a binding pattern or computed name here - checkExportsOnMergedDeclarations(node); - if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { - checkVarDeclaredNamesNotShadowed(node); - } - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); - if (!compilerOptions.noEmit && languageVersion < ScriptTarget.ESNext && needCollisionCheckForIdentifier(node, node.name as Identifier, "WeakMap")) { - potentialWeakMapCollisions.push(node); - } - } - } - - function errorNextVariableOrPropertyDeclarationMustHaveSameType(firstDeclaration: Declaration | undefined, firstType: Type, nextDeclaration: Declaration, nextType: Type): void { - const nextDeclarationName = getNameOfDeclaration(nextDeclaration); - const message = nextDeclaration.kind === SyntaxKind.PropertyDeclaration || nextDeclaration.kind === SyntaxKind.PropertySignature - ? Diagnostics.Subsequent_property_declarations_must_have_the_same_type_Property_0_must_be_of_type_1_but_here_has_type_2 - : Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2; - const declName = declarationNameToString(nextDeclarationName); - const err = error( - nextDeclarationName, - message, - declName, - typeToString(firstType), - typeToString(nextType) - ); - if (firstDeclaration) { - addRelatedInfo(err, - createDiagnosticForNode(firstDeclaration, Diagnostics._0_was_also_declared_here, declName) - ); - } - } - - function areDeclarationFlagsIdentical(left: Declaration, right: Declaration) { - if ((left.kind === SyntaxKind.Parameter && right.kind === SyntaxKind.VariableDeclaration) || - (left.kind === SyntaxKind.VariableDeclaration && right.kind === SyntaxKind.Parameter)) { - // Differences in optionality between parameters and variables are allowed. - return true; - } - - if (hasQuestionToken(left) !== hasQuestionToken(right)) { - return false; - } - - const interestingFlags = ModifierFlags.Private | - ModifierFlags.Protected | - ModifierFlags.Async | - ModifierFlags.Abstract | - ModifierFlags.Readonly | - ModifierFlags.Static; - - return getSelectedModifierFlags(left, interestingFlags) === getSelectedModifierFlags(right, interestingFlags); - } - - function checkVariableDeclaration(node: VariableDeclaration) { - checkGrammarVariableDeclaration(node); - return checkVariableLikeDeclaration(node); - } - - function checkBindingElement(node: BindingElement) { - checkGrammarBindingElement(node); - return checkVariableLikeDeclaration(node); - } - - function checkVariableStatement(node: VariableStatement) { - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node) && !checkGrammarVariableDeclarationList(node.declarationList)) checkGrammarForDisallowedLetOrConstStatement(node); - forEach(node.declarationList.declarations, checkSourceElement); - } - - function checkExpressionStatement(node: ExpressionStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkExpression(node.expression); - } - - function checkIfStatement(node: IfStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - const type = checkTruthinessExpression(node.expression); - checkTestingKnownTruthyCallableType(node, type); - checkSourceElement(node.thenStatement); - - if (node.thenStatement.kind === SyntaxKind.EmptyStatement) { - error(node.thenStatement, Diagnostics.The_body_of_an_if_statement_cannot_be_the_empty_statement); - } - - checkSourceElement(node.elseStatement); - } - - function checkTestingKnownTruthyCallableType(ifStatement: IfStatement, type: Type) { - if (!strictNullChecks) { - return; - } - - const testedNode = isIdentifier(ifStatement.expression) - ? ifStatement.expression - : isPropertyAccessExpression(ifStatement.expression) - ? ifStatement.expression.name - : undefined; - - if (!testedNode) { - return; - } - - const possiblyFalsy = getFalsyFlags(type); - if (possiblyFalsy) { - return; - } - - // While it technically should be invalid for any known-truthy value - // to be tested, we de-scope to functions unrefenced in the block as a - // heuristic to identify the most common bugs. There are too many - // false positives for values sourced from type definitions without - // strictNullChecks otherwise. - const callSignatures = getSignaturesOfType(type, SignatureKind.Call); - if (callSignatures.length === 0) { - return; - } - - const testedFunctionSymbol = getSymbolAtLocation(testedNode); - if (!testedFunctionSymbol) { - return; - } - - const functionIsUsedInBody = forEachChild(ifStatement.thenStatement, function check(childNode): boolean | undefined { - if (isIdentifier(childNode)) { - const childSymbol = getSymbolAtLocation(childNode); - if (childSymbol && childSymbol.id === testedFunctionSymbol.id) { - return true; - } - } - - return forEachChild(childNode, check); - }); - - if (!functionIsUsedInBody) { - error(ifStatement.expression, Diagnostics.This_condition_will_always_return_true_since_the_function_is_always_defined_Did_you_mean_to_call_it_instead); - } - } - - function checkDoStatement(node: DoStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkSourceElement(node.statement); - checkTruthinessExpression(node.expression); - } - - function checkWhileStatement(node: WhileStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkTruthinessExpression(node.expression); - checkSourceElement(node.statement); - } - - function checkTruthinessExpression(node: Expression, checkMode?: CheckMode) { - const type = checkExpression(node, checkMode); - if (type.flags & TypeFlags.Void) { - error(node, Diagnostics.An_expression_of_type_void_cannot_be_tested_for_truthiness); - } - return type; - } - - function checkForStatement(node: ForStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) { - checkGrammarVariableDeclarationList(node.initializer); - } - } - - if (node.initializer) { - if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { - forEach((node.initializer).declarations, checkVariableDeclaration); - } - else { - checkExpression(node.initializer); - } - } - - if (node.condition) checkTruthinessExpression(node.condition); - if (node.incrementor) checkExpression(node.incrementor); - checkSourceElement(node.statement); - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkForOfStatement(node: ForOfStatement): void { - checkGrammarForInOrForOfStatement(node); - - if (node.awaitModifier) { - const functionFlags = getFunctionFlags(getContainingFunction(node)); - if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) { - // for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper - checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes); - } - } - else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) { - // for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled - checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes); - } - - // Check the LHS and RHS - // If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS - // via checkRightHandSideOfForOf. - // If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference. - // Then check that the RHS is assignable to it. - if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { - checkForInOrForOfVariableDeclaration(node); - } - else { - const varExpr = node.initializer; - const iteratedType = checkRightHandSideOfForOf(node.expression, node.awaitModifier); - - // There may be a destructuring assignment on the left side - if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { - // iteratedType may be undefined. In this case, we still want to check the structure of - // varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like - // to short circuit the type relation checking as much as possible, so we pass the unknownType. - checkDestructuringAssignment(varExpr, iteratedType || errorType); - } - else { - const leftType = checkExpression(varExpr); - checkReferenceExpression( - varExpr, - Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access, - Diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access); - - // iteratedType will be undefined if the rightType was missing properties/signatures - // required to get its iteratedType (like [Symbol.iterator] or next). This may be - // because we accessed properties from anyType, or it may have led to an error inside - // getElementTypeOfIterable. - if (iteratedType) { - checkTypeAssignableToAndOptionallyElaborate(iteratedType, leftType, varExpr, node.expression); - } - } - } - - checkSourceElement(node.statement); - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkForInStatement(node: ForInStatement) { - // Grammar checking - checkGrammarForInOrForOfStatement(node); - - const rightType = getNonNullableTypeIfNeeded(checkExpression(node.expression)); - // TypeScript 1.0 spec (April 2014): 5.4 - // In a 'for-in' statement of the form - // for (let VarDecl in Expr) Statement - // VarDecl must be a variable declaration without a type annotation that declares a variable of type Any, - // and Expr must be an expression of type Any, an object type, or a type parameter type. - if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { - const variable = (node.initializer).declarations[0]; - if (variable && isBindingPattern(variable.name)) { - error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern); - } - checkForInOrForOfVariableDeclaration(node); - } - else { - // In a 'for-in' statement of the form - // for (Var in Expr) Statement - // Var must be an expression classified as a reference of type Any or the String primitive type, - // and Expr must be an expression of type Any, an object type, or a type parameter type. - const varExpr = node.initializer; - const leftType = checkExpression(varExpr); - if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { - error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern); - } - else if (!isTypeAssignableTo(getIndexTypeOrString(rightType), leftType)) { - error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any); - } - else { - // run check only former check succeeded to avoid cascading errors - checkReferenceExpression( - varExpr, - Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access, - Diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access); - } - } - - // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved - // in this case error about missing name is already reported - do not report extra one - if (rightType === neverType || !isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive)) { - error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter_but_here_has_type_0, typeToString(rightType)); - } - - checkSourceElement(node.statement); - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkForInOrForOfVariableDeclaration(iterationStatement: ForInOrOfStatement): void { - const variableDeclarationList = iterationStatement.initializer; - // checkGrammarForInOrForOfStatement will check that there is exactly one declaration. - if (variableDeclarationList.declarations.length >= 1) { - const decl = variableDeclarationList.declarations[0]; - checkVariableDeclaration(decl); - } - } - - function checkRightHandSideOfForOf(rhsExpression: Expression, awaitModifier: AwaitKeywordToken | undefined): Type { - const expressionType = checkNonNullExpression(rhsExpression); - const use = awaitModifier ? IterationUse.ForAwaitOf : IterationUse.ForOf; - return checkIteratedTypeOrElementType(use, expressionType, undefinedType, rhsExpression); - } - - function checkIteratedTypeOrElementType(use: IterationUse, inputType: Type, sentType: Type, errorNode: Node | undefined): Type { - if (isTypeAny(inputType)) { - return inputType; - } - - return getIteratedTypeOrElementType(use, inputType, sentType, errorNode, /*checkAssignability*/ true) || anyType; - } - - /** - * When consuming an iterable type in a for..of, spread, or iterator destructuring assignment - * we want to get the iterated type of an iterable for ES2015 or later, or the iterated type - * of a iterable (if defined globally) or element type of an array like for ES2015 or earlier. - */ - function getIteratedTypeOrElementType(use: IterationUse, inputType: Type, sentType: Type, errorNode: Node | undefined, checkAssignability: boolean): Type | undefined { - const allowAsyncIterables = (use & IterationUse.AllowsAsyncIterablesFlag) !== 0; - if (inputType === neverType) { - reportTypeNotIterableError(errorNode!, inputType, allowAsyncIterables); // TODO: GH#18217 - return undefined; - } - - const uplevelIteration = languageVersion >= ScriptTarget.ES2015; - const downlevelIteration = !uplevelIteration && compilerOptions.downlevelIteration; - - // Get the iterated type of an `Iterable` or `IterableIterator` only in ES2015 - // or higher, when inside of an async generator or for-await-if, or when - // downlevelIteration is requested. - if (uplevelIteration || downlevelIteration || allowAsyncIterables) { - // We only report errors for an invalid iterable type in ES2015 or higher. - const iterationTypes = getIterationTypesOfIterable(inputType, use, uplevelIteration ? errorNode : undefined); - if (checkAssignability) { - if (iterationTypes) { - const diagnostic = - use & IterationUse.ForOfFlag ? Diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_for_of_will_always_send_0 : - use & IterationUse.SpreadFlag ? Diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_spread_will_always_send_0 : - use & IterationUse.DestructuringFlag ? Diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_destructuring_will_always_send_0 : - use & IterationUse.YieldStarFlag ? Diagnostics.Cannot_delegate_iteration_to_value_because_the_next_method_of_its_iterator_expects_type_1_but_the_containing_generator_will_always_send_0 : - undefined; - if (diagnostic) { - checkTypeAssignableTo(sentType, iterationTypes.nextType, errorNode, diagnostic); - } - } - } - if (iterationTypes || uplevelIteration) { - return iterationTypes && iterationTypes.yieldType; - } - } - - let arrayType = inputType; - let reportedError = false; - let hasStringConstituent = false; - - // If strings are permitted, remove any string-like constituents from the array type. - // This allows us to find other non-string element types from an array unioned with - // a string. - if (use & IterationUse.AllowsStringInputFlag) { - if (arrayType.flags & TypeFlags.Union) { - // After we remove all types that are StringLike, we will know if there was a string constituent - // based on whether the result of filter is a new array. - const arrayTypes = (inputType).types; - const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike)); - if (filteredTypes !== arrayTypes) { - arrayType = getUnionType(filteredTypes, UnionReduction.Subtype); - } - } - else if (arrayType.flags & TypeFlags.StringLike) { - arrayType = neverType; - } - - hasStringConstituent = arrayType !== inputType; - if (hasStringConstituent) { - if (languageVersion < ScriptTarget.ES5) { - if (errorNode) { - error(errorNode, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher); - reportedError = true; - } - } - - // Now that we've removed all the StringLike types, if no constituents remain, then the entire - // arrayOrStringType was a string. - if (arrayType.flags & TypeFlags.Never) { - return stringType; - } - } - } - - if (!isArrayLikeType(arrayType)) { - if (errorNode && !reportedError) { - // Which error we report depends on whether we allow strings or if there was a - // string constituent. For example, if the input type is number | string, we - // want to say that number is not an array type. But if the input was just - // number and string input is allowed, we want to say that number is not an - // array type or a string type. - const yieldType = getIterationTypeOfIterable(use, IterationTypeKind.Yield, inputType, /*errorNode*/ undefined); - const [defaultDiagnostic, maybeMissingAwait]: [DiagnosticMessage, boolean] = !(use & IterationUse.AllowsStringInputFlag) || hasStringConstituent - ? downlevelIteration - ? [Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true] - : yieldType - ? [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators, false] - : [Diagnostics.Type_0_is_not_an_array_type, true] - : downlevelIteration - ? [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true] - : yieldType - ? [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators, false] - : [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type, true]; - errorAndMaybeSuggestAwait( - errorNode, - maybeMissingAwait && !!getAwaitedTypeOfPromise(arrayType), - defaultDiagnostic, - typeToString(arrayType)); - } - return hasStringConstituent ? stringType : undefined; - } - - const arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number); - if (hasStringConstituent && arrayElementType) { - // This is just an optimization for the case where arrayOrStringType is string | string[] - if (arrayElementType.flags & TypeFlags.StringLike) { - return stringType; - } - - return getUnionType([arrayElementType, stringType], UnionReduction.Subtype); - } - - return arrayElementType; - } - - /** - * Gets the requested "iteration type" from an `Iterable`-like or `AsyncIterable`-like type. - */ - function getIterationTypeOfIterable(use: IterationUse, typeKind: IterationTypeKind, inputType: Type, errorNode: Node | undefined): Type | undefined { - if (isTypeAny(inputType)) { - return undefined; - } - - const iterationTypes = getIterationTypesOfIterable(inputType, use, errorNode); - return iterationTypes && iterationTypes[getIterationTypesKeyFromIterationTypeKind(typeKind)]; - } - - function createIterationTypes(yieldType: Type = neverType, returnType: Type = neverType, nextType: Type = unknownType): IterationTypes { - // `yieldType` and `returnType` are defaulted to `neverType` they each will be combined - // via `getUnionType` when merging iteration types. `nextType` is defined as `unknownType` - // as it is combined via `getIntersectionType` when merging iteration types. - - // Use the cache only for intrinsic types to keep it small as they are likely to be - // more frequently created (i.e. `Iterator`). Iteration types - // are also cached on the type they are requested for, so we shouldn't need to maintain - // the cache for less-frequently used types. - if (yieldType.flags & TypeFlags.Intrinsic && - returnType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown | TypeFlags.Void | TypeFlags.Undefined) && - nextType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown | TypeFlags.Void | TypeFlags.Undefined)) { - const id = getTypeListId([yieldType, returnType, nextType]); - let iterationTypes = iterationTypesCache.get(id); - if (!iterationTypes) { - iterationTypes = { yieldType, returnType, nextType }; - iterationTypesCache.set(id, iterationTypes); - } - return iterationTypes; - } - return { yieldType, returnType, nextType }; - } - - /** - * Combines multiple `IterationTypes` records. - * - * If `array` is empty or all elements are missing or are references to `noIterationTypes`, - * then `noIterationTypes` is returned. Otherwise, an `IterationTypes` record is returned - * for the combined iteration types. - */ - function combineIterationTypes(array: (IterationTypes | undefined)[]) { - let yieldTypes: Type[] | undefined; - let returnTypes: Type[] | undefined; - let nextTypes: Type[] | undefined; - for (const iterationTypes of array) { - if (iterationTypes === undefined || iterationTypes === noIterationTypes) { - continue; - } - if (iterationTypes === anyIterationTypes) { - return anyIterationTypes; - } - yieldTypes = append(yieldTypes, iterationTypes.yieldType); - returnTypes = append(returnTypes, iterationTypes.returnType); - nextTypes = append(nextTypes, iterationTypes.nextType); - } - if (yieldTypes || returnTypes || nextTypes) { - return createIterationTypes( - yieldTypes && getUnionType(yieldTypes), - returnTypes && getUnionType(returnTypes), - nextTypes && getIntersectionType(nextTypes)); - } - return noIterationTypes; - } - - /** - * Gets the *yield*, *return*, and *next* types from an `Iterable`-like or `AsyncIterable`-like type. - * - * At every level that involves analyzing return types of signatures, we union the return types of all the signatures. - * - * Another thing to note is that at any step of this process, we could run into a dead end, - * meaning either the property is missing, or we run into the anyType. If either of these things - * happens, we return `undefined` to signal that we could not find the iteration type. If a property - * is missing, and the previous step did not result in `any`, then we also give an error if the - * caller requested it. Then the caller can decide what to do in the case where there is no iterated - * type. - * - * For a **for-of** statement, `yield*` (in a normal generator), spread, array - * destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()` - * method. - * - * For an async generator we will only ever look at the `[Symbol.asyncIterator]()` method. - * - * For a **for-await-of** statement or a `yield*` in an async generator we will look for - * the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method. - */ - function getIterationTypesOfIterable(type: Type, use: IterationUse, errorNode: Node | undefined) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - if (!(type.flags & TypeFlags.Union)) { - const iterationTypes = getIterationTypesOfIterableWorker(type, use, errorNode); - if (iterationTypes === noIterationTypes) { - if (errorNode) { - reportTypeNotIterableError(errorNode, type, !!(use & IterationUse.AllowsAsyncIterablesFlag)); - } - return undefined; - } - return iterationTypes; - } - - const cacheKey = use & IterationUse.AllowsAsyncIterablesFlag ? "iterationTypesOfAsyncIterable" : "iterationTypesOfIterable"; - const cachedTypes = (type as IterableOrIteratorType)[cacheKey]; - if (cachedTypes) return cachedTypes === noIterationTypes ? undefined : cachedTypes; - - let allIterationTypes: IterationTypes[] | undefined; - for (const constituent of (type as UnionType).types) { - const iterationTypes = getIterationTypesOfIterableWorker(constituent, use, errorNode); - if (iterationTypes === noIterationTypes) { - if (errorNode) { - reportTypeNotIterableError(errorNode, type, !!(use & IterationUse.AllowsAsyncIterablesFlag)); - errorNode = undefined; - } - } - else { - allIterationTypes = append(allIterationTypes, iterationTypes); - } - } - - const iterationTypes = allIterationTypes ? combineIterationTypes(allIterationTypes) : noIterationTypes; - (type as IterableOrIteratorType)[cacheKey] = iterationTypes; - return iterationTypes === noIterationTypes ? undefined : iterationTypes; - } - - function getAsyncFromSyncIterationTypes(iterationTypes: IterationTypes, errorNode: Node | undefined) { - if (iterationTypes === noIterationTypes) return noIterationTypes; - if (iterationTypes === anyIterationTypes) return anyIterationTypes; - const { yieldType, returnType, nextType } = iterationTypes; - return createIterationTypes( - getAwaitedType(yieldType, errorNode) || anyType, - getAwaitedType(returnType, errorNode) || anyType, - nextType); - } - - /** - * Gets the *yield*, *return*, and *next* types from a non-union type. - * - * If we are unable to find the *yield*, *return*, and *next* types, `noIterationTypes` is - * returned to indicate to the caller that it should report an error. Otherwise, an - * `IterationTypes` record is returned. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableWorker(type: Type, use: IterationUse, errorNode: Node | undefined) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - if (use & IterationUse.AllowsAsyncIterablesFlag) { - const iterationTypes = - getIterationTypesOfIterableCached(type, asyncIterationTypesResolver) || - getIterationTypesOfIterableFast(type, asyncIterationTypesResolver); - if (iterationTypes) { - return iterationTypes; - } - } - - if (use & IterationUse.AllowsSyncIterablesFlag) { - const iterationTypes = - getIterationTypesOfIterableCached(type, syncIterationTypesResolver) || - getIterationTypesOfIterableFast(type, syncIterationTypesResolver); - if (iterationTypes) { - if (use & IterationUse.AllowsAsyncIterablesFlag) { - // for a sync iterable in an async context, only use the cached types if they are valid. - if (iterationTypes !== noIterationTypes) { - return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = getAsyncFromSyncIterationTypes(iterationTypes, errorNode); - } - } - else { - return iterationTypes; - } - } - } - - if (use & IterationUse.AllowsAsyncIterablesFlag) { - const iterationTypes = getIterationTypesOfIterableSlow(type, asyncIterationTypesResolver, errorNode); - if (iterationTypes !== noIterationTypes) { - return iterationTypes; - } - } - - if (use & IterationUse.AllowsSyncIterablesFlag) { - const iterationTypes = getIterationTypesOfIterableSlow(type, syncIterationTypesResolver, errorNode); - if (iterationTypes !== noIterationTypes) { - if (use & IterationUse.AllowsAsyncIterablesFlag) { - return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = iterationTypes - ? getAsyncFromSyncIterationTypes(iterationTypes, errorNode) - : noIterationTypes; - } - else { - return iterationTypes; - } - } - } - - return noIterationTypes; - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or - * `AsyncIterable`-like type from the cache. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableCached(type: Type, resolver: IterationTypesResolver) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey]; - } - - function getIterationTypesOfGlobalIterableType(globalType: Type, resolver: IterationTypesResolver) { - const globalIterationTypes = - getIterationTypesOfIterableCached(globalType, resolver) || - getIterationTypesOfIterableSlow(globalType, resolver, /*errorNode*/ undefined); - return globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes; - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like - * type from from common heuristics. - * - * If we previously analyzed this type and found no iteration types, `noIterationTypes` is - * returned. If we found iteration types, an `IterationTypes` record is returned. - * Otherwise, we return `undefined` to indicate to the caller it should perform a more - * exhaustive analysis. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableFast(type: Type, resolver: IterationTypesResolver) { - // As an optimization, if the type is an instantiation of one of the following global types, then - // just grab its related type argument: - // - `Iterable` or `AsyncIterable` - // - `IterableIterator` or `AsyncIterableIterator` - let globalType: Type; - if (isReferenceToType(type, globalType = resolver.getGlobalIterableType(/*reportErrors*/ false)) || - isReferenceToType(type, globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false))) { - const [yieldType] = getTypeArguments(type as GenericType); - // The "return" and "next" types of `Iterable` and `IterableIterator` are defined by the - // iteration types of their `[Symbol.iterator]()` method. The same is true for their async cousins. - // While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use - // different definitions. - const { returnType, nextType } = getIterationTypesOfGlobalIterableType(globalType, resolver); - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType); - } - - // As an optimization, if the type is an instantiation of the following global type, then - // just grab its related type arguments: - // - `Generator` or `AsyncGenerator` - if (isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) { - const [yieldType, returnType, nextType] = getTypeArguments(type as GenericType); - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType); - } - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like - * type from its members. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, `noIterationTypes` is returned. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined) { - const method = getPropertyOfType(type, getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName)); - const methodType = method && !(method.flags & SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined; - if (isTypeAny(methodType)) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = anyIterationTypes; - } - - const signatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined; - if (!some(signatures)) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = noIterationTypes; - } - - const iteratorType = getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype); - const iterationTypes = getIterationTypesOfIterator(iteratorType, resolver, errorNode) || noIterationTypes; - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = iterationTypes; - } - - function reportTypeNotIterableError(errorNode: Node, type: Type, allowAsyncIterables: boolean): void { - const message = allowAsyncIterables - ? Diagnostics.Type_0_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator - : Diagnostics.Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator; - errorAndMaybeSuggestAwait(errorNode, !!getAwaitedTypeOfPromise(type), message, typeToString(type)); - } - - /** - * Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, `undefined` is returned. - */ - function getIterationTypesOfIterator(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - const iterationTypes = - getIterationTypesOfIteratorCached(type, resolver) || - getIterationTypesOfIteratorFast(type, resolver) || - getIterationTypesOfIteratorSlow(type, resolver, errorNode); - return iterationTypes === noIterationTypes ? undefined : iterationTypes; - } - - /** - * Gets the iteration types of an `Iterator`-like or `AsyncIterator`-like type from the - * cache. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterator` instead. - */ - function getIterationTypesOfIteratorCached(type: Type, resolver: IterationTypesResolver) { - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey]; - } - - /** - * Gets the iteration types of an `Iterator`-like or `AsyncIterator`-like type from the - * cache or from common heuristics. - * - * If we previously analyzed this type and found no iteration types, `noIterationTypes` is - * returned. If we found iteration types, an `IterationTypes` record is returned. - * Otherwise, we return `undefined` to indicate to the caller it should perform a more - * exhaustive analysis. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterator` instead. - */ - function getIterationTypesOfIteratorFast(type: Type, resolver: IterationTypesResolver) { - // As an optimization, if the type is an instantiation of one of the following global types, - // then just grab its related type argument: - // - `IterableIterator` or `AsyncIterableIterator` - // - `Iterator` or `AsyncIterator` - // - `Generator` or `AsyncGenerator` - const globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false); - if (isReferenceToType(type, globalType)) { - const [yieldType] = getTypeArguments(type as GenericType); - // The "return" and "next" types of `IterableIterator` and `AsyncIterableIterator` are defined by the - // iteration types of their `next`, `return`, and `throw` methods. While we define these as `any` - // and `undefined` in our libs by default, a custom lib *could* use different definitions. - const globalIterationTypes = - getIterationTypesOfIteratorCached(globalType, resolver) || - getIterationTypesOfIteratorSlow(globalType, resolver, /*errorNode*/ undefined); - const { returnType, nextType } = globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes; - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = createIterationTypes(yieldType, returnType, nextType); - } - if (isReferenceToType(type, resolver.getGlobalIteratorType(/*reportErrors*/ false)) || - isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) { - const [yieldType, returnType, nextType] = getTypeArguments(type as GenericType); - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = createIterationTypes(yieldType, returnType, nextType); - } - } - - function isIteratorResult(type: Type, kind: IterationTypeKind.Yield | IterationTypeKind.Return) { - // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface: - // > [done] is the result status of an iterator `next` method call. If the end of the iterator was reached `done` is `true`. - // > If the end was not reached `done` is `false` and a value is available. - // > If a `done` property (either own or inherited) does not exist, it is consider to have the value `false`. - const doneType = getTypeOfPropertyOfType(type, "done" as __String) || falseType; - return isTypeAssignableTo(kind === IterationTypeKind.Yield ? falseType : trueType, doneType); - } - - function isYieldIteratorResult(type: Type) { - return isIteratorResult(type, IterationTypeKind.Yield); - } - - function isReturnIteratorResult(type: Type) { - return isIteratorResult(type, IterationTypeKind.Return); - } - - /** - * Gets the *yield* and *return* types of an `IteratorResult`-like type. - * - * If we are unable to determine a *yield* or a *return* type, `noIterationTypes` is - * returned to indicate to the caller that it should handle the error. Otherwise, an - * `IterationTypes` record is returned. - */ - function getIterationTypesOfIteratorResult(type: Type) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - const cachedTypes = (type as IterableOrIteratorType).iterationTypesOfIteratorResult; - if (cachedTypes) { - return cachedTypes; - } - - // As an optimization, if the type is an instantiation of one of the global `IteratorYieldResult` - // or `IteratorReturnResult` types, then just grab its type argument. - if (isReferenceToType(type, getGlobalIteratorYieldResultType(/*reportErrors*/ false))) { - const yieldType = getTypeArguments(type as GenericType)[0]; - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(yieldType, /*returnType*/ undefined, /*nextType*/ undefined); - } - if (isReferenceToType(type, getGlobalIteratorReturnResultType(/*reportErrors*/ false))) { - const returnType = getTypeArguments(type as GenericType)[0]; - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(/*yieldType*/ undefined, returnType, /*nextType*/ undefined); - } - - // Choose any constituents that can produce the requested iteration type. - const yieldIteratorResult = filterType(type, isYieldIteratorResult); - const yieldType = yieldIteratorResult !== neverType ? getTypeOfPropertyOfType(yieldIteratorResult, "value" as __String) : undefined; - - const returnIteratorResult = filterType(type, isReturnIteratorResult); - const returnType = returnIteratorResult !== neverType ? getTypeOfPropertyOfType(returnIteratorResult, "value" as __String) : undefined; - - if (!yieldType && !returnType) { - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = noIterationTypes; - } - - // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface - // > ... If the iterator does not have a return value, `value` is `undefined`. In that case, the - // > `value` property may be absent from the conforming object if it does not inherit an explicit - // > `value` property. - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(yieldType, returnType || voidType, /*nextType*/ undefined); - } - - /** - * Gets the *yield*, *return*, and *next* types of a the `next()`, `return()`, or - * `throw()` method of an `Iterator`-like or `AsyncIterator`-like type. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, we return `undefined`. - */ - function getIterationTypesOfMethod(type: Type, resolver: IterationTypesResolver, methodName: "next" | "return" | "throw", errorNode: Node | undefined): IterationTypes | undefined { - const method = getPropertyOfType(type, methodName as __String); - - // Ignore 'return' or 'throw' if they are missing. - if (!method && methodName !== "next") { - return undefined; - } - - const methodType = method && !(methodName === "next" && (method.flags & SymbolFlags.Optional)) - ? methodName === "next" ? getTypeOfSymbol(method) : getTypeWithFacts(getTypeOfSymbol(method), TypeFacts.NEUndefinedOrNull) - : undefined; - - if (isTypeAny(methodType)) { - // `return()` and `throw()` don't provide a *next* type. - return methodName === "next" ? anyIterationTypes : anyIterationTypesExceptNext; - } - - // Both async and non-async iterators *must* have a `next` method. - const methodSignatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : emptyArray; - if (methodSignatures.length === 0) { - if (errorNode) { - const diagnostic = methodName === "next" - ? resolver.mustHaveANextMethodDiagnostic - : resolver.mustBeAMethodDiagnostic; - error(errorNode, diagnostic, methodName); - } - return methodName === "next" ? anyIterationTypes : undefined; - } - - // Extract the first parameter and return type of each signature. - let methodParameterTypes: Type[] | undefined; - let methodReturnTypes: Type[] | undefined; - for (const signature of methodSignatures) { - if (methodName !== "throw" && some(signature.parameters)) { - methodParameterTypes = append(methodParameterTypes, getTypeAtPosition(signature, 0)); - } - methodReturnTypes = append(methodReturnTypes, getReturnTypeOfSignature(signature)); - } - - // Resolve the *next* or *return* type from the first parameter of a `next()` or - // `return()` method, respectively. - let returnTypes: Type[] | undefined; - let nextType: Type | undefined; - if (methodName !== "throw") { - const methodParameterType = methodParameterTypes ? getUnionType(methodParameterTypes) : unknownType; - if (methodName === "next") { - // The value of `next(value)` is *not* awaited by async generators - nextType = methodParameterType; - } - else if (methodName === "return") { - // The value of `return(value)` *is* awaited by async generators - const resolvedMethodParameterType = resolver.resolveIterationType(methodParameterType, errorNode) || anyType; - returnTypes = append(returnTypes, resolvedMethodParameterType); - } - } - - // Resolve the *yield* and *return* types from the return type of the method (i.e. `IteratorResult`) - let yieldType: Type; - const methodReturnType = methodReturnTypes ? getUnionType(methodReturnTypes, UnionReduction.Subtype) : neverType; - const resolvedMethodReturnType = resolver.resolveIterationType(methodReturnType, errorNode) || anyType; - const iterationTypes = getIterationTypesOfIteratorResult(resolvedMethodReturnType); - if (iterationTypes === noIterationTypes) { - if (errorNode) { - error(errorNode, resolver.mustHaveAValueDiagnostic, methodName); - } - yieldType = anyType; - returnTypes = append(returnTypes, anyType); - } - else { - yieldType = iterationTypes.yieldType; - returnTypes = append(returnTypes, iterationTypes.returnType); - } - - return createIterationTypes(yieldType, getUnionType(returnTypes), nextType); - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterator`-like or `AsyncIterator`-like - * type from its members. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, `noIterationTypes` is returned. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterator` instead. - */ - function getIterationTypesOfIteratorSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined) { - const iterationTypes = combineIterationTypes([ - getIterationTypesOfMethod(type, resolver, "next", errorNode), - getIterationTypesOfMethod(type, resolver, "return", errorNode), - getIterationTypesOfMethod(type, resolver, "throw", errorNode), - ]); - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = iterationTypes; - } - - /** - * Gets the requested "iteration type" from a type that is either `Iterable`-like, `Iterator`-like, - * `IterableIterator`-like, or `Generator`-like (for a non-async generator); or `AsyncIterable`-like, - * `AsyncIterator`-like, `AsyncIterableIterator`-like, or `AsyncGenerator`-like (for an async generator). - */ - function getIterationTypeOfGeneratorFunctionReturnType(kind: IterationTypeKind, returnType: Type, isAsyncGenerator: boolean): Type | undefined { - if (isTypeAny(returnType)) { - return undefined; - } - - const iterationTypes = getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsyncGenerator); - return iterationTypes && iterationTypes[getIterationTypesKeyFromIterationTypeKind(kind)]; - } - - function getIterationTypesOfGeneratorFunctionReturnType(type: Type, isAsyncGenerator: boolean) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - const use = isAsyncGenerator ? IterationUse.AsyncGeneratorReturnType : IterationUse.GeneratorReturnType; - const resolver = isAsyncGenerator ? asyncIterationTypesResolver : syncIterationTypesResolver; - return getIterationTypesOfIterable(type, use, /*errorNode*/ undefined) || - getIterationTypesOfIterator(type, resolver, /*errorNode*/ undefined); - } - - function checkBreakOrContinueStatement(node: BreakOrContinueStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) checkGrammarBreakOrContinueStatement(node); - - // TODO: Check that target label is valid - } - - function unwrapReturnType(returnType: Type, functionFlags: FunctionFlags) { - const isGenerator = !!(functionFlags & FunctionFlags.Generator); - const isAsync = !!(functionFlags & FunctionFlags.Async); - return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) || errorType : - isAsync ? getPromisedTypeOfPromise(returnType) || errorType : - returnType; - } - - function isUnwrappedReturnTypeVoidOrAny(func: SignatureDeclaration, returnType: Type): boolean { - const unwrappedReturnType = unwrapReturnType(returnType, getFunctionFlags(func)); - return !!unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.AnyOrUnknown); - } - - function checkReturnStatement(node: ReturnStatement) { - // Grammar checking - if (checkGrammarStatementInAmbientContext(node)) { - return; - } - - const func = getContainingFunction(node); - if (!func) { - grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body); - return; - } - - const signature = getSignatureFromDeclaration(func); - const returnType = getReturnTypeOfSignature(signature); - const functionFlags = getFunctionFlags(func); - if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) { - const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType; - if (func.kind === SyntaxKind.SetAccessor) { - if (node.expression) { - error(node, Diagnostics.Setters_cannot_return_a_value); - } - } - else if (func.kind === SyntaxKind.Constructor) { - if (node.expression && !checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression)) { - error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); - } - } - else if (getReturnTypeFromAnnotation(func)) { - const unwrappedReturnType = unwrapReturnType(returnType, functionFlags); - const unwrappedExprType = functionFlags & FunctionFlags.Async - ? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) - : exprType; - if (unwrappedReturnType) { - // If the function has a return type, but promisedType is - // undefined, an error will be reported in checkAsyncFunctionReturnType - // so we don't need to report one here. - checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, node, node.expression); - } - } - } - else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) { - // The function has a return type, but the return statement doesn't have an expression. - error(node, Diagnostics.Not_all_code_paths_return_a_value); - } - } - - function checkWithStatement(node: WithStatement) { - // Grammar checking for withStatement - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.flags & NodeFlags.AwaitContext) { - grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_an_async_function_block); - } - } - - checkExpression(node.expression); - - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const start = getSpanOfTokenAtPosition(sourceFile, node.pos).start; - const end = node.statement.pos; - grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any); - } - } - - function checkSwitchStatement(node: SwitchStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - let firstDefaultClause: CaseOrDefaultClause; - let hasDuplicateDefaultClause = false; - - const expressionType = checkExpression(node.expression); - const expressionIsLiteral = isLiteralType(expressionType); - forEach(node.caseBlock.clauses, clause => { - // Grammar check for duplicate default clauses, skip if we already report duplicate default clause - if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) { - if (firstDefaultClause === undefined) { - firstDefaultClause = clause; - } - else { - grammarErrorOnNode(clause, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement); - hasDuplicateDefaultClause = true; - } - } - - if (produceDiagnostics && clause.kind === SyntaxKind.CaseClause) { - // TypeScript 1.0 spec (April 2014): 5.9 - // In a 'switch' statement, each 'case' expression must be of a type that is comparable - // to or from the type of the 'switch' expression. - let caseType = checkExpression(clause.expression); - const caseIsLiteral = isLiteralType(caseType); - let comparedExpressionType = expressionType; - if (!caseIsLiteral || !expressionIsLiteral) { - caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType; - comparedExpressionType = getBaseTypeOfLiteralType(expressionType); - } - if (!isTypeEqualityComparableTo(comparedExpressionType, caseType)) { - // expressionType is not comparable to caseType, try the reversed check and report errors if it fails - checkTypeComparableTo(caseType, comparedExpressionType, clause.expression, /*headMessage*/ undefined); - } - } - forEach(clause.statements, checkSourceElement); - if (compilerOptions.noFallthroughCasesInSwitch && clause.fallthroughFlowNode && isReachableFlowNode(clause.fallthroughFlowNode)) { - error(clause, Diagnostics.Fallthrough_case_in_switch); - } - }); - if (node.caseBlock.locals) { - registerForUnusedIdentifiersCheck(node.caseBlock); - } - } - - function checkLabeledStatement(node: LabeledStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - findAncestor(node.parent, current => { - if (isFunctionLike(current)) { - return "quit"; - } - if (current.kind === SyntaxKind.LabeledStatement && (current).label.escapedText === node.label.escapedText) { - grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNode(node.label)); - return true; - } - return false; - }); - } - - // ensure that label is unique - checkSourceElement(node.statement); - } - - function checkThrowStatement(node: ThrowStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.expression === undefined) { - grammarErrorAfterFirstToken(node, Diagnostics.Line_break_not_permitted_here); - } - } - - if (node.expression) { - checkExpression(node.expression); - } - } - - function checkTryStatement(node: TryStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkBlock(node.tryBlock); - const catchClause = node.catchClause; - if (catchClause) { - // Grammar checking - if (catchClause.variableDeclaration) { - if (catchClause.variableDeclaration.type) { - grammarErrorOnFirstToken(catchClause.variableDeclaration.type, Diagnostics.Catch_clause_variable_cannot_have_a_type_annotation); - } - else if (catchClause.variableDeclaration.initializer) { - grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer); - } - else { - const blockLocals = catchClause.block.locals; - if (blockLocals) { - forEachKey(catchClause.locals!, caughtName => { - const blockLocal = blockLocals.get(caughtName); - if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) { - grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName); - } - }); - } - } - } - - checkBlock(catchClause.block); - } - - if (node.finallyBlock) { - checkBlock(node.finallyBlock); - } - } - - function checkIndexConstraints(type: Type) { - const declaredNumberIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.Number); - const declaredStringIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.String); - - const stringIndexType = getIndexTypeOfType(type, IndexKind.String); - const numberIndexType = getIndexTypeOfType(type, IndexKind.Number); - - if (stringIndexType || numberIndexType) { - forEach(getPropertiesOfObjectType(type), prop => { - const propType = getTypeOfSymbol(prop); - checkIndexConstraintForProperty(prop, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String); - checkIndexConstraintForProperty(prop, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); - }); - - const classDeclaration = type.symbol.valueDeclaration; - if (getObjectFlags(type) & ObjectFlags.Class && isClassLike(classDeclaration)) { - for (const member of classDeclaration.members) { - // Only process instance properties with computed names here. - // Static properties cannot be in conflict with indexers, - // and properties with literal names were already checked. - if (!hasModifier(member, ModifierFlags.Static) && hasNonBindableDynamicName(member)) { - const symbol = getSymbolOfNode(member); - const propType = getTypeOfSymbol(symbol); - checkIndexConstraintForProperty(symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String); - checkIndexConstraintForProperty(symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); - } - } - } - } - - let errorNode: Node | undefined; - if (stringIndexType && numberIndexType) { - errorNode = declaredNumberIndexer || declaredStringIndexer; - // condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer - if (!errorNode && (getObjectFlags(type) & ObjectFlags.Interface)) { - const someBaseTypeHasBothIndexers = forEach(getBaseTypes(type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number)); - errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0]; - } - } - - if (errorNode && !isTypeAssignableTo(numberIndexType!, stringIndexType!)) { // TODO: GH#18217 - error(errorNode, Diagnostics.Numeric_index_type_0_is_not_assignable_to_string_index_type_1, - typeToString(numberIndexType!), typeToString(stringIndexType!)); - } - - function checkIndexConstraintForProperty( - prop: Symbol, - propertyType: Type, - containingType: Type, - indexDeclaration: Declaration | undefined, - indexType: Type | undefined, - indexKind: IndexKind): void { - - // ESSymbol properties apply to neither string nor numeric indexers. - if (!indexType || isKnownSymbol(prop)) { - return; - } - - const propDeclaration = prop.valueDeclaration; - const name = propDeclaration && getNameOfDeclaration(propDeclaration); - - // index is numeric and property name is not valid numeric literal - if (indexKind === IndexKind.Number && !(name ? isNumericName(name) : isNumericLiteralName(prop.escapedName))) { - return; - } - - // perform property check if property or indexer is declared in 'type' - // this allows us to rule out cases when both property and indexer are inherited from the base class - let errorNode: Node | undefined; - if (propDeclaration && name && - (propDeclaration.kind === SyntaxKind.BinaryExpression || - name.kind === SyntaxKind.ComputedPropertyName || - prop.parent === containingType.symbol)) { - errorNode = propDeclaration; - } - else if (indexDeclaration) { - errorNode = indexDeclaration; - } - else if (getObjectFlags(containingType) & ObjectFlags.Interface) { - // for interfaces property and indexer might be inherited from different bases - // check if any base class already has both property and indexer. - // check should be performed only if 'type' is the first type that brings property\indexer together - const someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(containingType), base => getPropertyOfObjectType(base, prop.escapedName) && getIndexTypeOfType(base, indexKind)); - errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0]; - } - - if (errorNode && !isTypeAssignableTo(propertyType, indexType)) { - const errorMessage = - indexKind === IndexKind.String - ? Diagnostics.Property_0_of_type_1_is_not_assignable_to_string_index_type_2 - : Diagnostics.Property_0_of_type_1_is_not_assignable_to_numeric_index_type_2; - error(errorNode, errorMessage, symbolToString(prop), typeToString(propertyType), typeToString(indexType)); - } - } - } - - function checkTypeNameIsReserved(name: Identifier, message: DiagnosticMessage): void { - // TS 1.0 spec (April 2014): 3.6.1 - // The predefined type keywords are reserved and cannot be used as names of user defined types. - switch (name.escapedText) { - case "any": - case "unknown": - case "number": - case "bigint": - case "boolean": - case "string": - case "symbol": - case "void": - case "object": - error(name, message, name.escapedText as string); - } - } - - /** - * The name cannot be used as 'Object' of user defined types with special target. - */ - function checkClassNameCollisionWithObject(name: Identifier): void { - if (languageVersion === ScriptTarget.ES5 && name.escapedText === "Object" - && moduleKind < ModuleKind.ES2015) { - error(name, Diagnostics.Class_name_cannot_be_Object_when_targeting_ES5_with_module_0, ModuleKind[moduleKind]); // https://github.com/Microsoft/TypeScript/issues/17494 - } - } - - /** - * Check each type parameter and check that type parameters have no duplicate type parameter declarations - */ - function checkTypeParameters(typeParameterDeclarations: readonly TypeParameterDeclaration[] | undefined) { - if (typeParameterDeclarations) { - let seenDefault = false; - for (let i = 0; i < typeParameterDeclarations.length; i++) { - const node = typeParameterDeclarations[i]; - checkTypeParameter(node); - - if (produceDiagnostics) { - if (node.default) { - seenDefault = true; - checkTypeParametersNotReferenced(node.default, typeParameterDeclarations, i); - } - else if (seenDefault) { - error(node, Diagnostics.Required_type_parameters_may_not_follow_optional_type_parameters); - } - for (let j = 0; j < i; j++) { - if (typeParameterDeclarations[j].symbol === node.symbol) { - error(node.name, Diagnostics.Duplicate_identifier_0, declarationNameToString(node.name)); - } - } - } - } - } - } - - /** Check that type parameter defaults only reference previously declared type parameters */ - function checkTypeParametersNotReferenced(root: TypeNode, typeParameters: readonly TypeParameterDeclaration[], index: number) { - visit(root); - function visit(node: Node) { - if (node.kind === SyntaxKind.TypeReference) { - const type = getTypeFromTypeReference(node); - if (type.flags & TypeFlags.TypeParameter) { - for (let i = index; i < typeParameters.length; i++) { - if (type.symbol === getSymbolOfNode(typeParameters[i])) { - error(node, Diagnostics.Type_parameter_defaults_can_only_reference_previously_declared_type_parameters); - } - } - } - } - forEachChild(node, visit); - } - } - - /** Check that type parameter lists are identical across multiple declarations */ - function checkTypeParameterListsIdentical(symbol: Symbol) { - if (symbol.declarations.length === 1) { - return; - } - - const links = getSymbolLinks(symbol); - if (!links.typeParametersChecked) { - links.typeParametersChecked = true; - const declarations = getClassOrInterfaceDeclarationsOfSymbol(symbol); - if (declarations.length <= 1) { - return; - } - - const type = getDeclaredTypeOfSymbol(symbol); - if (!areTypeParametersIdentical(declarations, type.localTypeParameters!)) { - // Report an error on every conflicting declaration. - const name = symbolToString(symbol); - for (const declaration of declarations) { - error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name); - } - } - } - } - - function areTypeParametersIdentical(declarations: readonly (ClassDeclaration | InterfaceDeclaration)[], targetParameters: TypeParameter[]) { - const maxTypeArgumentCount = length(targetParameters); - const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters); - - for (const declaration of declarations) { - // If this declaration has too few or too many type parameters, we report an error - const sourceParameters = getEffectiveTypeParameterDeclarations(declaration); - const numTypeParameters = sourceParameters.length; - if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) { - return false; - } - - for (let i = 0; i < numTypeParameters; i++) { - const source = sourceParameters[i]; - const target = targetParameters[i]; - - // If the type parameter node does not have the same as the resolved type - // parameter at this position, we report an error. - if (source.name.escapedText !== target.symbol.escapedName) { - return false; - } - - // If the type parameter node does not have an identical constraint as the resolved - // type parameter at this position, we report an error. - const constraint = getEffectiveConstraintOfTypeParameter(source); - const sourceConstraint = constraint && getTypeFromTypeNode(constraint); - const targetConstraint = getConstraintOfTypeParameter(target); - // relax check if later interface augmentation has no constraint, it's more broad and is OK to merge with - // a more constrained interface (this could be generalized to a full heirarchy check, but that's maybe overkill) - if (sourceConstraint && targetConstraint && !isTypeIdenticalTo(sourceConstraint, targetConstraint)) { - return false; - } - - // If the type parameter node has a default and it is not identical to the default - // for the type parameter at this position, we report an error. - const sourceDefault = source.default && getTypeFromTypeNode(source.default); - const targetDefault = getDefaultFromTypeParameter(target); - if (sourceDefault && targetDefault && !isTypeIdenticalTo(sourceDefault, targetDefault)) { - return false; - } - } - } - - return true; - } - - function checkClassExpression(node: ClassExpression): Type { - checkClassLikeDeclaration(node); - checkNodeDeferred(node); - return getTypeOfSymbol(getSymbolOfNode(node)); - } - - function checkClassExpressionDeferred(node: ClassExpression) { - forEach(node.members, checkSourceElement); - registerForUnusedIdentifiersCheck(node); - } - - function checkClassDeclaration(node: ClassDeclaration) { - if (!node.name && !hasModifier(node, ModifierFlags.Default)) { - grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name); - } - checkClassLikeDeclaration(node); - forEach(node.members, checkSourceElement); - - registerForUnusedIdentifiersCheck(node); - } - - function checkClassLikeDeclaration(node: ClassLikeDeclaration) { - checkGrammarClassLikeDeclaration(node); - checkDecorators(node); - if (node.name) { - checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); - if (!(node.flags & NodeFlags.Ambient)) { - checkClassNameCollisionWithObject(node.name); - } - } - checkTypeParameters(getEffectiveTypeParameterDeclarations(node)); - checkExportsOnMergedDeclarations(node); - const symbol = getSymbolOfNode(node); - const type = getDeclaredTypeOfSymbol(symbol); - const typeWithThis = getTypeWithThisArgument(type); - const staticType = getTypeOfSymbol(symbol); - checkTypeParameterListsIdentical(symbol); - checkClassForDuplicateDeclarations(node); - - // Only check for reserved static identifiers on non-ambient context. - if (!(node.flags & NodeFlags.Ambient)) { - checkClassForStaticPropertyNameConflicts(node); - } - - const baseTypeNode = getEffectiveBaseTypeNode(node); - if (baseTypeNode) { - forEach(baseTypeNode.typeArguments, checkSourceElement); - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends); - } - // check both @extends and extends if both are specified. - const extendsNode = getClassExtendsHeritageElement(node); - if (extendsNode && extendsNode !== baseTypeNode) { - checkExpression(extendsNode.expression); - } - - const baseTypes = getBaseTypes(type); - if (baseTypes.length && produceDiagnostics) { - const baseType = baseTypes[0]; - const baseConstructorType = getBaseConstructorTypeOfClass(type); - const staticBaseType = getApparentType(baseConstructorType); - checkBaseTypeAccessibility(staticBaseType, baseTypeNode); - checkSourceElement(baseTypeNode.expression); - if (some(baseTypeNode.typeArguments)) { - forEach(baseTypeNode.typeArguments, checkSourceElement); - for (const constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode)) { - if (!checkTypeArgumentConstraints(baseTypeNode, constructor.typeParameters!)) { - break; - } - } - } - const baseWithThis = getTypeWithThisArgument(baseType, type.thisType); - if (!checkTypeAssignableTo(typeWithThis, baseWithThis, /*errorNode*/ undefined)) { - issueMemberSpecificError(node, typeWithThis, baseWithThis, Diagnostics.Class_0_incorrectly_extends_base_class_1); - } - else { - // Report static side error only when instance type is assignable - checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node, - Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); - } - if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) { - error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any); - } - - if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) { - // When the static base type is a "class-like" constructor function (but not actually a class), we verify - // that all instantiated base constructor signatures return the same type. - const constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode); - if (forEach(constructors, sig => !isJSConstructor(sig.declaration) && !isTypeIdenticalTo(getReturnTypeOfSignature(sig), baseType))) { - error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type); - } - } - checkKindsOfPropertyMemberOverrides(type, baseType); - } - } - - const implementedTypeNodes = getClassImplementsHeritageClauseElements(node); - if (implementedTypeNodes) { - for (const typeRefNode of implementedTypeNodes) { - if (!isEntityNameExpression(typeRefNode.expression)) { - error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments); - } - checkTypeReferenceNode(typeRefNode); - if (produceDiagnostics) { - const t = getTypeFromTypeNode(typeRefNode); - if (t !== errorType) { - if (isValidBaseType(t)) { - const genericDiag = t.symbol && t.symbol.flags & SymbolFlags.Class ? - Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass : - Diagnostics.Class_0_incorrectly_implements_interface_1; - const baseWithThis = getTypeWithThisArgument(t, type.thisType); - if (!checkTypeAssignableTo(typeWithThis, baseWithThis, /*errorNode*/ undefined)) { - issueMemberSpecificError(node, typeWithThis, baseWithThis, genericDiag); - } - } - else { - error(typeRefNode, Diagnostics.A_class_can_only_implement_an_object_type_or_intersection_of_object_types_with_statically_known_members); - } - } - } - } - } - - if (produceDiagnostics) { - checkIndexConstraints(type); - checkTypeForDuplicateIndexSignatures(node); - checkPropertyInitialization(node); - } - } - - function issueMemberSpecificError(node: ClassLikeDeclaration, typeWithThis: Type, baseWithThis: Type, broadDiag: DiagnosticMessage) { - // iterate over all implemented properties and issue errors on each one which isn't compatible, rather than the class as a whole, if possible - let issuedMemberError = false; - for (const member of node.members) { - if (hasStaticModifier(member)) { - continue; - } - const declaredProp = member.name && getSymbolAtLocation(member.name) || getSymbolAtLocation(member); - if (declaredProp) { - const prop = getPropertyOfType(typeWithThis, declaredProp.escapedName); - const baseProp = getPropertyOfType(baseWithThis, declaredProp.escapedName); - if (prop && baseProp) { - const rootChain = () => chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics.Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2, - symbolToString(declaredProp), - typeToString(typeWithThis), - typeToString(baseWithThis) - ); - if (!checkTypeAssignableTo(getTypeOfSymbol(prop), getTypeOfSymbol(baseProp), member.name || member, /*message*/ undefined, rootChain)) { - issuedMemberError = true; - } - } - } - } - if (!issuedMemberError) { - // check again with diagnostics to generate a less-specific error - checkTypeAssignableTo(typeWithThis, baseWithThis, node.name || node, broadDiag); - } - } - - function checkBaseTypeAccessibility(type: Type, node: ExpressionWithTypeArguments) { - const signatures = getSignaturesOfType(type, SignatureKind.Construct); - if (signatures.length) { - const declaration = signatures[0].declaration; - if (declaration && hasModifier(declaration, ModifierFlags.Private)) { - const typeClassDeclaration = getClassLikeDeclarationOfSymbol(type.symbol)!; - if (!isNodeWithinClass(node, typeClassDeclaration)) { - error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, getFullyQualifiedName(type.symbol)); - } - } - } - } - - function getTargetSymbol(s: Symbol) { - // if symbol is instantiated its flags are not copied from the 'target' - // so we'll need to get back original 'target' symbol to work with correct set of flags - return getCheckFlags(s) & CheckFlags.Instantiated ? (s).target! : s; - } - - function getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) { - return filter(symbol.declarations, (d: Declaration): d is ClassDeclaration | InterfaceDeclaration => - d.kind === SyntaxKind.ClassDeclaration || d.kind === SyntaxKind.InterfaceDeclaration); - } - - function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: BaseType): void { - // TypeScript 1.0 spec (April 2014): 8.2.3 - // A derived class inherits all members from its base class it doesn't override. - // Inheritance means that a derived class implicitly contains all non - overridden members of the base class. - // Both public and private property members are inherited, but only public property members can be overridden. - // A property member in a derived class is said to override a property member in a base class - // when the derived class property member has the same name and kind(instance or static) - // as the base class property member. - // The type of an overriding property member must be assignable(section 3.8.4) - // to the type of the overridden property member, or otherwise a compile - time error occurs. - // Base class instance member functions can be overridden by derived class instance member functions, - // but not by other kinds of members. - // Base class instance member variables and accessors can be overridden by - // derived class instance member variables and accessors, but not by other kinds of members. - - // NOTE: assignability is checked in checkClassDeclaration - const baseProperties = getPropertiesOfType(baseType); - basePropertyCheck: for (const baseProperty of baseProperties) { - const base = getTargetSymbol(baseProperty); - - if (base.flags & SymbolFlags.Prototype) { - continue; - } - const baseSymbol = getPropertyOfObjectType(type, base.escapedName); - if (!baseSymbol) { - continue; - } - const derived = getTargetSymbol(baseSymbol); - const baseDeclarationFlags = getDeclarationModifierFlagsFromSymbol(base); - - Debug.assert(!!derived, "derived should point to something, even if it is the base class' declaration."); - - // In order to resolve whether the inherited method was overridden in the base class or not, - // we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated* - // type declaration, derived and base resolve to the same symbol even in the case of generic classes. - if (derived === base) { - // derived class inherits base without override/redeclaration - const derivedClassDecl = getClassLikeDeclarationOfSymbol(type.symbol)!; - - // It is an error to inherit an abstract member without implementing it or being declared abstract. - // If there is no declaration for the derived class (as in the case of class expressions), - // then the class cannot be declared abstract. - if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !hasModifier(derivedClassDecl, ModifierFlags.Abstract))) { - // Searches other base types for a declaration that would satisfy the inherited abstract member. - // (The class may have more than one base type via declaration merging with an interface with the - // same name.) - for (const otherBaseType of getBaseTypes(type)) { - if (otherBaseType === baseType) continue; - const baseSymbol = getPropertyOfObjectType(otherBaseType, base.escapedName); - const derivedElsewhere = baseSymbol && getTargetSymbol(baseSymbol); - if (derivedElsewhere && derivedElsewhere !== base) { - continue basePropertyCheck; - } - } - - if (derivedClassDecl.kind === SyntaxKind.ClassExpression) { - error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, - symbolToString(baseProperty), typeToString(baseType)); - } - else { - error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, - typeToString(type), symbolToString(baseProperty), typeToString(baseType)); - } - } - } - else { - // derived overrides base. - const derivedDeclarationFlags = getDeclarationModifierFlagsFromSymbol(derived); - if (baseDeclarationFlags & ModifierFlags.Private || derivedDeclarationFlags & ModifierFlags.Private) { - // either base or derived property is private - not override, skip it - continue; - } - - let errorMessage: DiagnosticMessage; - const basePropertyFlags = base.flags & SymbolFlags.PropertyOrAccessor; - const derivedPropertyFlags = derived.flags & SymbolFlags.PropertyOrAccessor; - if (basePropertyFlags && derivedPropertyFlags) { - // property/accessor is overridden with property/accessor - if (!compilerOptions.useDefineForClassFields - || baseDeclarationFlags & ModifierFlags.Abstract && !(base.valueDeclaration && isPropertyDeclaration(base.valueDeclaration) && base.valueDeclaration.initializer) - || base.valueDeclaration && base.valueDeclaration.parent.kind === SyntaxKind.InterfaceDeclaration - || derived.valueDeclaration && isBinaryExpression(derived.valueDeclaration)) { - // when the base property is abstract or from an interface, base/derived flags don't need to match - // same when the derived property is from an assignment - continue; - } - - const overriddenInstanceProperty = basePropertyFlags !== SymbolFlags.Property && derivedPropertyFlags === SymbolFlags.Property; - const overriddenInstanceAccessor = basePropertyFlags === SymbolFlags.Property && derivedPropertyFlags !== SymbolFlags.Property; - if (overriddenInstanceProperty || overriddenInstanceAccessor) { - const errorMessage = overriddenInstanceProperty ? - Diagnostics._0_is_defined_as_an_accessor_in_class_1_but_is_overridden_here_in_2_as_an_instance_property : - Diagnostics._0_is_defined_as_a_property_in_class_1_but_is_overridden_here_in_2_as_an_accessor; - error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, symbolToString(base), typeToString(baseType), typeToString(type)); - } - else { - const uninitialized = find(derived.declarations, d => d.kind === SyntaxKind.PropertyDeclaration && !(d as PropertyDeclaration).initializer); - if (uninitialized - && !(derived.flags & SymbolFlags.Transient) - && !(baseDeclarationFlags & ModifierFlags.Abstract) - && !(derivedDeclarationFlags & ModifierFlags.Abstract) - && !derived.declarations.some(d => !!(d.flags & NodeFlags.Ambient))) { - const constructor = findConstructorDeclaration(getClassLikeDeclarationOfSymbol(type.symbol)!); - const propName = (uninitialized as PropertyDeclaration).name; - if ((uninitialized as PropertyDeclaration).exclamationToken - || !constructor - || !isIdentifier(propName) - || !strictNullChecks - || !isPropertyInitializedInConstructor(propName, type, constructor)) { - const errorMessage = Diagnostics.Property_0_will_overwrite_the_base_property_in_1_If_this_is_intentional_add_an_initializer_Otherwise_add_a_declare_modifier_or_remove_the_redundant_declaration; - error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, symbolToString(base), typeToString(baseType)); - } - } - } - - // correct case - continue; - } - else if (isPrototypeProperty(base)) { - if (isPrototypeProperty(derived) || derived.flags & SymbolFlags.Property) { - // method is overridden with method or property -- correct case - continue; - } - else { - Debug.assert(!!(derived.flags & SymbolFlags.Accessor)); - errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor; - } - } - else if (base.flags & SymbolFlags.Accessor) { - errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function; - } - else { - errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function; - } - - error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type)); - } - } - } - - function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean { - const baseTypes = getBaseTypes(type); - if (baseTypes.length < 2) { - return true; - } - - interface InheritanceInfoMap { prop: Symbol; containingType: Type; } - const seen = createUnderscoreEscapedMap(); - forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen.set(p.escapedName, { prop: p, containingType: type }); }); - let ok = true; - - for (const base of baseTypes) { - const properties = getPropertiesOfType(getTypeWithThisArgument(base, type.thisType)); - for (const prop of properties) { - const existing = seen.get(prop.escapedName); - if (!existing) { - seen.set(prop.escapedName, { prop, containingType: base }); - } - else { - const isInheritedProperty = existing.containingType !== type; - if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) { - ok = false; - - const typeName1 = typeToString(existing.containingType); - const typeName2 = typeToString(base); - - let errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Named_property_0_of_types_1_and_2_are_not_identical, symbolToString(prop), typeName1, typeName2); - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, typeToString(type), typeName1, typeName2); - diagnostics.add(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo)); - } - } - } - } - - return ok; - } - - function checkPropertyInitialization(node: ClassLikeDeclaration) { - if (!strictNullChecks || !strictPropertyInitialization || node.flags & NodeFlags.Ambient) { - return; - } - const constructor = findConstructorDeclaration(node); - for (const member of node.members) { - if (getModifierFlags(member) & ModifierFlags.Ambient) { - continue; - } - if (isInstancePropertyWithoutInitializer(member)) { - const propName = (member).name; - if (isIdentifier(propName)) { - const type = getTypeOfSymbol(getSymbolOfNode(member)); - if (!(type.flags & TypeFlags.AnyOrUnknown || getFalsyFlags(type) & TypeFlags.Undefined)) { - if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) { - error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName)); - } - } - } - } - } - } - - function isInstancePropertyWithoutInitializer(node: Node) { - return node.kind === SyntaxKind.PropertyDeclaration && - !hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) && - !(node).exclamationToken && - !(node).initializer; - } - - function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) { - const reference = createPropertyAccess(createThis(), propName); - reference.expression.parent = reference; - reference.parent = constructor; - reference.flowNode = constructor.returnFlowNode; - const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType)); - return !(getFalsyFlags(flowType) & TypeFlags.Undefined); - } - - function checkInterfaceDeclaration(node: InterfaceDeclaration) { - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node); - - checkTypeParameters(node.typeParameters); - if (produceDiagnostics) { - checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); - - checkExportsOnMergedDeclarations(node); - const symbol = getSymbolOfNode(node); - checkTypeParameterListsIdentical(symbol); - - // Only check this symbol once - const firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); - if (node === firstInterfaceDecl) { - const type = getDeclaredTypeOfSymbol(symbol); - const typeWithThis = getTypeWithThisArgument(type); - // run subsequent checks only if first set succeeded - if (checkInheritedPropertiesAreIdentical(type, node.name)) { - for (const baseType of getBaseTypes(type)) { - checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1); - } - checkIndexConstraints(type); - } - } - checkObjectTypeForDuplicateDeclarations(node); - } - forEach(getInterfaceBaseTypeNodes(node), heritageElement => { - if (!isEntityNameExpression(heritageElement.expression)) { - error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments); - } - checkTypeReferenceNode(heritageElement); - }); - - forEach(node.members, checkSourceElement); - - if (produceDiagnostics) { - checkTypeForDuplicateIndexSignatures(node); - registerForUnusedIdentifiersCheck(node); - } - } - - function checkTypeAliasDeclaration(node: TypeAliasDeclaration) { - // Grammar checking - checkGrammarDecoratorsAndModifiers(node); - - checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); - checkTypeParameters(node.typeParameters); - checkSourceElement(node.type); - registerForUnusedIdentifiersCheck(node); - } - - function computeEnumMemberValues(node: EnumDeclaration) { - const nodeLinks = getNodeLinks(node); - if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { - nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; - let autoValue: number | undefined = 0; - for (const member of node.members) { - const value = computeMemberValue(member, autoValue); - getNodeLinks(member).enumMemberValue = value; - autoValue = typeof value === "number" ? value + 1 : undefined; - } - } - } - - function computeMemberValue(member: EnumMember, autoValue: number | undefined) { - if (isComputedNonLiteralName(member.name)) { - error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums); - } - else { - const text = getTextOfPropertyName(member.name); - if (isNumericLiteralName(text) && !isInfinityOrNaNString(text)) { - error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name); - } - } - if (member.initializer) { - return computeConstantValue(member); - } - // In ambient non-const numeric enum declarations, enum members without initializers are - // considered computed members (as opposed to having auto-incremented values). - if (member.parent.flags & NodeFlags.Ambient && !isEnumConst(member.parent) && getEnumKind(getSymbolOfNode(member.parent)) === EnumKind.Numeric) { - return undefined; - } - // If the member declaration specifies no value, the member is considered a constant enum member. - // If the member is the first member in the enum declaration, it is assigned the value zero. - // Otherwise, it is assigned the value of the immediately preceding member plus one, and an error - // occurs if the immediately preceding member is not a constant enum member. - if (autoValue !== undefined) { - return autoValue; - } - error(member.name, Diagnostics.Enum_member_must_have_initializer); - return undefined; - } - - function computeConstantValue(member: EnumMember): string | number | undefined { - const enumKind = getEnumKind(getSymbolOfNode(member.parent)); - const isConstEnum = isEnumConst(member.parent); - const initializer = member.initializer!; - const value = enumKind === EnumKind.Literal && !isLiteralEnumMember(member) ? undefined : evaluate(initializer); - if (value !== undefined) { - if (isConstEnum && typeof value === "number" && !isFinite(value)) { - error(initializer, isNaN(value) ? - Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN : - Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value); - } - } - else if (enumKind === EnumKind.Literal) { - error(initializer, Diagnostics.Computed_values_are_not_permitted_in_an_enum_with_string_valued_members); - return 0; - } - else if (isConstEnum) { - error(initializer, Diagnostics.const_enum_member_initializers_can_only_contain_literal_values_and_other_computed_enum_values); - } - else if (member.parent.flags & NodeFlags.Ambient) { - error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression); - } - else { - // Only here do we need to check that the initializer is assignable to the enum type. - checkTypeAssignableTo(checkExpression(initializer), getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined); - } - return value; - - function evaluate(expr: Expression): string | number | undefined { - switch (expr.kind) { - case SyntaxKind.PrefixUnaryExpression: - const value = evaluate((expr).operand); - if (typeof value === "number") { - switch ((expr).operator) { - case SyntaxKind.PlusToken: return value; - case SyntaxKind.MinusToken: return -value; - case SyntaxKind.TildeToken: return ~value; - } - } - break; - case SyntaxKind.BinaryExpression: - const left = evaluate((expr).left); - const right = evaluate((expr).right); - if (typeof left === "number" && typeof right === "number") { - switch ((expr).operatorToken.kind) { - case SyntaxKind.BarToken: return left | right; - case SyntaxKind.AmpersandToken: return left & right; - case SyntaxKind.GreaterThanGreaterThanToken: return left >> right; - case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right; - case SyntaxKind.LessThanLessThanToken: return left << right; - case SyntaxKind.CaretToken: return left ^ right; - case SyntaxKind.AsteriskToken: return left * right; - case SyntaxKind.SlashToken: return left / right; - case SyntaxKind.PlusToken: return left + right; - case SyntaxKind.MinusToken: return left - right; - case SyntaxKind.PercentToken: return left % right; - case SyntaxKind.AsteriskAsteriskToken: return left ** right; - } - } - else if (typeof left === "string" && typeof right === "string" && (expr).operatorToken.kind === SyntaxKind.PlusToken) { - return left + right; - } - break; - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return (expr).text; - case SyntaxKind.NumericLiteral: - checkGrammarNumericLiteral(expr); - return +(expr).text; - case SyntaxKind.ParenthesizedExpression: - return evaluate((expr).expression); - case SyntaxKind.Identifier: - const identifier = expr; - if (isInfinityOrNaNString(identifier.escapedText)) { - return +(identifier.escapedText); - } - return nodeIsMissing(expr) ? 0 : evaluateEnumMember(expr, getSymbolOfNode(member.parent), identifier.escapedText); - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.PropertyAccessExpression: - const ex = expr; - if (isConstantMemberAccess(ex)) { - const type = getTypeOfExpression(ex.expression); - if (type.symbol && type.symbol.flags & SymbolFlags.Enum) { - let name: __String; - if (ex.kind === SyntaxKind.PropertyAccessExpression) { - name = ex.name.escapedText; - } - else { - name = escapeLeadingUnderscores(cast(ex.argumentExpression, isLiteralExpression).text); - } - return evaluateEnumMember(expr, type.symbol, name); - } - } - break; - } - return undefined; - } - - function evaluateEnumMember(expr: Expression, enumSymbol: Symbol, name: __String) { - const memberSymbol = enumSymbol.exports!.get(name); - if (memberSymbol) { - const declaration = memberSymbol.valueDeclaration; - if (declaration !== member) { - if (isBlockScopedNameDeclaredBeforeUse(declaration, member)) { - return getEnumMemberValue(declaration as EnumMember); - } - error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums); - return 0; - } - } - return undefined; - } - } - - function isConstantMemberAccess(node: Expression): boolean { - return node.kind === SyntaxKind.Identifier || - node.kind === SyntaxKind.PropertyAccessExpression && isConstantMemberAccess((node).expression) || - node.kind === SyntaxKind.ElementAccessExpression && isConstantMemberAccess((node).expression) && - isStringLiteralLike((node).argumentExpression); - } - - function checkEnumDeclaration(node: EnumDeclaration) { - if (!produceDiagnostics) { - return; - } - - // Grammar checking - checkGrammarDecoratorsAndModifiers(node); - - checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); - checkExportsOnMergedDeclarations(node); - node.members.forEach(checkEnumMember); - - computeEnumMemberValues(node); - - // Spec 2014 - Section 9.3: - // It isn't possible for one enum declaration to continue the automatic numbering sequence of another, - // and when an enum type has multiple declarations, only one declaration is permitted to omit a value - // for the first member. - // - // Only perform this check once per symbol - const enumSymbol = getSymbolOfNode(node); - const firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind); - if (node === firstDeclaration) { - if (enumSymbol.declarations.length > 1) { - const enumIsConst = isEnumConst(node); - // check that const is placed\omitted on all enum declarations - forEach(enumSymbol.declarations, decl => { - if (isEnumDeclaration(decl) && isEnumConst(decl) !== enumIsConst) { - error(getNameOfDeclaration(decl), Diagnostics.Enum_declarations_must_all_be_const_or_non_const); - } - }); - } - - let seenEnumMissingInitialInitializer = false; - forEach(enumSymbol.declarations, declaration => { - // return true if we hit a violation of the rule, false otherwise - if (declaration.kind !== SyntaxKind.EnumDeclaration) { - return false; - } - - const enumDeclaration = declaration; - if (!enumDeclaration.members.length) { - return false; - } - - const firstEnumMember = enumDeclaration.members[0]; - if (!firstEnumMember.initializer) { - if (seenEnumMissingInitialInitializer) { - error(firstEnumMember.name, Diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element); - } - else { - seenEnumMissingInitialInitializer = true; - } - } - }); - } - } - - function checkEnumMember(node: EnumMember) { - if (isPrivateIdentifier(node.name)) { - error(node, Diagnostics.An_enum_member_cannot_be_named_with_a_private_identifier); - } - } - - function getFirstNonAmbientClassOrFunctionDeclaration(symbol: Symbol): Declaration | undefined { - const declarations = symbol.declarations; - for (const declaration of declarations) { - if ((declaration.kind === SyntaxKind.ClassDeclaration || - (declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((declaration).body))) && - !(declaration.flags & NodeFlags.Ambient)) { - return declaration; - } - } - return undefined; - } - - function inSameLexicalScope(node1: Node, node2: Node) { - const container1 = getEnclosingBlockScopeContainer(node1); - const container2 = getEnclosingBlockScopeContainer(node2); - if (isGlobalSourceFile(container1)) { - return isGlobalSourceFile(container2); - } - else if (isGlobalSourceFile(container2)) { - return false; - } - else { - return container1 === container2; - } - } - - function checkModuleDeclaration(node: ModuleDeclaration) { - if (produceDiagnostics) { - // Grammar checking - const isGlobalAugmentation = isGlobalScopeAugmentation(node); - const inAmbientContext = node.flags & NodeFlags.Ambient; - if (isGlobalAugmentation && !inAmbientContext) { - error(node.name, Diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context); - } - - const isAmbientExternalModule = isAmbientModule(node); - const contextErrorMessage = isAmbientExternalModule - ? Diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file - : Diagnostics.A_namespace_declaration_is_only_allowed_in_a_namespace_or_module; - if (checkGrammarModuleElementContext(node, contextErrorMessage)) { - // If we hit a module declaration in an illegal context, just bail out to avoid cascading errors. - return; - } - - if (!checkGrammarDecoratorsAndModifiers(node)) { - if (!inAmbientContext && node.name.kind === SyntaxKind.StringLiteral) { - grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names); - } - } - - if (isIdentifier(node.name)) { - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); - } - - checkExportsOnMergedDeclarations(node); - const symbol = getSymbolOfNode(node); - - // The following checks only apply on a non-ambient instantiated module declaration. - if (symbol.flags & SymbolFlags.ValueModule - && !inAmbientContext - && symbol.declarations.length > 1 - && isInstantiatedModule(node, !!compilerOptions.preserveConstEnums || !!compilerOptions.isolatedModules)) { - const firstNonAmbientClassOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol); - if (firstNonAmbientClassOrFunc) { - if (getSourceFileOfNode(node) !== getSourceFileOfNode(firstNonAmbientClassOrFunc)) { - error(node.name, Diagnostics.A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged); - } - else if (node.pos < firstNonAmbientClassOrFunc.pos) { - error(node.name, Diagnostics.A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged); - } - } - - // if the module merges with a class declaration in the same lexical scope, - // we need to track this to ensure the correct emit. - const mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration); - if (mergedClass && - inSameLexicalScope(node, mergedClass)) { - getNodeLinks(node).flags |= NodeCheckFlags.LexicalModuleMergesWithClass; - } - } - - if (isAmbientExternalModule) { - if (isExternalModuleAugmentation(node)) { - // body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module) - // otherwise we'll be swamped in cascading errors. - // We can detect if augmentation was applied using following rules: - // - augmentation for a global scope is always applied - // - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module). - const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Transient); - if (checkBody && node.body) { - for (const statement of node.body.statements) { - checkModuleAugmentationElement(statement, isGlobalAugmentation); - } - } - } - else if (isGlobalSourceFile(node.parent)) { - if (isGlobalAugmentation) { - error(node.name, Diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations); - } - else if (isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(node.name))) { - error(node.name, Diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name); - } - } - else { - if (isGlobalAugmentation) { - error(node.name, Diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations); - } - else { - // Node is not an augmentation and is not located on the script level. - // This means that this is declaration of ambient module that is located in other module or namespace which is prohibited. - error(node.name, Diagnostics.Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces); - } - } - } - } - - if (node.body) { - checkSourceElement(node.body); - if (!isGlobalScopeAugmentation(node)) { - registerForUnusedIdentifiersCheck(node); - } - } - } - - function checkModuleAugmentationElement(node: Node, isGlobalAugmentation: boolean): void { - switch (node.kind) { - case SyntaxKind.VariableStatement: - // error each individual name in variable statement instead of marking the entire variable statement - for (const decl of (node).declarationList.declarations) { - checkModuleAugmentationElement(decl, isGlobalAugmentation); - } - break; - case SyntaxKind.ExportAssignment: - case SyntaxKind.ExportDeclaration: - grammarErrorOnFirstToken(node, Diagnostics.Exports_and_export_assignments_are_not_permitted_in_module_augmentations); - break; - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportDeclaration: - grammarErrorOnFirstToken(node, Diagnostics.Imports_are_not_permitted_in_module_augmentations_Consider_moving_them_to_the_enclosing_external_module); - break; - case SyntaxKind.BindingElement: - case SyntaxKind.VariableDeclaration: - const name = (node).name; - if (isBindingPattern(name)) { - for (const el of name.elements) { - // mark individual names in binding pattern - checkModuleAugmentationElement(el, isGlobalAugmentation); - } - break; - } - // falls through - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.TypeAliasDeclaration: - if (isGlobalAugmentation) { - return; - } - const symbol = getSymbolOfNode(node); - if (symbol) { - // module augmentations cannot introduce new names on the top level scope of the module - // this is done it two steps - // 1. quick check - if symbol for node is not merged - this is local symbol to this augmentation - report error - // 2. main check - report error if value declaration of the parent symbol is module augmentation) - let reportError = !(symbol.flags & SymbolFlags.Transient); - if (!reportError) { - // symbol should not originate in augmentation - reportError = !!symbol.parent && isExternalModuleAugmentation(symbol.parent.declarations[0]); - } - } - break; - } - } - - function getFirstNonModuleExportsIdentifier(node: EntityNameOrEntityNameExpression): Identifier { - switch (node.kind) { - case SyntaxKind.Identifier: - return node; - case SyntaxKind.QualifiedName: - do { - node = node.left; - } while (node.kind !== SyntaxKind.Identifier); - return node; - case SyntaxKind.PropertyAccessExpression: - do { - if (isModuleExportsAccessExpression(node.expression) && !isPrivateIdentifier(node.name)) { - return node.name; - } - node = node.expression; - } while (node.kind !== SyntaxKind.Identifier); - return node; - } - } - - function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { - const moduleName = getExternalModuleName(node); - if (!moduleName || nodeIsMissing(moduleName)) { - // Should be a parse error. - return false; - } - if (!isStringLiteral(moduleName)) { - error(moduleName, Diagnostics.String_literal_expected); - return false; - } - const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent); - if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule) { - error(moduleName, node.kind === SyntaxKind.ExportDeclaration ? - Diagnostics.Export_declarations_are_not_permitted_in_a_namespace : - Diagnostics.Import_declarations_in_a_namespace_cannot_reference_a_module); - return false; - } - if (inAmbientExternalModule && isExternalModuleNameRelative(moduleName.text)) { - // we have already reported errors on top level imports\exports in external module augmentations in checkModuleDeclaration - // no need to do this again. - if (!isTopLevelInExternalModuleAugmentation(node)) { - // TypeScript 1.0 spec (April 2013): 12.1.6 - // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference - // other external modules only through top - level external module names. - // Relative external module names are not permitted. - error(node, Diagnostics.Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name); - return false; - } - } - return true; - } - - function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier | NamespaceExport) { - let symbol = getSymbolOfNode(node); - const target = resolveAlias(symbol); - - const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment; - if (!shouldSkipWithJSExpandoTargets && target !== unknownSymbol) { - // For external modules symbol represents local symbol for an alias. - // This local symbol will merge any other local declarations (excluding other aliases) - // and symbol.flags will contains combined representation for all merged declaration. - // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, - // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* - // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). - symbol = getMergedSymbol(symbol.exportSymbol || symbol); - const excludedMeanings = - (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | - (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | - (symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0); - if (target.flags & excludedMeanings) { - const message = node.kind === SyntaxKind.ExportSpecifier ? - Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 : - Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0; - error(node, message, symbolToString(symbol)); - } - - // Don't allow to re-export something with no value side when `--isolatedModules` is set. - if (compilerOptions.isolatedModules - && node.kind === SyntaxKind.ExportSpecifier - && !node.parent.parent.isTypeOnly - && !(target.flags & SymbolFlags.Value) - && !(node.flags & NodeFlags.Ambient)) { - error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type); - } - } - } - - function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) { - checkCollisionWithRequireExportsInGeneratedCode(node, node.name!); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name!); - checkAliasSymbol(node); - } - - function checkImportDeclaration(node: ImportDeclaration) { - if (checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) { - // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. - return; - } - if (!checkGrammarDecoratorsAndModifiers(node) && hasModifiers(node)) { - grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers); - } - if (checkExternalImportOrExportDeclaration(node)) { - const importClause = node.importClause; - if (importClause && !checkGrammarImportClause(importClause)) { - if (importClause.name) { - checkImportBinding(importClause); - } - if (importClause.namedBindings) { - if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - checkImportBinding(importClause.namedBindings); - } - else { - const moduleExisted = resolveExternalModuleName(node, node.moduleSpecifier); - if (moduleExisted) { - forEach(importClause.namedBindings.elements, checkImportBinding); - } - } - } - } - } - } - - function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) { - if (checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) { - // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. - return; - } - - checkGrammarDecoratorsAndModifiers(node); - if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) { - checkImportBinding(node); - if (hasModifier(node, ModifierFlags.Export)) { - markExportAsReferenced(node); - } - if (node.moduleReference.kind !== SyntaxKind.ExternalModuleReference) { - const target = resolveAlias(getSymbolOfNode(node)); - if (target !== unknownSymbol) { - if (target.flags & SymbolFlags.Value) { - // Target is a value symbol, check that it is not hidden by a local declaration with the same name - const moduleName = getFirstIdentifier(node.moduleReference); - if (!(resolveEntityName(moduleName, SymbolFlags.Value | SymbolFlags.Namespace)!.flags & SymbolFlags.Namespace)) { - error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName)); - } - } - if (target.flags & SymbolFlags.Type) { - checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0); - } - } - } - else { - if (moduleKind >= ModuleKind.ES2015 && !(node.flags & NodeFlags.Ambient)) { - // Import equals declaration is deprecated in es6 or above - grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead); - } - } - } - } - - function checkExportDeclaration(node: ExportDeclaration) { - if (checkGrammarModuleElementContext(node, Diagnostics.An_export_declaration_can_only_be_used_in_a_module)) { - // If we hit an export in an illegal context, just bail out to avoid cascading errors. - return; - } - - if (!checkGrammarDecoratorsAndModifiers(node) && hasModifiers(node)) { - grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers); - } - - if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) { - if (node.exportClause) { - // export { x, y } - // export { x, y } from "foo" - if (isNamedExports(node.exportClause)) { - forEach(node.exportClause.elements, checkExportSpecifier); - } - else if(!isNamespaceExport(node.exportClause)) { - checkImportBinding(node.exportClause); - } - - const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent); - const inAmbientNamespaceDeclaration = !inAmbientExternalModule && node.parent.kind === SyntaxKind.ModuleBlock && - !node.moduleSpecifier && node.flags & NodeFlags.Ambient; - if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule && !inAmbientNamespaceDeclaration) { - error(node, Diagnostics.Export_declarations_are_not_permitted_in_a_namespace); - } - } - else { - // export * from "foo" - const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier!); - if (moduleSymbol && hasExportAssignmentSymbol(moduleSymbol)) { - error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol)); - } - - if (moduleKind !== ModuleKind.System && moduleKind < ModuleKind.ES2015) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.ExportStar); - } - } - } - } - - function checkGrammarModuleElementContext(node: Statement, errorMessage: DiagnosticMessage): boolean { - const isInAppropriateContext = node.parent.kind === SyntaxKind.SourceFile || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.ModuleDeclaration; - if (!isInAppropriateContext) { - grammarErrorOnFirstToken(node, errorMessage); - } - return !isInAppropriateContext; - } - - function importClauseContainsReferencedImport(importClause: ImportClause) { - return importClause.name && isReferenced(importClause) - || importClause.namedBindings && namedBindingsContainsReferencedImport(importClause.namedBindings); - - function isReferenced(declaration: Declaration) { - return !!getMergedSymbol(getSymbolOfNode(declaration)).isReferenced; - } - function namedBindingsContainsReferencedImport(namedBindings: NamedImportBindings) { - return isNamespaceImport(namedBindings) - ? isReferenced(namedBindings) - : some(namedBindings.elements, isReferenced); - } - } - - function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if ( - isImportDeclaration(statement) && - statement.importClause && - !statement.importClause.isTypeOnly && - importClauseContainsReferencedImport(statement.importClause) && - !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) - ) { - const isError = compilerOptions.importsNotUsedAsValue === ImportsNotUsedAsValue.Error; - errorOrSuggestion( - isError, - statement, - isError - ? Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValue_is_set_to_error - : Diagnostics.This_import_may_be_converted_to_a_type_only_import); - } - } - } - - function checkExportSpecifier(node: ExportSpecifier) { - checkAliasSymbol(node); - if (getEmitDeclarations(compilerOptions)) { - collectLinkedAliases(node.propertyName || node.name, /*setVisibility*/ true); - } - if (!node.parent.parent.moduleSpecifier) { - const exportedName = node.propertyName || node.name; - // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) - const symbol = resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, - /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); - if (symbol && (symbol === undefinedSymbol || symbol === globalThisSymbol || isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) { - error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, idText(exportedName)); - } - else { - markExportAsReferenced(node); - const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol); - if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) { - checkExpressionCached(node.propertyName || node.name); - } - } - } - } - - function checkExportAssignment(node: ExportAssignment) { - if (checkGrammarModuleElementContext(node, Diagnostics.An_export_assignment_can_only_be_used_in_a_module)) { - // If we hit an export assignment in an illegal context, just bail out to avoid cascading errors. - return; - } - - const container = node.parent.kind === SyntaxKind.SourceFile ? node.parent : node.parent.parent; - if (container.kind === SyntaxKind.ModuleDeclaration && !isAmbientModule(container)) { - if (node.isExportEquals) { - error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_namespace); - } - else { - error(node, Diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module); - } - - return; - } - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node) && hasModifiers(node)) { - grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers); - } - if (node.expression.kind === SyntaxKind.Identifier) { - const id = node.expression as Identifier; - const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node); - if (sym) { - markAliasReferenced(sym, id); - // If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`) - const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym; - if (target === unknownSymbol || target.flags & SymbolFlags.Value) { - // However if it is a value, we need to check it's being used correctly - checkExpressionCached(node.expression); - } - } - - if (getEmitDeclarations(compilerOptions)) { - collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true); - } - } - else { - checkExpressionCached(node.expression); - } - - checkExternalModuleExports(container); - - if ((node.flags & NodeFlags.Ambient) && !isEntityNameExpression(node.expression)) { - grammarErrorOnNode(node.expression, Diagnostics.The_expression_of_an_export_assignment_must_be_an_identifier_or_qualified_name_in_an_ambient_context); - } - - if (node.isExportEquals && !(node.flags & NodeFlags.Ambient)) { - if (moduleKind >= ModuleKind.ES2015) { - // export assignment is not supported in es6 modules - grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead); - } - else if (moduleKind === ModuleKind.System) { - // system modules does not support export assignment - grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system); - } - } - } - - function hasExportedMembers(moduleSymbol: Symbol) { - return forEachEntry(moduleSymbol.exports!, (_, id) => id !== "export="); - } - - function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { - const moduleSymbol = getSymbolOfNode(node); - const links = getSymbolLinks(moduleSymbol); - if (!links.exportsChecked) { - const exportEqualsSymbol = moduleSymbol.exports!.get("export=" as __String); - if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) { - const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration; - if (!isTopLevelInExternalModuleAugmentation(declaration) && !isInJSFile(declaration)) { - error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements); - } - } - // Checks for export * conflicts - const exports = getExportsOfModule(moduleSymbol); - if (exports) { - exports.forEach(({ declarations, flags }, id) => { - if (id === "__export") { - return; - } - // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. - // (TS Exceptions: namespaces, function overloads, enums, and interfaces) - if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) { - return; - } - const exportedDeclarationsCount = countWhere(declarations, isNotOverloadAndNotAccessor); - if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) { - // it is legal to merge type alias with other values - // so count should be either 1 (just type alias) or 2 (type alias + merged value) - return; - } - if (exportedDeclarationsCount > 1) { - for (const declaration of declarations) { - if (isNotOverload(declaration)) { - diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id))); - } - } - } - }); - } - links.exportsChecked = true; - } - } - - function checkSourceElement(node: Node | undefined): void { - if (node) { - const saveCurrentNode = currentNode; - currentNode = node; - instantiationCount = 0; - checkSourceElementWorker(node); - currentNode = saveCurrentNode; - } - } - - function checkSourceElementWorker(node: Node): void { - if (isInJSFile(node)) { - forEach((node as JSDocContainer).jsDoc, ({ tags }) => forEach(tags, checkSourceElement)); - } - - const kind = node.kind; - if (cancellationToken) { - // Only bother checking on a few construct kinds. We don't want to be excessively - // hitting the cancellation token on every node we check. - switch (kind) { - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.FunctionDeclaration: - cancellationToken.throwIfCancellationRequested(); - } - } - if (kind >= SyntaxKind.FirstStatement && kind <= SyntaxKind.LastStatement && node.flowNode && !isReachableFlowNode(node.flowNode)) { - errorOrSuggestion(compilerOptions.allowUnreachableCode === false, node, Diagnostics.Unreachable_code_detected); - } - - switch (kind) { - case SyntaxKind.TypeParameter: - return checkTypeParameter(node); - case SyntaxKind.Parameter: - return checkParameter(node); - case SyntaxKind.PropertyDeclaration: - return checkPropertyDeclaration(node); - case SyntaxKind.PropertySignature: - return checkPropertySignature(node); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - return checkSignatureDeclaration(node); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - return checkMethodDeclaration(node); - case SyntaxKind.Constructor: - return checkConstructorDeclaration(node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return checkAccessorDeclaration(node); - case SyntaxKind.TypeReference: - return checkTypeReferenceNode(node); - case SyntaxKind.TypePredicate: - return checkTypePredicate(node); - case SyntaxKind.TypeQuery: - return checkTypeQuery(node); - case SyntaxKind.TypeLiteral: - return checkTypeLiteral(node); - case SyntaxKind.ArrayType: - return checkArrayType(node); - case SyntaxKind.TupleType: - return checkTupleType(node); - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - return checkUnionOrIntersectionType(node); - case SyntaxKind.ParenthesizedType: - case SyntaxKind.OptionalType: - case SyntaxKind.RestType: - return checkSourceElement((node).type); - case SyntaxKind.ThisType: - return checkThisType(node); - case SyntaxKind.TypeOperator: - return checkTypeOperator(node); - case SyntaxKind.ConditionalType: - return checkConditionalType(node); - case SyntaxKind.InferType: - return checkInferType(node); - case SyntaxKind.ImportType: - return checkImportType(node); - case SyntaxKind.JSDocAugmentsTag: - return checkJSDocAugmentsTag(node as JSDocAugmentsTag); - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - return checkJSDocTypeAliasTag(node as JSDocTypedefTag); - case SyntaxKind.JSDocTemplateTag: - return checkJSDocTemplateTag(node as JSDocTemplateTag); - case SyntaxKind.JSDocTypeTag: - return checkJSDocTypeTag(node as JSDocTypeTag); - case SyntaxKind.JSDocParameterTag: - return checkJSDocParameterTag(node as JSDocParameterTag); - case SyntaxKind.JSDocFunctionType: - checkJSDocFunctionType(node as JSDocFunctionType); - // falls through - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocTypeLiteral: - checkJSDocTypeIsInJsFile(node); - forEachChild(node, checkSourceElement); - return; - case SyntaxKind.JSDocVariadicType: - checkJSDocVariadicType(node as JSDocVariadicType); - return; - case SyntaxKind.JSDocTypeExpression: - return checkSourceElement((node as JSDocTypeExpression).type); - case SyntaxKind.IndexedAccessType: - return checkIndexedAccessType(node); - case SyntaxKind.MappedType: - return checkMappedType(node); - case SyntaxKind.FunctionDeclaration: - return checkFunctionDeclaration(node); - case SyntaxKind.Block: - case SyntaxKind.ModuleBlock: - return checkBlock(node); - case SyntaxKind.VariableStatement: - return checkVariableStatement(node); - case SyntaxKind.ExpressionStatement: - return checkExpressionStatement(node); - case SyntaxKind.IfStatement: - return checkIfStatement(node); - case SyntaxKind.DoStatement: - return checkDoStatement(node); - case SyntaxKind.WhileStatement: - return checkWhileStatement(node); - case SyntaxKind.ForStatement: - return checkForStatement(node); - case SyntaxKind.ForInStatement: - return checkForInStatement(node); - case SyntaxKind.ForOfStatement: - return checkForOfStatement(node); - case SyntaxKind.ContinueStatement: - case SyntaxKind.BreakStatement: - return checkBreakOrContinueStatement(node); - case SyntaxKind.ReturnStatement: - return checkReturnStatement(node); - case SyntaxKind.WithStatement: - return checkWithStatement(node); - case SyntaxKind.SwitchStatement: - return checkSwitchStatement(node); - case SyntaxKind.LabeledStatement: - return checkLabeledStatement(node); - case SyntaxKind.ThrowStatement: - return checkThrowStatement(node); - case SyntaxKind.TryStatement: - return checkTryStatement(node); - case SyntaxKind.VariableDeclaration: - return checkVariableDeclaration(node); - case SyntaxKind.BindingElement: - return checkBindingElement(node); - case SyntaxKind.ClassDeclaration: - return checkClassDeclaration(node); - case SyntaxKind.InterfaceDeclaration: - return checkInterfaceDeclaration(node); - case SyntaxKind.TypeAliasDeclaration: - return checkTypeAliasDeclaration(node); - case SyntaxKind.EnumDeclaration: - return checkEnumDeclaration(node); - case SyntaxKind.ModuleDeclaration: - return checkModuleDeclaration(node); - case SyntaxKind.ImportDeclaration: - return checkImportDeclaration(node); - case SyntaxKind.ImportEqualsDeclaration: - return checkImportEqualsDeclaration(node); - case SyntaxKind.ExportDeclaration: - return checkExportDeclaration(node); - case SyntaxKind.ExportAssignment: - return checkExportAssignment(node); - case SyntaxKind.EmptyStatement: - case SyntaxKind.DebuggerStatement: - checkGrammarStatementInAmbientContext(node); - return; - case SyntaxKind.MissingDeclaration: - return checkMissingDeclaration(node); - } - } - - function checkJSDocTypeIsInJsFile(node: Node): void { - if (!isInJSFile(node)) { - grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); - } - } - - function checkJSDocVariadicType(node: JSDocVariadicType): void { - checkJSDocTypeIsInJsFile(node); - checkSourceElement(node.type); - - // Only legal location is in the *last* parameter tag or last parameter of a JSDoc function. - const { parent } = node; - if (isParameter(parent) && isJSDocFunctionType(parent.parent)) { - if (last(parent.parent.parameters) !== parent) { - error(node, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list); - } - return; - } - - if (!isJSDocTypeExpression(parent)) { - error(node, Diagnostics.JSDoc_may_only_appear_in_the_last_parameter_of_a_signature); - } - - const paramTag = node.parent.parent; - if (!isJSDocParameterTag(paramTag)) { - error(node, Diagnostics.JSDoc_may_only_appear_in_the_last_parameter_of_a_signature); - return; - } - - const param = getParameterSymbolFromJSDoc(paramTag); - if (!param) { - // We will error in `checkJSDocParameterTag`. - return; - } - - const host = getHostSignatureFromJSDoc(paramTag); - if (!host || last(host.parameters).symbol !== param) { - error(node, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list); - } - } - - function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { - const type = getTypeFromTypeNode(node.type); - const { parent } = node; - const paramTag = node.parent.parent; - if (isJSDocTypeExpression(node.parent) && isJSDocParameterTag(paramTag)) { - // Else we will add a diagnostic, see `checkJSDocVariadicType`. - const host = getHostSignatureFromJSDoc(paramTag); - if (host) { - /* - Only return an array type if the corresponding parameter is marked as a rest parameter, or if there are no parameters. - So in the following situation we will not create an array type: - /** @param {...number} a * / - function f(a) {} - Because `a` will just be of type `number | undefined`. A synthetic `...args` will also be added, which *will* get an array type. - */ - const lastParamDeclaration = lastOrUndefined(host.parameters); - const symbol = getParameterSymbolFromJSDoc(paramTag); - if (!lastParamDeclaration || - symbol && lastParamDeclaration.symbol === symbol && isRestParameter(lastParamDeclaration)) { - return createArrayType(type); - } - } - } - if (isParameter(parent) && isJSDocFunctionType(parent.parent)) { - return createArrayType(type); - } - return addOptionality(type); - } - - // Function and class expression bodies are checked after all statements in the enclosing body. This is - // to ensure constructs like the following are permitted: - // const foo = function () { - // const s = foo(); - // return "hello"; - // } - // Here, performing a full type check of the body of the function expression whilst in the process of - // determining the type of foo would cause foo to be given type any because of the recursive reference. - // Delaying the type check of the body ensures foo has been assigned a type. - function checkNodeDeferred(node: Node) { - const enclosingFile = getSourceFileOfNode(node); - const links = getNodeLinks(enclosingFile); - if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes = links.deferredNodes || createMap(); - const id = "" + getNodeId(node); - links.deferredNodes.set(id, node); - } - } - - function checkDeferredNodes(context: SourceFile) { - const links = getNodeLinks(context); - if (links.deferredNodes) { - links.deferredNodes.forEach(checkDeferredNode); - } - } - - function checkDeferredNode(node: Node) { - const saveCurrentNode = currentNode; - currentNode = node; - instantiationCount = 0; - switch (node.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - checkFunctionExpressionOrObjectLiteralMethodDeferred(node); - break; - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - checkAccessorDeclaration(node); - break; - case SyntaxKind.ClassExpression: - checkClassExpressionDeferred(node); - break; - case SyntaxKind.JsxSelfClosingElement: - checkJsxSelfClosingElementDeferred(node); - break; - case SyntaxKind.JsxElement: - checkJsxElementDeferred(node); - break; - } - currentNode = saveCurrentNode; - } - - function checkSourceFile(node: SourceFile) { - performance.mark("beforeCheck"); - checkSourceFileWorker(node); - performance.mark("afterCheck"); - performance.measure("Check", "beforeCheck", "afterCheck"); - } - - function unusedIsError(kind: UnusedKind, isAmbient: boolean): boolean { - if (isAmbient) { - return false; - } - switch (kind) { - case UnusedKind.Local: - return !!compilerOptions.noUnusedLocals; - case UnusedKind.Parameter: - return !!compilerOptions.noUnusedParameters; - default: - return Debug.assertNever(kind); - } - } - - function getPotentiallyUnusedIdentifiers(sourceFile: SourceFile): readonly PotentiallyUnusedIdentifier[] { - return allPotentiallyUnusedIdentifiers.get(sourceFile.path) || emptyArray; - } - - // Fully type check a source file and collect the relevant diagnostics. - function checkSourceFileWorker(node: SourceFile) { - const links = getNodeLinks(node); - if (!(links.flags & NodeCheckFlags.TypeChecked)) { - if (skipTypeChecking(node, compilerOptions, host)) { - return; - } - - // Grammar checking - checkGrammarSourceFile(node); - - clear(potentialThisCollisions); - clear(potentialNewTargetCollisions); - clear(potentialWeakMapCollisions); - - forEach(node.statements, checkSourceElement); - checkSourceElement(node.endOfFileToken); - - checkDeferredNodes(node); - - if (isExternalOrCommonJsModule(node)) { - registerForUnusedIdentifiersCheck(node); - } - - if (!node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters)) { - checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(node), (containingNode, kind, diag) => { - if (!containsParseError(containingNode) && unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { - diagnostics.add(diag); - } - }); - } - - if (!node.isDeclarationFile && isExternalModule(node)) { - checkImportsForTypeOnlyConversion(node); - } - - if (isExternalOrCommonJsModule(node)) { - checkExternalModuleExports(node); - } - - if (potentialThisCollisions.length) { - forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope); - clear(potentialThisCollisions); - } - - if (potentialNewTargetCollisions.length) { - forEach(potentialNewTargetCollisions, checkIfNewTargetIsCapturedInEnclosingScope); - clear(potentialNewTargetCollisions); - } - - if (potentialWeakMapCollisions.length) { - forEach(potentialWeakMapCollisions, checkWeakMapCollision); - clear(potentialWeakMapCollisions); - } - - links.flags |= NodeCheckFlags.TypeChecked; - } - } - - function getDiagnostics(sourceFile: SourceFile, ct: CancellationToken): Diagnostic[] { - try { - // Record the cancellation token so it can be checked later on during checkSourceElement. - // Do this in a finally block so we can ensure that it gets reset back to nothing after - // this call is done. - cancellationToken = ct; - return getDiagnosticsWorker(sourceFile); - } - finally { - cancellationToken = undefined; - } - } - - function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { - throwIfNonDiagnosticsProducing(); - if (sourceFile) { - // Some global diagnostics are deferred until they are needed and - // may not be reported in the first call to getGlobalDiagnostics. - // We should catch these changes and report them. - const previousGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); - const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length; - - checkSourceFile(sourceFile); - - const semanticDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName); - const currentGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); - if (currentGlobalDiagnostics !== previousGlobalDiagnostics) { - // If the arrays are not the same reference, new diagnostics were added. - const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics, compareDiagnostics); - return concatenate(deferredGlobalDiagnostics, semanticDiagnostics); - } - else if (previousGlobalDiagnosticsSize === 0 && currentGlobalDiagnostics.length > 0) { - // If the arrays are the same reference, but the length has changed, a single - // new diagnostic was added as DiagnosticCollection attempts to reuse the - // same array. - return concatenate(currentGlobalDiagnostics, semanticDiagnostics); - } - - return semanticDiagnostics; - } - - // Global diagnostics are always added when a file is not provided to - // getDiagnostics - forEach(host.getSourceFiles(), checkSourceFile); - return diagnostics.getDiagnostics(); - } - - function getGlobalDiagnostics(): Diagnostic[] { - throwIfNonDiagnosticsProducing(); - return diagnostics.getGlobalDiagnostics(); - } - - function throwIfNonDiagnosticsProducing() { - if (!produceDiagnostics) { - throw new Error("Trying to get diagnostics from a type checker that does not produce them."); - } - } - - // Language service support - - function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { - if (location.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return []; - } - - const symbols = createSymbolTable(); - let isStatic = false; - - populateSymbols(); - - symbols.delete(InternalSymbolName.This); // Not a symbol, a keyword - return symbolsToArray(symbols); - - function populateSymbols() { - while (location) { - if (location.locals && !isGlobalSourceFile(location)) { - copySymbols(location.locals, meaning); - } - - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule(location)) break; - // falls through - case SyntaxKind.ModuleDeclaration: - copySymbols(getSymbolOfNode(location as ModuleDeclaration | SourceFile).exports!, meaning & SymbolFlags.ModuleMember); - break; - case SyntaxKind.EnumDeclaration: - copySymbols(getSymbolOfNode(location as EnumDeclaration).exports!, meaning & SymbolFlags.EnumMember); - break; - case SyntaxKind.ClassExpression: - const className = (location as ClassExpression).name; - if (className) { - copySymbol(location.symbol, meaning); - } - - // this fall-through is necessary because we would like to handle - // type parameter inside class expression similar to how we handle it in classDeclaration and interface Declaration. - // falls through - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - // If we didn't come from static member of class or interface, - // add the type parameters into the symbol table - // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. - // Note: that the memberFlags come from previous iteration. - if (!isStatic) { - copySymbols(getMembersOfSymbol(getSymbolOfNode(location as ClassDeclaration | InterfaceDeclaration)), meaning & SymbolFlags.Type); - } - break; - case SyntaxKind.FunctionExpression: - const funcName = (location as FunctionExpression).name; - if (funcName) { - copySymbol(location.symbol, meaning); - } - break; - } - - if (introducesArgumentsExoticObject(location)) { - copySymbol(argumentsSymbol, meaning); - } - - isStatic = hasModifier(location, ModifierFlags.Static); - location = location.parent; - } - - copySymbols(globals, meaning); - } - - /** - * Copy the given symbol into symbol tables if the symbol has the given meaning - * and it doesn't already existed in the symbol table - * @param key a key for storing in symbol table; if undefined, use symbol.name - * @param symbol the symbol to be added into symbol table - * @param meaning meaning of symbol to filter by before adding to symbol table - */ - function copySymbol(symbol: Symbol, meaning: SymbolFlags): void { - if (getCombinedLocalAndExportSymbolFlags(symbol) & meaning) { - const id = symbol.escapedName; - // We will copy all symbol regardless of its reserved name because - // symbolsToArray will check whether the key is a reserved name and - // it will not copy symbol with reserved name to the array - if (!symbols.has(id)) { - symbols.set(id, symbol); - } - } - } - - function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { - if (meaning) { - source.forEach(symbol => { - copySymbol(symbol, meaning); - }); - } - } - } - - function isTypeDeclarationName(name: Node): boolean { - return name.kind === SyntaxKind.Identifier && - isTypeDeclaration(name.parent) && - name.parent.name === name; - } - - function isTypeDeclaration(node: Node): node is TypeParameterDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration | ImportClause | ImportSpecifier | ExportSpecifier { - switch (node.kind) { - case SyntaxKind.TypeParameter: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.EnumDeclaration: - return true; - case SyntaxKind.ImportClause: - return (node as ImportClause).isTypeOnly; - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; - default: - return false; - } - } - - // True if the given identifier is part of a type reference - function isTypeReferenceIdentifier(node: EntityName): boolean { - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent as QualifiedName; - } - - return node.parent.kind === SyntaxKind.TypeReference; - } - - function isHeritageClauseElementIdentifier(node: Node): boolean { - while (node.parent.kind === SyntaxKind.PropertyAccessExpression) { - node = node.parent; - } - - return node.parent.kind === SyntaxKind.ExpressionWithTypeArguments; - } - - function forEachEnclosingClass(node: Node, callback: (node: Node) => T | undefined): T | undefined { - let result: T | undefined; - - while (true) { - node = getContainingClass(node)!; - if (!node) break; - if (result = callback(node)) break; - } - - return result; - } - - function isNodeUsedDuringClassInitialization(node: Node) { - return !!findAncestor(node, element => { - if (isConstructorDeclaration(element) && nodeIsPresent(element.body) || isPropertyDeclaration(element)) { - return true; - } - else if (isClassLike(element) || isFunctionLikeDeclaration(element)) { - return "quit"; - } - - return false; - }); - } - - function isNodeWithinClass(node: Node, classDeclaration: ClassLikeDeclaration) { - return !!forEachEnclosingClass(node, n => n === classDeclaration); - } - - function getLeftSideOfImportEqualsOrExportAssignment(nodeOnRightSide: EntityName): ImportEqualsDeclaration | ExportAssignment | undefined { - while (nodeOnRightSide.parent.kind === SyntaxKind.QualifiedName) { - nodeOnRightSide = nodeOnRightSide.parent; - } - - if (nodeOnRightSide.parent.kind === SyntaxKind.ImportEqualsDeclaration) { - return (nodeOnRightSide.parent).moduleReference === nodeOnRightSide ? nodeOnRightSide.parent : undefined; - } - - if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) { - return (nodeOnRightSide.parent).expression === nodeOnRightSide ? nodeOnRightSide.parent : undefined; - } - - return undefined; - } - - function isInRightSideOfImportOrExportAssignment(node: EntityName) { - return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined; - } - - function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) { - const specialPropertyAssignmentKind = getAssignmentDeclarationKind(entityName.parent.parent as BinaryExpression); - switch (specialPropertyAssignmentKind) { - case AssignmentDeclarationKind.ExportsProperty: - case AssignmentDeclarationKind.PrototypeProperty: - return getSymbolOfNode(entityName.parent); - case AssignmentDeclarationKind.ThisProperty: - case AssignmentDeclarationKind.ModuleExports: - case AssignmentDeclarationKind.Property: - return getSymbolOfNode(entityName.parent.parent); - } - } - - function isImportTypeQualifierPart(node: EntityName): ImportTypeNode | undefined { - let parent = node.parent; - while (isQualifiedName(parent)) { - node = parent; - parent = parent.parent; - } - if (parent && parent.kind === SyntaxKind.ImportType && (parent as ImportTypeNode).qualifier === node) { - return parent as ImportTypeNode; - } - return undefined; - } - - function getSymbolOfNameOrPropertyAccessExpression(name: EntityName | PrivateIdentifier | PropertyAccessExpression): Symbol | undefined { - if (isDeclarationName(name)) { - return getSymbolOfNode(name.parent); - } - - if (isInJSFile(name) && - name.parent.kind === SyntaxKind.PropertyAccessExpression && - name.parent === (name.parent.parent as BinaryExpression).left) { - // Check if this is a special property assignment - if (!isPrivateIdentifier(name)) { - const specialPropertyAssignmentSymbol = getSpecialPropertyAssignmentSymbolFromEntityName(name); - if (specialPropertyAssignmentSymbol) { - return specialPropertyAssignmentSymbol; - } - } - } - - if (name.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(name)) { - // Even an entity name expression that doesn't resolve as an entityname may still typecheck as a property access expression - const success = resolveEntityName(name, - /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*ignoreErrors*/ true); - if (success && success !== unknownSymbol) { - return success; - } - } - else if (!isPropertyAccessExpression(name) && !isPrivateIdentifier(name) && isInRightSideOfImportOrExportAssignment(name)) { - // Since we already checked for ExportAssignment, this really could only be an Import - const importEqualsDeclaration = getAncestor(name, SyntaxKind.ImportEqualsDeclaration); - Debug.assert(importEqualsDeclaration !== undefined); - return getSymbolOfPartOfRightHandSideOfImportEquals(name, /*dontResolveAlias*/ true); - } - - if (!isPropertyAccessExpression(name) && !isPrivateIdentifier(name)) { - const possibleImportNode = isImportTypeQualifierPart(name); - if (possibleImportNode) { - getTypeFromTypeNode(possibleImportNode); - const sym = getNodeLinks(name).resolvedSymbol; - return sym === unknownSymbol ? undefined : sym; - } - } - - while (isRightSideOfQualifiedNameOrPropertyAccess(name)) { - name = name.parent; - } - - if (isHeritageClauseElementIdentifier(name)) { - let meaning = SymbolFlags.None; - // In an interface or class, we're definitely interested in a type. - if (name.parent.kind === SyntaxKind.ExpressionWithTypeArguments) { - meaning = SymbolFlags.Type; - - // In a class 'extends' clause we are also looking for a value. - if (isExpressionWithTypeArgumentsInClassExtendsClause(name.parent)) { - meaning |= SymbolFlags.Value; - } - } - else { - meaning = SymbolFlags.Namespace; - } - - meaning |= SymbolFlags.Alias; - const entityNameSymbol = isEntityNameExpression(name) ? resolveEntityName(name, meaning) : undefined; - if (entityNameSymbol) { - return entityNameSymbol; - } - } - - if (name.parent.kind === SyntaxKind.JSDocParameterTag) { - return getParameterSymbolFromJSDoc(name.parent as JSDocParameterTag); - } - - if (name.parent.kind === SyntaxKind.TypeParameter && name.parent.parent.kind === SyntaxKind.JSDocTemplateTag) { - Debug.assert(!isInJSFile(name)); // Otherwise `isDeclarationName` would have been true. - const typeParameter = getTypeParameterFromJsDoc(name.parent as TypeParameterDeclaration & { parent: JSDocTemplateTag }); - return typeParameter && typeParameter.symbol; - } - - if (isExpressionNode(name)) { - if (nodeIsMissing(name)) { - // Missing entity name. - return undefined; - } - - if (name.kind === SyntaxKind.Identifier) { - if (isJSXTagName(name) && isJsxIntrinsicIdentifier(name)) { - const symbol = getIntrinsicTagSymbol(name.parent); - return symbol === unknownSymbol ? undefined : symbol; - } - - return resolveEntityName(name, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); - } - else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) { - const links = getNodeLinks(name); - if (links.resolvedSymbol) { - return links.resolvedSymbol; - } - - if (name.kind === SyntaxKind.PropertyAccessExpression) { - checkPropertyAccessExpression(name); - } - else { - checkQualifiedName(name); - } - return links.resolvedSymbol; - } - } - else if (isTypeReferenceIdentifier(name)) { - const meaning = name.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; - return resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); - } - - if (name.parent.kind === SyntaxKind.TypePredicate) { - return resolveEntityName(name, /*meaning*/ SymbolFlags.FunctionScopedVariable); - } - - // Do we want to return undefined here? - return undefined; - } - - function getSymbolAtLocation(node: Node): Symbol | undefined { - if (node.kind === SyntaxKind.SourceFile) { - return isExternalModule(node) ? getMergedSymbol(node.symbol) : undefined; - } - const { parent } = node; - const grandParent = parent.parent; - - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return undefined; - } - - if (isDeclarationNameOrImportPropertyName(node)) { - // This is a declaration, call getSymbolOfNode - const parentSymbol = getSymbolOfNode(parent)!; - return isImportOrExportSpecifier(node.parent) && node.parent.propertyName === node - ? getImmediateAliasedSymbol(parentSymbol) - : parentSymbol; - } - else if (isLiteralComputedPropertyDeclarationName(node)) { - return getSymbolOfNode(parent.parent); - } - - if (node.kind === SyntaxKind.Identifier) { - if (isInRightSideOfImportOrExportAssignment(node)) { - return getSymbolOfNameOrPropertyAccessExpression(node); - } - else if (parent.kind === SyntaxKind.BindingElement && - grandParent.kind === SyntaxKind.ObjectBindingPattern && - node === (parent).propertyName) { - const typeOfPattern = getTypeOfNode(grandParent); - const propertyDeclaration = getPropertyOfType(typeOfPattern, (node).escapedText); - - if (propertyDeclaration) { - return propertyDeclaration; - } - } - } - - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PrivateIdentifier: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.QualifiedName: - return getSymbolOfNameOrPropertyAccessExpression(node); - - case SyntaxKind.ThisKeyword: - const container = getThisContainer(node, /*includeArrowFunctions*/ false); - if (isFunctionLike(container)) { - const sig = getSignatureFromDeclaration(container); - if (sig.thisParameter) { - return sig.thisParameter; - } - } - if (isInExpressionContext(node)) { - return checkExpression(node as Expression).symbol; - } - // falls through - - case SyntaxKind.ThisType: - return getTypeFromThisTypeNode(node as ThisExpression | ThisTypeNode).symbol; - - case SyntaxKind.SuperKeyword: - return checkExpression(node as Expression).symbol; - - case SyntaxKind.ConstructorKeyword: - // constructor keyword for an overload, should take us to the definition if it exist - const constructorDeclaration = node.parent; - if (constructorDeclaration && constructorDeclaration.kind === SyntaxKind.Constructor) { - return (constructorDeclaration.parent).symbol; - } - return undefined; - - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - // 1). import x = require("./mo/*gotToDefinitionHere*/d") - // 2). External module name in an import declaration - // 3). Dynamic import call or require in javascript - // 4). type A = import("./f/*gotToDefinitionHere*/oo") - if ((isExternalModuleImportEqualsDeclaration(node.parent.parent) && getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) || - ((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) && (node.parent).moduleSpecifier === node) || - ((isInJSFile(node) && isRequireCall(node.parent, /*checkArgumentIsStringLiteralLike*/ false)) || isImportCall(node.parent)) || - (isLiteralTypeNode(node.parent) && isLiteralImportTypeNode(node.parent.parent) && node.parent.parent.argument === node.parent) - ) { - return resolveExternalModuleName(node, node); - } - if (isCallExpression(parent) && isBindableObjectDefinePropertyCall(parent) && parent.arguments[1] === node) { - return getSymbolOfNode(parent); - } - // falls through - - case SyntaxKind.NumericLiteral: - // index access - const objectType = isElementAccessExpression(parent) - ? parent.argumentExpression === node ? getTypeOfExpression(parent.expression) : undefined - : isLiteralTypeNode(parent) && isIndexedAccessTypeNode(grandParent) - ? getTypeFromTypeNode(grandParent.objectType) - : undefined; - return objectType && getPropertyOfType(objectType, escapeLeadingUnderscores((node as StringLiteral | NumericLiteral).text)); - - case SyntaxKind.DefaultKeyword: - case SyntaxKind.FunctionKeyword: - case SyntaxKind.EqualsGreaterThanToken: - case SyntaxKind.ClassKeyword: - return getSymbolOfNode(node.parent); - case SyntaxKind.ImportType: - return isLiteralImportTypeNode(node) ? getSymbolAtLocation(node.argument.literal) : undefined; - - case SyntaxKind.ExportKeyword: - return isExportAssignment(node.parent) ? Debug.assertDefined(node.parent.symbol) : undefined; - - default: - return undefined; - } - } - - function getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined { - if (location && location.kind === SyntaxKind.ShorthandPropertyAssignment) { - return resolveEntityName((location).name, SymbolFlags.Value | SymbolFlags.Alias); - } - return undefined; - } - - /** Returns the target of an export specifier without following aliases */ - function getExportSpecifierLocalTargetSymbol(node: ExportSpecifier): Symbol | undefined { - return node.parent.parent.moduleSpecifier ? - getExternalModuleMember(node.parent.parent, node) : - resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); - } - - function getTypeOfNode(node: Node): Type { - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return errorType; - } - - const classDecl = tryGetClassImplementingOrExtendingExpressionWithTypeArguments(node); - const classType = classDecl && getDeclaredTypeOfClassOrInterface(getSymbolOfNode(classDecl.class)); - if (isPartOfTypeNode(node)) { - const typeFromTypeNode = getTypeFromTypeNode(node); - return classType ? getTypeWithThisArgument(typeFromTypeNode, classType.thisType) : typeFromTypeNode; - } - - if (isExpressionNode(node)) { - return getRegularTypeOfExpression(node); - } - - if (classType && !classDecl!.isImplements) { - // A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the - // extends clause of a class. We handle that case here. - const baseType = firstOrUndefined(getBaseTypes(classType)); - return baseType ? getTypeWithThisArgument(baseType, classType.thisType) : errorType; - } - - if (isTypeDeclaration(node)) { - // In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration - const symbol = getSymbolOfNode(node); - return getDeclaredTypeOfSymbol(symbol); - } - - if (isTypeDeclarationName(node)) { - const symbol = getSymbolAtLocation(node); - return symbol ? getDeclaredTypeOfSymbol(symbol) : errorType; - } - - if (isDeclaration(node)) { - // In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration - const symbol = getSymbolOfNode(node); - return getTypeOfSymbol(symbol); - } - - if (isDeclarationNameOrImportPropertyName(node)) { - const symbol = getSymbolAtLocation(node); - if (symbol) { - return isTypeOnlyImportOrExportName(node) ? getDeclaredTypeOfSymbol(symbol) : getTypeOfSymbol(symbol); - } - return errorType; - } - - if (isBindingPattern(node)) { - return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true) || errorType; - } - - if (isInRightSideOfImportOrExportAssignment(node)) { - const symbol = getSymbolAtLocation(node); - if (symbol) { - const declaredType = getDeclaredTypeOfSymbol(symbol); - return declaredType !== errorType ? declaredType : getTypeOfSymbol(symbol); - } - } - - return errorType; - } - - // Gets the type of object literal or array literal of destructuring assignment. - // { a } from - // for ( { a } of elems) { - // } - // [ a ] from - // [a] = [ some array ...] - function getTypeOfAssignmentPattern(expr: AssignmentPattern): Type | undefined { - Debug.assert(expr.kind === SyntaxKind.ObjectLiteralExpression || expr.kind === SyntaxKind.ArrayLiteralExpression); - // If this is from "for of" - // for ( { a } of elems) { - // } - if (expr.parent.kind === SyntaxKind.ForOfStatement) { - const iteratedType = checkRightHandSideOfForOf((expr.parent).expression, (expr.parent).awaitModifier); - return checkDestructuringAssignment(expr, iteratedType || errorType); - } - // If this is from "for" initializer - // for ({a } = elems[0];.....) { } - if (expr.parent.kind === SyntaxKind.BinaryExpression) { - const iteratedType = getTypeOfExpression((expr.parent).right); - return checkDestructuringAssignment(expr, iteratedType || errorType); - } - // If this is from nested object binding pattern - // for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) { - if (expr.parent.kind === SyntaxKind.PropertyAssignment) { - const node = cast(expr.parent.parent, isObjectLiteralExpression); - const typeOfParentObjectLiteral = getTypeOfAssignmentPattern(node) || errorType; - const propertyIndex = indexOfNode(node.properties, expr.parent); - return checkObjectLiteralDestructuringPropertyAssignment(node, typeOfParentObjectLiteral, propertyIndex); - } - // Array literal assignment - array destructuring pattern - const node = cast(expr.parent, isArrayLiteralExpression); - // [{ property1: p1, property2 }] = elems; - const typeOfArrayLiteral = getTypeOfAssignmentPattern(node) || errorType; - const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, typeOfArrayLiteral, undefinedType, expr.parent) || errorType; - return checkArrayLiteralDestructuringElementAssignment(node, typeOfArrayLiteral, node.elements.indexOf(expr), elementType); - } - - // Gets the property symbol corresponding to the property in destructuring assignment - // 'property1' from - // for ( { property1: a } of elems) { - // } - // 'property1' at location 'a' from: - // [a] = [ property1, property2 ] - function getPropertySymbolOfDestructuringAssignment(location: Identifier) { - // Get the type of the object or array literal and then look for property of given name in the type - const typeOfObjectLiteral = getTypeOfAssignmentPattern(cast(location.parent.parent, isAssignmentPattern)); - return typeOfObjectLiteral && getPropertyOfType(typeOfObjectLiteral, location.escapedText); - } - - function getRegularTypeOfExpression(expr: Expression): Type { - if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) { - expr = expr.parent; - } - return getRegularTypeOfLiteralType(getTypeOfExpression(expr)); - } - - /** - * Gets either the static or instance type of a class element, based on - * whether the element is declared as "static". - */ - function getParentTypeOfClassElement(node: ClassElement) { - const classSymbol = getSymbolOfNode(node.parent)!; - return hasModifier(node, ModifierFlags.Static) - ? getTypeOfSymbol(classSymbol) - : getDeclaredTypeOfSymbol(classSymbol); - } - - function getClassElementPropertyKeyType(element: ClassElement) { - const name = element.name!; - switch (name.kind) { - case SyntaxKind.Identifier: - return getLiteralType(idText(name)); - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - return getLiteralType(name.text); - case SyntaxKind.ComputedPropertyName: - const nameType = checkComputedPropertyName(name); - return isTypeAssignableToKind(nameType, TypeFlags.ESSymbolLike) ? nameType : stringType; - default: - return Debug.fail("Unsupported property name."); - } - } - - // Return the list of properties of the given type, augmented with properties from Function - // if the type has call or construct signatures - function getAugmentedPropertiesOfType(type: Type): Symbol[] { - type = getApparentType(type); - const propsByName = createSymbolTable(getPropertiesOfType(type)); - const functionType = getSignaturesOfType(type, SignatureKind.Call).length ? globalCallableFunctionType : - getSignaturesOfType(type, SignatureKind.Construct).length ? globalNewableFunctionType : - undefined; - if (functionType) { - forEach(getPropertiesOfType(functionType), p => { - if (!propsByName.has(p.escapedName)) { - propsByName.set(p.escapedName, p); - } - }); - } - return getNamedMembers(propsByName); - } - - function typeHasCallOrConstructSignatures(type: Type): boolean { - return ts.typeHasCallOrConstructSignatures(type, checker); - } - - function getRootSymbols(symbol: Symbol): readonly Symbol[] { - const roots = getImmediateRootSymbols(symbol); - return roots ? flatMap(roots, getRootSymbols) : [symbol]; - } - function getImmediateRootSymbols(symbol: Symbol): readonly Symbol[] | undefined { - if (getCheckFlags(symbol) & CheckFlags.Synthetic) { - return mapDefined(getSymbolLinks(symbol).containingType!.types, type => getPropertyOfType(type, symbol.escapedName)); - } - else if (symbol.flags & SymbolFlags.Transient) { - const { leftSpread, rightSpread, syntheticOrigin } = symbol as TransientSymbol; - return leftSpread ? [leftSpread, rightSpread!] - : syntheticOrigin ? [syntheticOrigin] - : singleElementArray(tryGetAliasTarget(symbol)); - } - return undefined; - } - function tryGetAliasTarget(symbol: Symbol): Symbol | undefined { - let target: Symbol | undefined; - let next: Symbol | undefined = symbol; - while (next = getSymbolLinks(next).target) { - target = next; - } - return target; - } - - // Emitter support - - function isArgumentsLocalBinding(nodeIn: Identifier): boolean { - if (!isGeneratedIdentifier(nodeIn)) { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - const isPropertyName = node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).name === node; - return !isPropertyName && getReferencedValueSymbol(node) === argumentsSymbol; - } - } - - return false; - } - - function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean { - let moduleSymbol = resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression); - if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) { - // If the module is not found or is shorthand, assume that it may export a value. - return true; - } - - const hasExportAssignment = hasExportAssignmentSymbol(moduleSymbol); - // if module has export assignment then 'resolveExternalModuleSymbol' will return resolved symbol for export assignment - // otherwise it will return moduleSymbol itself - moduleSymbol = resolveExternalModuleSymbol(moduleSymbol); - - const symbolLinks = getSymbolLinks(moduleSymbol); - if (symbolLinks.exportsSomeValue === undefined) { - // for export assignments - check if resolved symbol for RHS is itself a value - // otherwise - check if at least one export is value - symbolLinks.exportsSomeValue = hasExportAssignment - ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachEntry(getExportsOfModule(moduleSymbol), isValue); - } - - return symbolLinks.exportsSomeValue!; - - function isValue(s: Symbol): boolean { - s = resolveSymbol(s); - return s && !!(s.flags & SymbolFlags.Value); - } - } - - function isNameOfModuleOrEnumDeclaration(node: Identifier) { - return isModuleOrEnumDeclaration(node.parent) && node === node.parent.name; - } - - // When resolved as an expression identifier, if the given node references an exported entity, return the declaration - // node of the exported entity's container. Otherwise, return undefined. - function getReferencedExportContainer(nodeIn: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration | undefined { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - // When resolving the export container for the name of a module or enum - // declaration, we need to start resolution at the declaration's container. - // Otherwise, we could incorrectly resolve the export container as the - // declaration if it contains an exported member with the same name. - let symbol = getReferencedValueSymbol(node, /*startInDeclarationContainer*/ isNameOfModuleOrEnumDeclaration(node)); - if (symbol) { - if (symbol.flags & SymbolFlags.ExportValue) { - // If we reference an exported entity within the same module declaration, then whether - // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the - // kinds that we do NOT prefix. - const exportSymbol = getMergedSymbol(symbol.exportSymbol!); - if (!prefixLocals && exportSymbol.flags & SymbolFlags.ExportHasLocal && !(exportSymbol.flags & SymbolFlags.Variable)) { - return undefined; - } - symbol = exportSymbol; - } - const parentSymbol = getParentOfSymbol(symbol); - if (parentSymbol) { - if (parentSymbol.flags & SymbolFlags.ValueModule && parentSymbol.valueDeclaration.kind === SyntaxKind.SourceFile) { - const symbolFile = parentSymbol.valueDeclaration; - const referenceFile = getSourceFileOfNode(node); - // If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return undefined. - const symbolIsUmdExport = symbolFile !== referenceFile; - return symbolIsUmdExport ? undefined : symbolFile; - } - return findAncestor(node.parent, (n): n is ModuleDeclaration | EnumDeclaration => isModuleOrEnumDeclaration(n) && getSymbolOfNode(n) === parentSymbol); - } - } - } - } - - // When resolved as an expression identifier, if the given node references an import, return the declaration of - // that import. Otherwise, return undefined. - function getReferencedImportDeclaration(nodeIn: Identifier): Declaration | undefined { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - const symbol = getReferencedValueSymbol(node); - // We should only get the declaration of an alias if there isn't a local value - // declaration for the symbol - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value)) { - return getDeclarationOfAliasSymbol(symbol); - } - } - - return undefined; - } - - function isSymbolOfDestructuredElementOfCatchBinding(symbol: Symbol) { - return isBindingElement(symbol.valueDeclaration) - && walkUpBindingElementsAndPatterns(symbol.valueDeclaration).parent.kind === SyntaxKind.CatchClause; - } - - function isSymbolOfDeclarationWithCollidingName(symbol: Symbol): boolean { - if (symbol.flags & SymbolFlags.BlockScoped && !isSourceFile(symbol.valueDeclaration)) { - const links = getSymbolLinks(symbol); - if (links.isDeclarationWithCollidingName === undefined) { - const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); - if (isStatementWithLocals(container) || isSymbolOfDestructuredElementOfCatchBinding(symbol)) { - const nodeLinks = getNodeLinks(symbol.valueDeclaration); - if (resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false)) { - // redeclaration - always should be renamed - links.isDeclarationWithCollidingName = true; - } - else if (nodeLinks.flags & NodeCheckFlags.CapturedBlockScopedBinding) { - // binding is captured in the function - // should be renamed if: - // - binding is not top level - top level bindings never collide with anything - // AND - // - binding is not declared in loop, should be renamed to avoid name reuse across siblings - // let a, b - // { let x = 1; a = () => x; } - // { let x = 100; b = () => x; } - // console.log(a()); // should print '1' - // console.log(b()); // should print '100' - // OR - // - binding is declared inside loop but not in inside initializer of iteration statement or directly inside loop body - // * variables from initializer are passed to rewritten loop body as parameters so they are not captured directly - // * variables that are declared immediately in loop body will become top level variable after loop is rewritten and thus - // they will not collide with anything - const isDeclaredInLoop = nodeLinks.flags & NodeCheckFlags.BlockScopedBindingInLoop; - const inLoopInitializer = isIterationStatement(container, /*lookInLabeledStatements*/ false); - const inLoopBodyBlock = container.kind === SyntaxKind.Block && isIterationStatement(container.parent, /*lookInLabeledStatements*/ false); - - links.isDeclarationWithCollidingName = !isBlockScopedContainerTopLevel(container) && (!isDeclaredInLoop || (!inLoopInitializer && !inLoopBodyBlock)); - } - else { - links.isDeclarationWithCollidingName = false; - } - } - } - return links.isDeclarationWithCollidingName!; - } - return false; - } - - // When resolved as an expression identifier, if the given node references a nested block scoped entity with - // a name that either hides an existing name or might hide it when compiled downlevel, - // return the declaration of that entity. Otherwise, return undefined. - function getReferencedDeclarationWithCollidingName(nodeIn: Identifier): Declaration | undefined { - if (!isGeneratedIdentifier(nodeIn)) { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - const symbol = getReferencedValueSymbol(node); - if (symbol && isSymbolOfDeclarationWithCollidingName(symbol)) { - return symbol.valueDeclaration; - } - } - } - - return undefined; - } - - // Return true if the given node is a declaration of a nested block scoped entity with a name that either hides an - // existing name or might hide a name when compiled downlevel - function isDeclarationWithCollidingName(nodeIn: Declaration): boolean { - const node = getParseTreeNode(nodeIn, isDeclaration); - if (node) { - const symbol = getSymbolOfNode(node); - if (symbol) { - return isSymbolOfDeclarationWithCollidingName(symbol); - } - } - - return false; - } - - function isValueAliasDeclaration(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol); - case SyntaxKind.ExportDeclaration: - const exportClause = (node).exportClause; - return !!exportClause && ( - isNamespaceExport(exportClause) || - some(exportClause.elements, isValueAliasDeclaration) - ); - case SyntaxKind.ExportAssignment: - return (node).expression && (node).expression.kind === SyntaxKind.Identifier ? - isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol) : - true; - } - return false; - } - - function isTopLevelValueImportEqualsWithEntityName(nodeIn: ImportEqualsDeclaration): boolean { - const node = getParseTreeNode(nodeIn, isImportEqualsDeclaration); - if (node === undefined || node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) { - // parent is not source file or it is not reference to internal module - return false; - } - - const isValue = isAliasResolvedToValue(getSymbolOfNode(node)); - return isValue && node.moduleReference && !nodeIsMissing(node.moduleReference); - } - - function isAliasResolvedToValue(symbol: Symbol): boolean { - const target = resolveAlias(symbol); - if (target === unknownSymbol) { - return true; - } - // const enums and modules that contain only const enums are not considered values from the emit perspective - // unless 'preserveConstEnums' option is set to true - return !!(target.flags & SymbolFlags.Value) && - (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target)); - } - - function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean { - return isConstEnumSymbol(s) || !!s.constEnumOnlyModule; - } - - function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { - if (isAliasSymbolDeclaration(node)) { - const symbol = getSymbolOfNode(node); - if (symbol && getSymbolLinks(symbol).referenced) { - return true; - } - const target = getSymbolLinks(symbol!).target; // TODO: GH#18217 - if (target && getModifierFlags(node) & ModifierFlags.Export && - target.flags & SymbolFlags.Value && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target))) { - // An `export import ... =` of a value symbol is always considered referenced - return true; - } - } - - if (checkChildren) { - return !!forEachChild(node, node => isReferencedAliasDeclaration(node, checkChildren)); - } - return false; - } - - function isImplementationOfOverload(node: SignatureDeclaration) { - if (nodeIsPresent((node as FunctionLikeDeclaration).body)) { - if (isGetAccessor(node) || isSetAccessor(node)) return false; // Get or set accessors can never be overload implementations, but can have up to 2 signatures - const symbol = getSymbolOfNode(node); - const signaturesOfSymbol = getSignaturesOfSymbol(symbol); - // If this function body corresponds to function with multiple signature, it is implementation of overload - // e.g.: function foo(a: string): string; - // function foo(a: number): number; - // function foo(a: any) { // This is implementation of the overloads - // return a; - // } - return signaturesOfSymbol.length > 1 || - // If there is single signature for the symbol, it is overload if that signature isn't coming from the node - // e.g.: function foo(a: string): string; - // function foo(a: any) { // This is implementation of the overloads - // return a; - // } - (signaturesOfSymbol.length === 1 && signaturesOfSymbol[0].declaration !== node); - } - return false; - } - - function isRequiredInitializedParameter(parameter: ParameterDeclaration | JSDocParameterTag): boolean { - return !!strictNullChecks && - !isOptionalParameter(parameter) && - !isJSDocParameterTag(parameter) && - !!parameter.initializer && - !hasModifier(parameter, ModifierFlags.ParameterPropertyModifier); - } - - function isOptionalUninitializedParameterProperty(parameter: ParameterDeclaration) { - return strictNullChecks && - isOptionalParameter(parameter) && - !parameter.initializer && - hasModifier(parameter, ModifierFlags.ParameterPropertyModifier); - } - - function isExpandoFunctionDeclaration(node: Declaration): boolean { - const declaration = getParseTreeNode(node, isFunctionDeclaration); - if (!declaration) { - return false; - } - const symbol = getSymbolOfNode(declaration); - if (!symbol || !(symbol.flags & SymbolFlags.Function)) { - return false; - } - return !!forEachEntry(getExportsOfSymbol(symbol), p => p.flags & SymbolFlags.Value && p.valueDeclaration && isPropertyAccessExpression(p.valueDeclaration)); - } - - function getPropertiesOfContainerFunction(node: Declaration): Symbol[] { - const declaration = getParseTreeNode(node, isFunctionDeclaration); - if (!declaration) { - return emptyArray; - } - const symbol = getSymbolOfNode(declaration); - return symbol && getPropertiesOfType(getTypeOfSymbol(symbol)) || emptyArray; - } - - function getNodeCheckFlags(node: Node): NodeCheckFlags { - return getNodeLinks(node).flags || 0; - } - - function getEnumMemberValue(node: EnumMember): string | number | undefined { - computeEnumMemberValues(node.parent); - return getNodeLinks(node).enumMemberValue; - } - - function canHaveConstantValue(node: Node): node is EnumMember | AccessExpression { - switch (node.kind) { - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return true; - } - return false; - } - - function getConstantValue(node: EnumMember | AccessExpression): string | number | undefined { - if (node.kind === SyntaxKind.EnumMember) { - return getEnumMemberValue(node); - } - - const symbol = getNodeLinks(node).resolvedSymbol; - if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { - // inline property\index accesses only for const enums - const member = symbol.valueDeclaration as EnumMember; - if (isEnumConst(member.parent)) { - return getEnumMemberValue(member); - } - } - - return undefined; - } - - function isFunctionType(type: Type): boolean { - return !!(type.flags & TypeFlags.Object) && getSignaturesOfType(type, SignatureKind.Call).length > 0; - } - - function getTypeReferenceSerializationKind(typeNameIn: EntityName, location?: Node): TypeReferenceSerializationKind { - // ensure both `typeName` and `location` are parse tree nodes. - const typeName = getParseTreeNode(typeNameIn, isEntityName); - if (!typeName) return TypeReferenceSerializationKind.Unknown; - - if (location) { - location = getParseTreeNode(location); - if (!location) return TypeReferenceSerializationKind.Unknown; - } - - // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. - const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location); - - // Resolve the symbol as a type so that we can provide a more useful hint for the type serializer. - const typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location); - if (valueSymbol && valueSymbol === typeSymbol) { - const globalPromiseSymbol = getGlobalPromiseConstructorSymbol(/*reportErrors*/ false); - if (globalPromiseSymbol && valueSymbol === globalPromiseSymbol) { - return TypeReferenceSerializationKind.Promise; - } - - const constructorType = getTypeOfSymbol(valueSymbol); - if (constructorType && isConstructorType(constructorType)) { - return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue; - } - } - - // We might not be able to resolve type symbol so use unknown type in that case (eg error case) - if (!typeSymbol) { - return TypeReferenceSerializationKind.Unknown; - } - const type = getDeclaredTypeOfSymbol(typeSymbol); - if (type === errorType) { - return TypeReferenceSerializationKind.Unknown; - } - else if (type.flags & TypeFlags.AnyOrUnknown) { - return TypeReferenceSerializationKind.ObjectType; - } - else if (isTypeAssignableToKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) { - return TypeReferenceSerializationKind.VoidNullableOrNeverType; - } - else if (isTypeAssignableToKind(type, TypeFlags.BooleanLike)) { - return TypeReferenceSerializationKind.BooleanType; - } - else if (isTypeAssignableToKind(type, TypeFlags.NumberLike)) { - return TypeReferenceSerializationKind.NumberLikeType; - } - else if (isTypeAssignableToKind(type, TypeFlags.BigIntLike)) { - return TypeReferenceSerializationKind.BigIntLikeType; - } - else if (isTypeAssignableToKind(type, TypeFlags.StringLike)) { - return TypeReferenceSerializationKind.StringLikeType; - } - else if (isTupleType(type)) { - return TypeReferenceSerializationKind.ArrayLikeType; - } - else if (isTypeAssignableToKind(type, TypeFlags.ESSymbolLike)) { - return TypeReferenceSerializationKind.ESSymbolType; - } - else if (isFunctionType(type)) { - return TypeReferenceSerializationKind.TypeWithCallSignature; - } - else if (isArrayType(type)) { - return TypeReferenceSerializationKind.ArrayLikeType; - } - else { - return TypeReferenceSerializationKind.ObjectType; - } - } - - function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) { - const declaration = getParseTreeNode(declarationIn, isVariableLikeOrAccessor); - if (!declaration) { - return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; - } - // Get type of the symbol if this is the valid symbol otherwise get type at location - const symbol = getSymbolOfNode(declaration); - let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) - ? getWidenedLiteralType(getTypeOfSymbol(symbol)) - : errorType; - if (type.flags & TypeFlags.UniqueESSymbol && - type.symbol === symbol) { - flags |= NodeBuilderFlags.AllowUniqueESSymbolType; - } - if (addUndefined) { - type = getOptionalType(type); - } - return nodeBuilder.typeToTypeNode(type, enclosingDeclaration, flags | NodeBuilderFlags.MultilineObjectLiterals, tracker); - } - - function createReturnTypeOfSignatureDeclaration(signatureDeclarationIn: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker) { - const signatureDeclaration = getParseTreeNode(signatureDeclarationIn, isFunctionLike); - if (!signatureDeclaration) { - return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; - } - const signature = getSignatureFromDeclaration(signatureDeclaration); - return nodeBuilder.typeToTypeNode(getReturnTypeOfSignature(signature), enclosingDeclaration, flags | NodeBuilderFlags.MultilineObjectLiterals, tracker); - } - - function createTypeOfExpression(exprIn: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker) { - const expr = getParseTreeNode(exprIn, isExpression); - if (!expr) { - return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; - } - const type = getWidenedType(getRegularTypeOfExpression(expr)); - return nodeBuilder.typeToTypeNode(type, enclosingDeclaration, flags | NodeBuilderFlags.MultilineObjectLiterals, tracker); - } - - function hasGlobalName(name: string): boolean { - return globals.has(escapeLeadingUnderscores(name)); - } - - function getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol | undefined { - const resolvedSymbol = getNodeLinks(reference).resolvedSymbol; - if (resolvedSymbol) { - return resolvedSymbol; - } - - let location: Node = reference; - if (startInDeclarationContainer) { - // When resolving the name of a declaration as a value, we need to start resolution - // at a point outside of the declaration. - const parent = reference.parent; - if (isDeclaration(parent) && reference === parent.name) { - location = getDeclarationContainer(parent); - } - } - - return resolveName(location, reference.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); - } - - function getReferencedValueDeclaration(referenceIn: Identifier): Declaration | undefined { - if (!isGeneratedIdentifier(referenceIn)) { - const reference = getParseTreeNode(referenceIn, isIdentifier); - if (reference) { - const symbol = getReferencedValueSymbol(reference); - if (symbol) { - return getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; - } - } - } - - return undefined; - } - - function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean { - if (isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConst(node)) { - return isFreshLiteralType(getTypeOfSymbol(getSymbolOfNode(node))); - } - return false; - } - - function literalTypeToNode(type: FreshableType, enclosing: Node, tracker: SymbolTracker): Expression { - const enumResult = type.flags & TypeFlags.EnumLiteral ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, tracker) - : type === trueType ? createTrue() : type === falseType && createFalse(); - return enumResult || createLiteral((type as LiteralType).value); - } - - function createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker) { - const type = getTypeOfSymbol(getSymbolOfNode(node)); - return literalTypeToNode(type, node, tracker); - } - - function createResolver(): EmitResolver { - // this variable and functions that use it are deliberately moved here from the outer scope - // to avoid scope pollution - const resolvedTypeReferenceDirectives = host.getResolvedTypeReferenceDirectives(); - let fileToDirective: Map; - if (resolvedTypeReferenceDirectives) { - // populate reverse mapping: file path -> type reference directive that was resolved to this file - fileToDirective = createMap(); - resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => { - if (!resolvedDirective || !resolvedDirective.resolvedFileName) { - return; - } - const file = host.getSourceFile(resolvedDirective.resolvedFileName)!; - // Add the transitive closure of path references loaded by this file (as long as they are not) - // part of an existing type reference. - addReferencedFilesToTypeDirective(file, key); - }); - } - - return { - getReferencedExportContainer, - getReferencedImportDeclaration, - getReferencedDeclarationWithCollidingName, - isDeclarationWithCollidingName, - isValueAliasDeclaration: node => { - node = getParseTreeNode(node); - // Synthesized nodes are always treated like values. - return node ? isValueAliasDeclaration(node) : true; - }, - hasGlobalName, - isReferencedAliasDeclaration: (node, checkChildren?) => { - node = getParseTreeNode(node); - // Synthesized nodes are always treated as referenced. - return node ? isReferencedAliasDeclaration(node, checkChildren) : true; - }, - getNodeCheckFlags: node => { - node = getParseTreeNode(node); - return node ? getNodeCheckFlags(node) : 0; - }, - isTopLevelValueImportEqualsWithEntityName, - isDeclarationVisible, - isImplementationOfOverload, - isRequiredInitializedParameter, - isOptionalUninitializedParameterProperty, - isExpandoFunctionDeclaration, - getPropertiesOfContainerFunction, - createTypeOfDeclaration, - createReturnTypeOfSignatureDeclaration, - createTypeOfExpression, - createLiteralConstValue, - isSymbolAccessible, - isEntityNameVisible, - getConstantValue: nodeIn => { - const node = getParseTreeNode(nodeIn, canHaveConstantValue); - return node ? getConstantValue(node) : undefined; - }, - collectLinkedAliases, - getReferencedValueDeclaration, - getTypeReferenceSerializationKind, - isOptionalParameter, - moduleExportsSomeValue, - isArgumentsLocalBinding, - getExternalModuleFileFromDeclaration, - getTypeReferenceDirectivesForEntityName, - getTypeReferenceDirectivesForSymbol, - isLiteralConstDeclaration, - isLateBound: (nodeIn: Declaration): nodeIn is LateBoundDeclaration => { - const node = getParseTreeNode(nodeIn, isDeclaration); - const symbol = node && getSymbolOfNode(node); - return !!(symbol && getCheckFlags(symbol) & CheckFlags.Late); - }, - getJsxFactoryEntity: location => location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity, - getAllAccessorDeclarations(accessor: AccessorDeclaration): AllAccessorDeclarations { - accessor = getParseTreeNode(accessor, isGetOrSetAccessorDeclaration)!; // TODO: GH#18217 - const otherKind = accessor.kind === SyntaxKind.SetAccessor ? SyntaxKind.GetAccessor : SyntaxKind.SetAccessor; - const otherAccessor = getDeclarationOfKind(getSymbolOfNode(accessor), otherKind); - const firstAccessor = otherAccessor && (otherAccessor.pos < accessor.pos) ? otherAccessor : accessor; - const secondAccessor = otherAccessor && (otherAccessor.pos < accessor.pos) ? accessor : otherAccessor; - const setAccessor = accessor.kind === SyntaxKind.SetAccessor ? accessor : otherAccessor as SetAccessorDeclaration; - const getAccessor = accessor.kind === SyntaxKind.GetAccessor ? accessor : otherAccessor as GetAccessorDeclaration; - return { - firstAccessor, - secondAccessor, - setAccessor, - getAccessor - }; - }, - getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined), - isBindingCapturedByNode: (node, decl) => { - const parseNode = getParseTreeNode(node); - const parseDecl = getParseTreeNode(decl); - return !!parseNode && !!parseDecl && (isVariableDeclaration(parseDecl) || isBindingElement(parseDecl)) && isBindingCapturedByNode(parseNode, parseDecl); - }, - getDeclarationStatementsForSourceFile: (node, flags, tracker, bundled) => { - const n = getParseTreeNode(node) as SourceFile; - Debug.assert(n && n.kind === SyntaxKind.SourceFile, "Non-sourcefile node passed into getDeclarationsForSourceFile"); - const sym = getSymbolOfNode(node); - if (!sym) { - return !node.locals ? [] : nodeBuilder.symbolTableToDeclarationStatements(node.locals, node, flags, tracker, bundled); - } - return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker, bundled); - } - }; - - function isInHeritageClause(node: PropertyAccessEntityNameExpression) { - return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && node.parent.parent && node.parent.parent.kind === SyntaxKind.HeritageClause; - } - - // defined here to avoid outer scope pollution - function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] | undefined { - // program does not have any files with type reference directives - bail out - if (!fileToDirective) { - return undefined; - } - // property access can only be used as values, or types when within an expression with type arguments inside a heritage clause - // qualified names can only be used as types\namespaces - // identifiers are treated as values only if they appear in type queries - let meaning = SymbolFlags.Type | SymbolFlags.Namespace; - if ((node.kind === SyntaxKind.Identifier && isInTypeQuery(node)) || (node.kind === SyntaxKind.PropertyAccessExpression && !isInHeritageClause(node))) { - meaning = SymbolFlags.Value | SymbolFlags.ExportValue; - } - - const symbol = resolveEntityName(node, meaning, /*ignoreErrors*/ true); - return symbol && symbol !== unknownSymbol ? getTypeReferenceDirectivesForSymbol(symbol, meaning) : undefined; - } - - // defined here to avoid outer scope pollution - function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined { - // program does not have any files with type reference directives - bail out - if (!fileToDirective) { - return undefined; - } - if (!isSymbolFromTypeDeclarationFile(symbol)) { - return undefined; - } - // check what declarations in the symbol can contribute to the target meaning - let typeReferenceDirectives: string[] | undefined; - for (const decl of symbol.declarations) { - // check meaning of the local symbol to see if declaration needs to be analyzed further - if (decl.symbol && decl.symbol.flags & meaning!) { - const file = getSourceFileOfNode(decl); - const typeReferenceDirective = fileToDirective.get(file.path); - if (typeReferenceDirective) { - (typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective); - } - else { - // found at least one entry that does not originate from type reference directive - return undefined; - } - } - } - return typeReferenceDirectives; - } - - function isSymbolFromTypeDeclarationFile(symbol: Symbol): boolean { - // bail out if symbol does not have associated declarations (i.e. this is transient symbol created for property in binding pattern) - if (!symbol.declarations) { - return false; - } - - // walk the parent chain for symbols to make sure that top level parent symbol is in the global scope - // external modules cannot define or contribute to type declaration files - let current = symbol; - while (true) { - const parent = getParentOfSymbol(current); - if (parent) { - current = parent; - } - else { - break; - } - } - - if (current.valueDeclaration && current.valueDeclaration.kind === SyntaxKind.SourceFile && current.flags & SymbolFlags.ValueModule) { - return false; - } - - // check that at least one declaration of top level symbol originates from type declaration file - for (const decl of symbol.declarations) { - const file = getSourceFileOfNode(decl); - if (fileToDirective.has(file.path)) { - return true; - } - } - return false; - } - - function addReferencedFilesToTypeDirective(file: SourceFile, key: string) { - if (fileToDirective.has(file.path)) return; - fileToDirective.set(file.path, key); - for (const { fileName } of file.referencedFiles) { - const resolvedFile = resolveTripleslashReference(fileName, file.originalFileName); - const referencedFile = host.getSourceFile(resolvedFile); - if (referencedFile) { - addReferencedFilesToTypeDirective(referencedFile, key); - } - } - } - } - - function getExternalModuleFileFromDeclaration(declaration: AnyImportOrReExport | ModuleDeclaration | ImportTypeNode): SourceFile | undefined { - const specifier = declaration.kind === SyntaxKind.ModuleDeclaration ? tryCast(declaration.name, isStringLiteral) : getExternalModuleName(declaration); - const moduleSymbol = resolveExternalModuleNameWorker(specifier!, specifier!, /*moduleNotFoundError*/ undefined); // TODO: GH#18217 - if (!moduleSymbol) { - return undefined; - } - return getDeclarationOfKind(moduleSymbol, SyntaxKind.SourceFile); - } - - function initializeTypeChecker() { - // Bind all source files and propagate errors - for (const file of host.getSourceFiles()) { - bindSourceFile(file, compilerOptions); - } - - amalgamatedDuplicates = createMap(); - - // Initialize global symbol table - let augmentations: (readonly (StringLiteral | Identifier)[])[] | undefined; - for (const file of host.getSourceFiles()) { - if (file.redirectInfo) { - continue; - } - if (!isExternalOrCommonJsModule(file)) { - // It is an error for a non-external-module (i.e. script) to declare its own `globalThis`. - // We can't use `builtinGlobals` for this due to synthetic expando-namespace generation in JS files. - const fileGlobalThisSymbol = file.locals!.get("globalThis" as __String); - if (fileGlobalThisSymbol) { - for (const declaration of fileGlobalThisSymbol.declarations) { - diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, "globalThis")); - } - } - mergeSymbolTable(globals, file.locals!); - } - if (file.jsGlobalAugmentations) { - mergeSymbolTable(globals, file.jsGlobalAugmentations); - } - if (file.patternAmbientModules && file.patternAmbientModules.length) { - patternAmbientModules = concatenate(patternAmbientModules, file.patternAmbientModules); - } - if (file.moduleAugmentations.length) { - (augmentations || (augmentations = [])).push(file.moduleAugmentations); - } - if (file.symbol && file.symbol.globalExports) { - // Merge in UMD exports with first-in-wins semantics (see #9771) - const source = file.symbol.globalExports; - source.forEach((sourceSymbol, id) => { - if (!globals.has(id)) { - globals.set(id, sourceSymbol); - } - }); - } - } - - // We do global augmentations separately from module augmentations (and before creating global types) because they - // 1. Affect global types. We won't have the correct global types until global augmentations are merged. Also, - // 2. Module augmentation instantiation requires creating the type of a module, which, in turn, can require - // checking for an export or property on the module (if export=) which, in turn, can fall back to the - // apparent type of the module - either globalObjectType or globalFunctionType - which wouldn't exist if we - // did module augmentations prior to finalizing the global types. - if (augmentations) { - // merge _global_ module augmentations. - // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed - for (const list of augmentations) { - for (const augmentation of list) { - if (!isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration)) continue; - mergeModuleAugmentation(augmentation); - } - } - } - - // Setup global builtins - addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0); - - getSymbolLinks(undefinedSymbol).type = undefinedWideningType; - getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments" as __String, /*arity*/ 0, /*reportErrors*/ true); - getSymbolLinks(unknownSymbol).type = errorType; - getSymbolLinks(globalThisSymbol).type = createObjectType(ObjectFlags.Anonymous, globalThisSymbol); - - // Initialize special types - globalArrayType = getGlobalType("Array" as __String, /*arity*/ 1, /*reportErrors*/ true); - globalObjectType = getGlobalType("Object" as __String, /*arity*/ 0, /*reportErrors*/ true); - globalFunctionType = getGlobalType("Function" as __String, /*arity*/ 0, /*reportErrors*/ true); - globalCallableFunctionType = strictBindCallApply && getGlobalType("CallableFunction" as __String, /*arity*/ 0, /*reportErrors*/ true) || globalFunctionType; - globalNewableFunctionType = strictBindCallApply && getGlobalType("NewableFunction" as __String, /*arity*/ 0, /*reportErrors*/ true) || globalFunctionType; - globalStringType = getGlobalType("String" as __String, /*arity*/ 0, /*reportErrors*/ true); - globalNumberType = getGlobalType("Number" as __String, /*arity*/ 0, /*reportErrors*/ true); - globalBooleanType = getGlobalType("Boolean" as __String, /*arity*/ 0, /*reportErrors*/ true); - globalRegExpType = getGlobalType("RegExp" as __String, /*arity*/ 0, /*reportErrors*/ true); - anyArrayType = createArrayType(anyType); - - autoArrayType = createArrayType(autoType); - if (autoArrayType === emptyObjectType) { - // autoArrayType is used as a marker, so even if global Array type is not defined, it needs to be a unique type - autoArrayType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - } - - globalReadonlyArrayType = getGlobalTypeOrUndefined("ReadonlyArray" as __String, /*arity*/ 1) || globalArrayType; - anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType; - globalThisType = getGlobalTypeOrUndefined("ThisType" as __String, /*arity*/ 1); - - if (augmentations) { - // merge _nonglobal_ module augmentations. - // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed - for (const list of augmentations) { - for (const augmentation of list) { - if (isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration)) continue; - mergeModuleAugmentation(augmentation); - } - } - } - - amalgamatedDuplicates.forEach(({ firstFile, secondFile, conflictingSymbols }) => { - // If not many things conflict, issue individual errors - if (conflictingSymbols.size < 8) { - conflictingSymbols.forEach(({ isBlockScoped, firstFileLocations, secondFileLocations }, symbolName) => { - const message = isBlockScoped ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; - for (const node of firstFileLocations) { - addDuplicateDeclarationError(node, message, symbolName, secondFileLocations); - } - for (const node of secondFileLocations) { - addDuplicateDeclarationError(node, message, symbolName, firstFileLocations); - } - }); - } - else { - // Otherwise issue top-level error since the files appear very identical in terms of what they contain - const list = arrayFrom(conflictingSymbols.keys()).join(", "); - diagnostics.add(addRelatedInfo( - createDiagnosticForNode(firstFile, Diagnostics.Definitions_of_the_following_identifiers_conflict_with_those_in_another_file_Colon_0, list), - createDiagnosticForNode(secondFile, Diagnostics.Conflicts_are_in_this_file) - )); - diagnostics.add(addRelatedInfo( - createDiagnosticForNode(secondFile, Diagnostics.Definitions_of_the_following_identifiers_conflict_with_those_in_another_file_Colon_0, list), - createDiagnosticForNode(firstFile, Diagnostics.Conflicts_are_in_this_file) - )); - } - }); - amalgamatedDuplicates = undefined; - } - - function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) { - if ((requestedExternalEmitHelpers & helpers) !== helpers && compilerOptions.importHelpers) { - const sourceFile = getSourceFileOfNode(location); - if (isEffectiveExternalModule(sourceFile, compilerOptions) && !(location.flags & NodeFlags.Ambient)) { - const helpersModule = resolveHelpersModule(sourceFile, location); - if (helpersModule !== unknownSymbol) { - const uncheckedHelpers = helpers & ~requestedExternalEmitHelpers; - for (let helper = ExternalEmitHelpers.FirstEmitHelper; helper <= ExternalEmitHelpers.LastEmitHelper; helper <<= 1) { - if (uncheckedHelpers & helper) { - const name = getHelperName(helper); - const symbol = getSymbol(helpersModule.exports!, escapeLeadingUnderscores(name), SymbolFlags.Value); - if (!symbol) { - error(location, Diagnostics.This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0, externalHelpersModuleNameText, name); - } - } - } - } - requestedExternalEmitHelpers |= helpers; - } - } - } - - function getHelperName(helper: ExternalEmitHelpers) { - switch (helper) { - case ExternalEmitHelpers.Extends: return "__extends"; - case ExternalEmitHelpers.Assign: return "__assign"; - case ExternalEmitHelpers.Rest: return "__rest"; - case ExternalEmitHelpers.Decorate: return "__decorate"; - case ExternalEmitHelpers.Metadata: return "__metadata"; - case ExternalEmitHelpers.Param: return "__param"; - case ExternalEmitHelpers.Awaiter: return "__awaiter"; - case ExternalEmitHelpers.Generator: return "__generator"; - case ExternalEmitHelpers.Values: return "__values"; - case ExternalEmitHelpers.Read: return "__read"; - case ExternalEmitHelpers.Spread: return "__spread"; - case ExternalEmitHelpers.SpreadArrays: return "__spreadArrays"; - case ExternalEmitHelpers.Await: return "__await"; - case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator"; - case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator"; - case ExternalEmitHelpers.AsyncValues: return "__asyncValues"; - case ExternalEmitHelpers.ExportStar: return "__exportStar"; - case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject"; - case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet"; - case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet"; - default: return Debug.fail("Unrecognized helper"); - } - } - - function resolveHelpersModule(node: SourceFile, errorNode: Node) { - if (!externalHelpersModule) { - externalHelpersModule = resolveExternalModule(node, externalHelpersModuleNameText, Diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, errorNode) || unknownSymbol; - } - return externalHelpersModule; - } - - // GRAMMAR CHECKING - function checkGrammarDecoratorsAndModifiers(node: Node): boolean { - return checkGrammarDecorators(node) || checkGrammarModifiers(node); - } - - function checkGrammarDecorators(node: Node): boolean { - if (!node.decorators) { - return false; - } - if (!nodeCanBeDecorated(node, node.parent, node.parent.parent)) { - if (node.kind === SyntaxKind.MethodDeclaration && !nodeIsPresent((node).body)) { - return grammarErrorOnFirstToken(node, Diagnostics.A_decorator_can_only_decorate_a_method_implementation_not_an_overload); - } - else { - return grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_not_valid_here); - } - } - else if (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { - const accessors = getAllAccessorDeclarations((node.parent).members, node); - if (accessors.firstAccessor.decorators && node === accessors.secondAccessor) { - return grammarErrorOnFirstToken(node, Diagnostics.Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name); - } - } - return false; - } - - function checkGrammarModifiers(node: Node): boolean { - const quickResult = reportObviousModifierErrors(node); - if (quickResult !== undefined) { - return quickResult; - } - - let lastStatic: Node | undefined, lastDeclare: Node | undefined, lastAsync: Node | undefined, lastReadonly: Node | undefined; - let flags = ModifierFlags.None; - for (const modifier of node.modifiers!) { - if (modifier.kind !== SyntaxKind.ReadonlyKeyword) { - if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_member, tokenToString(modifier.kind)); - } - if (node.kind === SyntaxKind.IndexSignature) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind)); - } - } - switch (modifier.kind) { - case SyntaxKind.ConstKeyword: - if (node.kind !== SyntaxKind.EnumDeclaration) { - return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword)); - } - break; - case SyntaxKind.PublicKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.PrivateKeyword: - const text = visibilityToString(modifierToFlag(modifier.kind)); - - if (flags & ModifierFlags.AccessibilityModifier) { - return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen); - } - else if (flags & ModifierFlags.Static) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static"); - } - else if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly"); - } - else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "async"); - } - else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, text); - } - else if (flags & ModifierFlags.Abstract) { - if (modifier.kind === SyntaxKind.PrivateKeyword) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, text, "abstract"); - } - else { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "abstract"); - } - } - flags |= modifierToFlag(modifier.kind); - break; - - case SyntaxKind.StaticKeyword: - if (flags & ModifierFlags.Static) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static"); - } - else if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "readonly"); - } - else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async"); - } - else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, "static"); - } - else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static"); - } - else if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract"); - } - else if (isPrivateIdentifierPropertyDeclaration(node)) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_a_private_identifier, "static"); - } - flags |= ModifierFlags.Static; - lastStatic = modifier; - break; - - case SyntaxKind.ReadonlyKeyword: - if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly"); - } - else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.Parameter) { - // If node.kind === SyntaxKind.Parameter, checkParameter report an error if it's not a parameter property. - return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature); - } - flags |= ModifierFlags.Readonly; - lastReadonly = modifier; - break; - - case SyntaxKind.ExportKeyword: - if (flags & ModifierFlags.Export) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export"); - } - else if (flags & ModifierFlags.Ambient) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare"); - } - else if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "abstract"); - } - else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "async"); - } - else if (isClassLike(node.parent)) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export"); - } - else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export"); - } - flags |= ModifierFlags.Export; - break; - case SyntaxKind.DefaultKeyword: - const container = node.parent.kind === SyntaxKind.SourceFile ? node.parent : node.parent.parent; - if (container.kind === SyntaxKind.ModuleDeclaration && !isAmbientModule(container)) { - return grammarErrorOnNode(modifier, Diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module); - } - - flags |= ModifierFlags.Default; - break; - case SyntaxKind.DeclareKeyword: - if (flags & ModifierFlags.Ambient) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare"); - } - else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); - } - else if (isClassLike(node.parent) && !isPropertyDeclaration(node)) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare"); - } - else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare"); - } - else if ((node.parent.flags & NodeFlags.Ambient) && node.parent.kind === SyntaxKind.ModuleBlock) { - return grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context); - } - flags |= ModifierFlags.Ambient; - lastDeclare = modifier; - break; - - case SyntaxKind.AbstractKeyword: - if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract"); - } - if (node.kind !== SyntaxKind.ClassDeclaration) { - if (node.kind !== SyntaxKind.MethodDeclaration && - node.kind !== SyntaxKind.PropertyDeclaration && - node.kind !== SyntaxKind.GetAccessor && - node.kind !== SyntaxKind.SetAccessor) { - return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration); - } - if (!(node.parent.kind === SyntaxKind.ClassDeclaration && hasModifier(node.parent, ModifierFlags.Abstract))) { - return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class); - } - if (flags & ModifierFlags.Static) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract"); - } - if (flags & ModifierFlags.Private) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "private", "abstract"); - } - } - if (isNamedDeclaration(node) && node.name.kind === SyntaxKind.PrivateIdentifier) { - return grammarErrorOnNode(node, Diagnostics._0_modifier_cannot_be_used_with_a_private_identifier, "abstract"); - } - - flags |= ModifierFlags.Abstract; - break; - - case SyntaxKind.AsyncKeyword: - if (flags & ModifierFlags.Async) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "async"); - } - else if (flags & ModifierFlags.Ambient || node.parent.flags & NodeFlags.Ambient) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); - } - else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "async"); - } - flags |= ModifierFlags.Async; - lastAsync = modifier; - break; - } - } - - if (node.kind === SyntaxKind.Constructor) { - if (flags & ModifierFlags.Static) { - return grammarErrorOnNode(lastStatic!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static"); - } - if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode(lastStatic!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "abstract"); // TODO: GH#18217 - } - else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode(lastAsync!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async"); - } - else if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode(lastReadonly!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "readonly"); - } - return false; - } - else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & ModifierFlags.Ambient) { - return grammarErrorOnNode(lastDeclare!, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare"); - } - else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && isBindingPattern((node).name)) { - return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_declared_using_a_binding_pattern); - } - else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && (node).dotDotDotToken) { - return grammarErrorOnNode(node, Diagnostics.A_parameter_property_cannot_be_declared_using_a_rest_parameter); - } - else if (isNamedDeclaration(node) && (flags & ModifierFlags.AccessibilityModifier) && node.name.kind === SyntaxKind.PrivateIdentifier) { - return grammarErrorOnNode(node, Diagnostics.An_accessibility_modifier_cannot_be_used_with_a_private_identifier); - } - if (flags & ModifierFlags.Async) { - return checkGrammarAsyncModifier(node, lastAsync!); - } - return false; - } - - /** - * true | false: Early return this value from checkGrammarModifiers. - * undefined: Need to do full checking on the modifiers. - */ - function reportObviousModifierErrors(node: Node): boolean | undefined { - return !node.modifiers - ? false - : shouldReportBadModifier(node) - ? grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here) - : undefined; - } - function shouldReportBadModifier(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.Constructor: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportDeclaration: - case SyntaxKind.ExportAssignment: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.Parameter: - return false; - default: - if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { - return false; - } - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - return nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword); - case SyntaxKind.ClassDeclaration: - return nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword); - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.VariableStatement: - case SyntaxKind.TypeAliasDeclaration: - return true; - case SyntaxKind.EnumDeclaration: - return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword); - default: - Debug.fail(); - return false; - } - } - } - function nodeHasAnyModifiersExcept(node: Node, allowedModifier: SyntaxKind): boolean { - return node.modifiers!.length > 1 || node.modifiers![0].kind !== allowedModifier; - } - - function checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean { - switch (node.kind) { - case SyntaxKind.MethodDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return false; - } - - return grammarErrorOnNode(asyncModifier, Diagnostics._0_modifier_cannot_be_used_here, "async"); - } - - function checkGrammarForDisallowedTrailingComma(list: NodeArray | undefined, diag = Diagnostics.Trailing_comma_not_allowed): boolean { - if (list && list.hasTrailingComma) { - return grammarErrorAtPos(list[0], list.end - ",".length, ",".length, diag); - } - return false; - } - - function checkGrammarTypeParameterList(typeParameters: NodeArray | undefined, file: SourceFile): boolean { - if (typeParameters && typeParameters.length === 0) { - const start = typeParameters.pos - "<".length; - const end = skipTrivia(file.text, typeParameters.end) + ">".length; - return grammarErrorAtPos(file, start, end - start, Diagnostics.Type_parameter_list_cannot_be_empty); - } - return false; - } - - function checkGrammarParameterList(parameters: NodeArray) { - let seenOptionalParameter = false; - const parameterCount = parameters.length; - - for (let i = 0; i < parameterCount; i++) { - const parameter = parameters[i]; - if (parameter.dotDotDotToken) { - if (i !== (parameterCount - 1)) { - return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list); - } - if (!(parameter.flags & NodeFlags.Ambient)) { // Allow `...foo,` in ambient declarations; see GH#23070 - checkGrammarForDisallowedTrailingComma(parameters, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma); - } - - if (parameter.questionToken) { - return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_rest_parameter_cannot_be_optional); - } - - if (parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer); - } - } - else if (parameter.questionToken) { - seenOptionalParameter = true; - - if (parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer); - } - } - else if (seenOptionalParameter && !parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); - } - } - } - - function getNonSimpleParameters(parameters: readonly ParameterDeclaration[]): readonly ParameterDeclaration[] { - return filter(parameters, parameter => !!parameter.initializer || isBindingPattern(parameter.name) || isRestParameter(parameter)); - } - - function checkGrammarForUseStrictSimpleParameterList(node: FunctionLikeDeclaration): boolean { - if (languageVersion >= ScriptTarget.ES2016) { - const useStrictDirective = node.body && isBlock(node.body) && findUseStrictPrologue(node.body.statements); - if (useStrictDirective) { - const nonSimpleParameters = getNonSimpleParameters(node.parameters); - if (length(nonSimpleParameters)) { - forEach(nonSimpleParameters, parameter => { - addRelatedInfo( - error(parameter, Diagnostics.This_parameter_is_not_allowed_with_use_strict_directive), - createDiagnosticForNode(useStrictDirective, Diagnostics.use_strict_directive_used_here) - ); - }); - - const diagnostics = nonSimpleParameters.map((parameter, index) => ( - index === 0 ? createDiagnosticForNode(parameter, Diagnostics.Non_simple_parameter_declared_here) : createDiagnosticForNode(parameter, Diagnostics.and_here) - )) as [DiagnosticWithLocation, ...DiagnosticWithLocation[]]; - addRelatedInfo(error(useStrictDirective, Diagnostics.use_strict_directive_cannot_be_used_with_non_simple_parameter_list), ...diagnostics); - return true; - } - } - } - return false; - } - - function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration | MethodSignature): boolean { - // Prevent cascading error by short-circuit - const file = getSourceFileOfNode(node); - return checkGrammarDecoratorsAndModifiers(node) || checkGrammarTypeParameterList(node.typeParameters, file) || - checkGrammarParameterList(node.parameters) || checkGrammarArrowFunction(node, file) || - (isFunctionLikeDeclaration(node) && checkGrammarForUseStrictSimpleParameterList(node)); - } - - function checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean { - const file = getSourceFileOfNode(node); - return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file); - } - - function checkGrammarArrowFunction(node: Node, file: SourceFile): boolean { - if (!isArrowFunction(node)) { - return false; - } - - const { equalsGreaterThanToken } = node; - const startLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.pos).line; - const endLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.end).line; - return startLine !== endLine && grammarErrorOnNode(equalsGreaterThanToken, Diagnostics.Line_terminator_not_permitted_before_arrow); - } - - function checkGrammarIndexSignatureParameters(node: SignatureDeclaration): boolean { - const parameter = node.parameters[0]; - if (node.parameters.length !== 1) { - if (parameter) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_must_have_exactly_one_parameter); - } - else { - return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_exactly_one_parameter); - } - } - if (parameter.dotDotDotToken) { - return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter); - } - if (hasModifiers(parameter)) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); - } - if (parameter.questionToken) { - return grammarErrorOnNode(parameter.questionToken, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark); - } - if (parameter.initializer) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer); - } - if (!parameter.type) { - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation); - } - if (parameter.type.kind !== SyntaxKind.StringKeyword && parameter.type.kind !== SyntaxKind.NumberKeyword) { - const type = getTypeFromTypeNode(parameter.type); - - if (type.flags & TypeFlags.String || type.flags & TypeFlags.Number) { - return grammarErrorOnNode(parameter.name, - Diagnostics.An_index_signature_parameter_type_cannot_be_a_type_alias_Consider_writing_0_Colon_1_Colon_2_instead, - getTextOfNode(parameter.name), - typeToString(type), - typeToString(node.type ? getTypeFromTypeNode(node.type) : anyType)); - } - - if (type.flags & TypeFlags.Union && allTypesAssignableToKind(type, TypeFlags.StringOrNumberLiteral, /*strict*/ true)) { - return grammarErrorOnNode(parameter.name, - Diagnostics.An_index_signature_parameter_type_cannot_be_a_union_type_Consider_using_a_mapped_object_type_instead); - } - - return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_either_string_or_number); - } - if (!node.type) { - return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_a_type_annotation); - } - return false; - } - - function checkGrammarIndexSignature(node: SignatureDeclaration) { - // Prevent cascading error by short-circuit - return checkGrammarDecoratorsAndModifiers(node) || checkGrammarIndexSignatureParameters(node); - } - - function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray | undefined): boolean { - if (typeArguments && typeArguments.length === 0) { - const sourceFile = getSourceFileOfNode(node); - const start = typeArguments.pos - "<".length; - const end = skipTrivia(sourceFile.text, typeArguments.end) + ">".length; - return grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Type_argument_list_cannot_be_empty); - } - return false; - } - - function checkGrammarTypeArguments(node: Node, typeArguments: NodeArray | undefined): boolean { - return checkGrammarForDisallowedTrailingComma(typeArguments) || - checkGrammarForAtLeastOneTypeArgument(node, typeArguments); - } - - function checkGrammarTaggedTemplateChain(node: TaggedTemplateExpression): boolean { - if (node.questionDotToken || node.flags & NodeFlags.OptionalChain) { - return grammarErrorOnNode(node.template, Diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain); - } - return false; - } - - function checkGrammarForOmittedArgument(args: NodeArray | undefined): boolean { - if (args) { - for (const arg of args) { - if (arg.kind === SyntaxKind.OmittedExpression) { - return grammarErrorAtPos(arg, arg.pos, 0, Diagnostics.Argument_expression_expected); - } - } - } - return false; - } - - function checkGrammarArguments(args: NodeArray | undefined): boolean { - return checkGrammarForOmittedArgument(args); - } - - function checkGrammarHeritageClause(node: HeritageClause): boolean { - const types = node.types; - if (checkGrammarForDisallowedTrailingComma(types)) { - return true; - } - if (types && types.length === 0) { - const listType = tokenToString(node.token); - return grammarErrorAtPos(node, types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType); - } - return some(types, checkGrammarExpressionWithTypeArguments); - } - - function checkGrammarExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - return checkGrammarTypeArguments(node, node.typeArguments); - } - - function checkGrammarClassDeclarationHeritageClauses(node: ClassLikeDeclaration) { - let seenExtendsClause = false; - let seenImplementsClause = false; - - if (!checkGrammarDecoratorsAndModifiers(node) && node.heritageClauses) { - for (const heritageClause of node.heritageClauses) { - if (heritageClause.token === SyntaxKind.ExtendsKeyword) { - if (seenExtendsClause) { - return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen); - } - - if (seenImplementsClause) { - return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_must_precede_implements_clause); - } - - if (heritageClause.types.length > 1) { - return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_can_only_extend_a_single_class); - } - - seenExtendsClause = true; - } - else { - Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword); - if (seenImplementsClause) { - return grammarErrorOnFirstToken(heritageClause, Diagnostics.implements_clause_already_seen); - } - - seenImplementsClause = true; - } - - // Grammar checking heritageClause inside class declaration - checkGrammarHeritageClause(heritageClause); - } - } - } - - function checkGrammarInterfaceDeclaration(node: InterfaceDeclaration) { - let seenExtendsClause = false; - - if (node.heritageClauses) { - for (const heritageClause of node.heritageClauses) { - if (heritageClause.token === SyntaxKind.ExtendsKeyword) { - if (seenExtendsClause) { - return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen); - } - - seenExtendsClause = true; - } - else { - Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword); - return grammarErrorOnFirstToken(heritageClause, Diagnostics.Interface_declaration_cannot_have_implements_clause); - } - - // Grammar checking heritageClause inside class declaration - checkGrammarHeritageClause(heritageClause); - } - } - return false; - } - - function checkGrammarComputedPropertyName(node: Node): boolean { - // If node is not a computedPropertyName, just skip the grammar checking - if (node.kind !== SyntaxKind.ComputedPropertyName) { - return false; - } - - const computedPropertyName = node; - if (computedPropertyName.expression.kind === SyntaxKind.BinaryExpression && (computedPropertyName.expression).operatorToken.kind === SyntaxKind.CommaToken) { - return grammarErrorOnNode(computedPropertyName.expression, Diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name); - } - return false; - } - - function checkGrammarForGenerator(node: FunctionLikeDeclaration) { - if (node.asteriskToken) { - Debug.assert( - node.kind === SyntaxKind.FunctionDeclaration || - node.kind === SyntaxKind.FunctionExpression || - node.kind === SyntaxKind.MethodDeclaration); - if (node.flags & NodeFlags.Ambient) { - return grammarErrorOnNode(node.asteriskToken!, Diagnostics.Generators_are_not_allowed_in_an_ambient_context); - } - if (!node.body) { - return grammarErrorOnNode(node.asteriskToken!, Diagnostics.An_overload_signature_cannot_be_declared_as_a_generator); - } - } - } - - function checkGrammarForInvalidQuestionMark(questionToken: QuestionToken | undefined, message: DiagnosticMessage): boolean { - return !!questionToken && grammarErrorOnNode(questionToken, message); - } - - function checkGrammarForInvalidExclamationToken(exclamationToken: ExclamationToken | undefined, message: DiagnosticMessage): boolean { - return !!exclamationToken && grammarErrorOnNode(exclamationToken, message); - } - - function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { - const seen = createUnderscoreEscapedMap(); - - for (const prop of node.properties) { - if (prop.kind === SyntaxKind.SpreadAssignment) { - if (inDestructuring) { - // a rest property cannot be destructured any further - const expression = skipParentheses(prop.expression); - if (isArrayLiteralExpression(expression) || isObjectLiteralExpression(expression)) { - return grammarErrorOnNode(prop.expression, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern); - } - } - continue; - } - const name = prop.name; - if (name.kind === SyntaxKind.ComputedPropertyName) { - // If the name is not a ComputedPropertyName, the grammar checking will skip it - checkGrammarComputedPropertyName(name); - } - - if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && prop.objectAssignmentInitializer) { - // having objectAssignmentInitializer is only valid in ObjectAssignmentPattern - // outside of destructuring it is a syntax error - return grammarErrorOnNode(prop.equalsToken!, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment); - } - - if (name.kind === SyntaxKind.PrivateIdentifier) { - return grammarErrorOnNode(name, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - } - - // Modifiers are never allowed on properties except for 'async' on a method declaration - if (prop.modifiers) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - for (const mod of prop.modifiers!) { // TODO: GH#19955 - if (mod.kind !== SyntaxKind.AsyncKeyword || prop.kind !== SyntaxKind.MethodDeclaration) { - grammarErrorOnNode(mod, Diagnostics._0_modifier_cannot_be_used_here, getTextOfNode(mod)); - } - } - } - - // ECMA-262 11.1.5 Object Initializer - // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true - // a.This production is contained in strict code and IsDataDescriptor(previous) is true and - // IsDataDescriptor(propId.descriptor) is true. - // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true. - // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true. - // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true - // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields - let currentKind: DeclarationMeaning; - switch (prop.kind) { - case SyntaxKind.ShorthandPropertyAssignment: - checkGrammarForInvalidExclamationToken(prop.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); - // falls through - case SyntaxKind.PropertyAssignment: - // Grammar checking for computedPropertyName and shorthandPropertyAssignment - checkGrammarForInvalidQuestionMark(prop.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional); - if (name.kind === SyntaxKind.NumericLiteral) { - checkGrammarNumericLiteral(name); - } - currentKind = DeclarationMeaning.PropertyAssignment; - break; - case SyntaxKind.MethodDeclaration: - currentKind = DeclarationMeaning.Method; - break; - case SyntaxKind.GetAccessor: - currentKind = DeclarationMeaning.GetAccessor; - break; - case SyntaxKind.SetAccessor: - currentKind = DeclarationMeaning.SetAccessor; - break; - default: - throw Debug.assertNever(prop, "Unexpected syntax kind:" + (prop).kind); - } - - const effectiveName = getPropertyNameForPropertyNameNode(name); - if (effectiveName === undefined) { - continue; - } - - const existingKind = seen.get(effectiveName); - if (!existingKind) { - seen.set(effectiveName, currentKind); - } - else { - if ((currentKind & DeclarationMeaning.PropertyAssignmentOrMethod) && (existingKind & DeclarationMeaning.PropertyAssignmentOrMethod)) { - grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name)); - } - else if ((currentKind & DeclarationMeaning.GetOrSetAccessor) && (existingKind & DeclarationMeaning.GetOrSetAccessor)) { - if (existingKind !== DeclarationMeaning.GetOrSetAccessor && currentKind !== existingKind) { - seen.set(effectiveName, currentKind | existingKind); - } - else { - return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); - } - } - else { - return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name); - } - } - } - } - - function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - checkGrammarTypeArguments(node, node.typeArguments); - const seen = createUnderscoreEscapedMap(); - - for (const attr of node.attributes.properties) { - if (attr.kind === SyntaxKind.JsxSpreadAttribute) { - continue; - } - - const { name, initializer } = attr; - if (!seen.get(name.escapedText)) { - seen.set(name.escapedText, true); - } - else { - return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name); - } - - if (initializer && initializer.kind === SyntaxKind.JsxExpression && !initializer.expression) { - return grammarErrorOnNode(initializer, Diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression); - } - } - } - - function checkGrammarJsxExpression(node: JsxExpression) { - if (node.expression && isCommaSequence(node.expression)) { - return grammarErrorOnNode(node.expression, Diagnostics.JSX_expressions_may_not_use_the_comma_operator_Did_you_mean_to_write_an_array); - } - } - - function checkGrammarForInOrForOfStatement(forInOrOfStatement: ForInOrOfStatement): boolean { - if (checkGrammarStatementInAmbientContext(forInOrOfStatement)) { - return true; - } - - if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitModifier) { - if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) { - // use of 'for-await-of' in non-async function - const sourceFile = getSourceFileOfNode(forInOrOfStatement); - if (!hasParseDiagnostics(sourceFile)) { - const diagnostic = createDiagnosticForNode(forInOrOfStatement.awaitModifier, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator); - const func = getContainingFunction(forInOrOfStatement); - if (func && func.kind !== SyntaxKind.Constructor) { - Debug.assert((getFunctionFlags(func) & FunctionFlags.Async) === 0, "Enclosing function should never be an async function."); - const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async); - addRelatedInfo(diagnostic, relatedInfo); - } - diagnostics.add(diagnostic); - return true; - } - return false; - } - } - - if (forInOrOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) { - const variableList = forInOrOfStatement.initializer; - if (!checkGrammarVariableDeclarationList(variableList)) { - const declarations = variableList.declarations; - - // declarations.length can be zero if there is an error in variable declaration in for-of or for-in - // See http://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements for details - // For example: - // var let = 10; - // for (let of [1,2,3]) {} // this is invalid ES6 syntax - // for (let in [1,2,3]) {} // this is invalid ES6 syntax - // We will then want to skip on grammar checking on variableList declaration - if (!declarations.length) { - return false; - } - - if (declarations.length > 1) { - const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement - ? Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement - : Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement; - return grammarErrorOnFirstToken(variableList.declarations[1], diagnostic); - } - const firstDeclaration = declarations[0]; - - if (firstDeclaration.initializer) { - const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement - ? Diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer - : Diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer; - return grammarErrorOnNode(firstDeclaration.name, diagnostic); - } - if (firstDeclaration.type) { - const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement - ? Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation - : Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation; - return grammarErrorOnNode(firstDeclaration, diagnostic); - } - } - } - - return false; - } - - function checkGrammarAccessor(accessor: AccessorDeclaration): boolean { - if (!(accessor.flags & NodeFlags.Ambient)) { - if (languageVersion < ScriptTarget.ES5) { - return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher); - } - if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract)) { - return grammarErrorAtPos(accessor, accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); - } - } - if (accessor.body && hasModifier(accessor, ModifierFlags.Abstract)) { - return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation); - } - if (accessor.typeParameters) { - return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters); - } - if (!doesAccessorHaveCorrectParameterCount(accessor)) { - return grammarErrorOnNode(accessor.name, - accessor.kind === SyntaxKind.GetAccessor ? - Diagnostics.A_get_accessor_cannot_have_parameters : - Diagnostics.A_set_accessor_must_have_exactly_one_parameter); - } - if (accessor.kind === SyntaxKind.SetAccessor) { - if (accessor.type) { - return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation); - } - const parameter = Debug.assertDefined(getSetAccessorValueParameter(accessor), "Return value does not match parameter count assertion."); - if (parameter.dotDotDotToken) { - return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_set_accessor_cannot_have_rest_parameter); - } - if (parameter.questionToken) { - return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter); - } - if (parameter.initializer) { - return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_parameter_cannot_have_an_initializer); - } - } - return false; - } - - /** Does the accessor have the right number of parameters? - * A get accessor has no parameters or a single `this` parameter. - * A set accessor has one parameter or a `this` parameter and one more parameter. - */ - function doesAccessorHaveCorrectParameterCount(accessor: AccessorDeclaration) { - return getAccessorThisParameter(accessor) || accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 0 : 1); - } - - function getAccessorThisParameter(accessor: AccessorDeclaration): ParameterDeclaration | undefined { - if (accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2)) { - return getThisParameter(accessor); - } - } - - function checkGrammarTypeOperatorNode(node: TypeOperatorNode) { - if (node.operator === SyntaxKind.UniqueKeyword) { - if (node.type.kind !== SyntaxKind.SymbolKeyword) { - return grammarErrorOnNode(node.type, Diagnostics._0_expected, tokenToString(SyntaxKind.SymbolKeyword)); - } - - const parent = walkUpParenthesizedTypes(node.parent); - switch (parent.kind) { - case SyntaxKind.VariableDeclaration: - const decl = parent as VariableDeclaration; - if (decl.name.kind !== SyntaxKind.Identifier) { - return grammarErrorOnNode(node, Diagnostics.unique_symbol_types_may_not_be_used_on_a_variable_declaration_with_a_binding_name); - } - if (!isVariableDeclarationInVariableStatement(decl)) { - return grammarErrorOnNode(node, Diagnostics.unique_symbol_types_are_only_allowed_on_variables_in_a_variable_statement); - } - if (!(decl.parent.flags & NodeFlags.Const)) { - return grammarErrorOnNode((parent).name, Diagnostics.A_variable_whose_type_is_a_unique_symbol_type_must_be_const); - } - break; - - case SyntaxKind.PropertyDeclaration: - if (!hasModifier(parent, ModifierFlags.Static) || - !hasModifier(parent, ModifierFlags.Readonly)) { - return grammarErrorOnNode((parent).name, Diagnostics.A_property_of_a_class_whose_type_is_a_unique_symbol_type_must_be_both_static_and_readonly); - } - break; - - case SyntaxKind.PropertySignature: - if (!hasModifier(parent, ModifierFlags.Readonly)) { - return grammarErrorOnNode((parent).name, Diagnostics.A_property_of_an_interface_or_type_literal_whose_type_is_a_unique_symbol_type_must_be_readonly); - } - break; - - default: - return grammarErrorOnNode(node, Diagnostics.unique_symbol_types_are_not_allowed_here); - } - } - else if (node.operator === SyntaxKind.ReadonlyKeyword) { - if (node.type.kind !== SyntaxKind.ArrayType && node.type.kind !== SyntaxKind.TupleType) { - return grammarErrorOnFirstToken(node, Diagnostics.readonly_type_modifier_is_only_permitted_on_array_and_tuple_literal_types, tokenToString(SyntaxKind.SymbolKeyword)); - } - } - } - - function checkGrammarForInvalidDynamicName(node: DeclarationName, message: DiagnosticMessage) { - if (isNonBindableDynamicName(node)) { - return grammarErrorOnNode(node, message); - } - } - - function checkGrammarMethod(node: MethodDeclaration | MethodSignature) { - if (checkGrammarFunctionLikeDeclaration(node)) { - return true; - } - - if (node.kind === SyntaxKind.MethodDeclaration) { - if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) { - // We only disallow modifier on a method declaration if it is a property of object-literal-expression - if (node.modifiers && !(node.modifiers.length === 1 && first(node.modifiers).kind === SyntaxKind.AsyncKeyword)) { - return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); - } - else if (checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) { - return true; - } - else if (checkGrammarForInvalidExclamationToken(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context)) { - return true; - } - else if (node.body === undefined) { - return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{"); - } - } - if (checkGrammarForGenerator(node)) { - return true; - } - } - - if (isClassLike(node.parent)) { - // Technically, computed properties in ambient contexts is disallowed - // for property declarations and accessors too, not just methods. - // However, property declarations disallow computed names in general, - // and accessors are not allowed in ambient contexts in general, - // so this error only really matters for methods. - if (node.flags & NodeFlags.Ambient) { - return checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_an_ambient_context_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type); - } - else if (node.kind === SyntaxKind.MethodDeclaration && !node.body) { - return checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_method_overload_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type); - } - } - else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) { - return checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type); - } - else if (node.parent.kind === SyntaxKind.TypeLiteral) { - return checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type); - } - } - - function checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean { - let current: Node = node; - while (current) { - if (isFunctionLike(current)) { - return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary); - } - - switch (current.kind) { - case SyntaxKind.LabeledStatement: - if (node.label && (current).label.escapedText === node.label.escapedText) { - // found matching label - verify that label usage is correct - // continue can only target labels that are on iteration statements - const isMisplacedContinueLabel = node.kind === SyntaxKind.ContinueStatement - && !isIterationStatement((current).statement, /*lookInLabeledStatement*/ true); - - if (isMisplacedContinueLabel) { - return grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement); - } - - return false; - } - break; - case SyntaxKind.SwitchStatement: - if (node.kind === SyntaxKind.BreakStatement && !node.label) { - // unlabeled break within switch statement - ok - return false; - } - break; - default: - if (isIterationStatement(current, /*lookInLabeledStatement*/ false) && !node.label) { - // unlabeled break or continue within iteration statement - ok - return false; - } - break; - } - - current = current.parent; - } - - if (node.label) { - const message = node.kind === SyntaxKind.BreakStatement - ? Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement - : Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement; - - return grammarErrorOnNode(node, message); - } - else { - const message = node.kind === SyntaxKind.BreakStatement - ? Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement - : Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement; - return grammarErrorOnNode(node, message); - } - } - - function checkGrammarBindingElement(node: BindingElement) { - if (node.dotDotDotToken) { - const elements = node.parent.elements; - if (node !== last(elements)) { - return grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern); - } - checkGrammarForDisallowedTrailingComma(elements, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma); - - if (node.propertyName) { - return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_have_a_property_name); - } - - if (node.initializer) { - // Error on equals token which immediately precedes the initializer - return grammarErrorAtPos(node, node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer); - } - } - } - - function isStringOrNumberLiteralExpression(expr: Expression) { - return isStringOrNumericLiteralLike(expr) || - expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && - (expr).operand.kind === SyntaxKind.NumericLiteral; - } - - function isBigIntLiteralExpression(expr: Expression) { - return expr.kind === SyntaxKind.BigIntLiteral || - expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && - (expr).operand.kind === SyntaxKind.BigIntLiteral; - } - - function isSimpleLiteralEnumReference(expr: Expression) { - if ((isPropertyAccessExpression(expr) || (isElementAccessExpression(expr) && isStringOrNumberLiteralExpression(expr.argumentExpression))) && - isEntityNameExpression(expr.expression)) { - return !!(checkExpressionCached(expr).flags & TypeFlags.EnumLiteral); - } - } - - function checkAmbientInitializer(node: VariableDeclaration | PropertyDeclaration | PropertySignature) { - const {initializer} = node; - if (initializer) { - const isInvalidInitializer = !( - isStringOrNumberLiteralExpression(initializer) || - isSimpleLiteralEnumReference(initializer) || - initializer.kind === SyntaxKind.TrueKeyword || initializer.kind === SyntaxKind.FalseKeyword || - isBigIntLiteralExpression(initializer) - ); - const isConstOrReadonly = isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConst(node); - if (isConstOrReadonly && !node.type) { - if (isInvalidInitializer) { - return grammarErrorOnNode(initializer, Diagnostics.A_const_initializer_in_an_ambient_context_must_be_a_string_or_numeric_literal_or_literal_enum_reference); - } - } - else { - return grammarErrorOnNode(initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); - } - if (!isConstOrReadonly || isInvalidInitializer) { - return grammarErrorOnNode(initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); - } - } - } - - function checkGrammarVariableDeclaration(node: VariableDeclaration) { - if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) { - if (node.flags & NodeFlags.Ambient) { - checkAmbientInitializer(node); - } - else if (!node.initializer) { - if (isBindingPattern(node.name) && !isBindingPattern(node.parent)) { - return grammarErrorOnNode(node, Diagnostics.A_destructuring_declaration_must_have_an_initializer); - } - if (isVarConst(node)) { - return grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized); - } - } - } - - if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) { - return grammarErrorOnNode(node.exclamationToken, Diagnostics.Definite_assignment_assertions_can_only_be_used_along_with_a_type_annotation); - } - - const moduleKind = getEmitModuleKind(compilerOptions); - - if (moduleKind < ModuleKind.ES2015 && moduleKind !== ModuleKind.System && !compilerOptions.noEmit && - !(node.parent.parent.flags & NodeFlags.Ambient) && hasModifier(node.parent.parent, ModifierFlags.Export)) { - checkESModuleMarker(node.name); - } - - const checkLetConstNames = (isLet(node) || isVarConst(node)); - - // 1. LexicalDeclaration : LetOrConst BindingList ; - // It is a Syntax Error if the BoundNames of BindingList contains "let". - // 2. ForDeclaration: ForDeclaration : LetOrConst ForBinding - // It is a Syntax Error if the BoundNames of ForDeclaration contains "let". - - // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code - // and its Identifier is eval or arguments - return checkLetConstNames && checkGrammarNameInLetOrConstDeclarations(node.name); - } - - function checkESModuleMarker(name: Identifier | BindingPattern): boolean { - if (name.kind === SyntaxKind.Identifier) { - if (idText(name) === "__esModule") { - return grammarErrorOnNode(name, Diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules); - } - } - else { - const elements = name.elements; - for (const element of elements) { - if (!isOmittedExpression(element)) { - return checkESModuleMarker(element.name); - } - } - } - return false; - } - - function checkGrammarNameInLetOrConstDeclarations(name: Identifier | BindingPattern): boolean { - if (name.kind === SyntaxKind.Identifier) { - if (name.originalKeywordKind === SyntaxKind.LetKeyword) { - return grammarErrorOnNode(name, Diagnostics.let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations); - } - } - else { - const elements = name.elements; - for (const element of elements) { - if (!isOmittedExpression(element)) { - checkGrammarNameInLetOrConstDeclarations(element.name); - } - } - } - return false; - } - - function checkGrammarVariableDeclarationList(declarationList: VariableDeclarationList): boolean { - const declarations = declarationList.declarations; - if (checkGrammarForDisallowedTrailingComma(declarationList.declarations)) { - return true; - } - - if (!declarationList.declarations.length) { - return grammarErrorAtPos(declarationList, declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty); - } - return false; - } - - function allowLetAndConstDeclarations(parent: Node): boolean { - switch (parent.kind) { - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - return false; - case SyntaxKind.LabeledStatement: - return allowLetAndConstDeclarations(parent.parent); - } - - return true; - } - - function checkGrammarForDisallowedLetOrConstStatement(node: VariableStatement) { - if (!allowLetAndConstDeclarations(node.parent)) { - if (isLet(node.declarationList)) { - return grammarErrorOnNode(node, Diagnostics.let_declarations_can_only_be_declared_inside_a_block); - } - else if (isVarConst(node.declarationList)) { - return grammarErrorOnNode(node, Diagnostics.const_declarations_can_only_be_declared_inside_a_block); - } - } - } - - function checkGrammarMetaProperty(node: MetaProperty) { - const escapedText = node.name.escapedText; - switch (node.keywordToken) { - case SyntaxKind.NewKeyword: - if (escapedText !== "target") { - return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.escapedText, tokenToString(node.keywordToken), "target"); - } - break; - case SyntaxKind.ImportKeyword: - if (escapedText !== "meta") { - return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.escapedText, tokenToString(node.keywordToken), "meta"); - } - break; - } - } - - function hasParseDiagnostics(sourceFile: SourceFile): boolean { - return sourceFile.parseDiagnostics.length > 0; - } - - function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition(sourceFile, node.pos); - diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2)); - return true; - } - return false; - } - - function grammarErrorAtPos(nodeForSourceFile: Node, start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { - const sourceFile = getSourceFileOfNode(nodeForSourceFile); - if (!hasParseDiagnostics(sourceFile)) { - diagnostics.add(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2)); - return true; - } - return false; - } - - function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - diagnostics.add(createDiagnosticForNode(node, message, arg0, arg1, arg2)); - return true; - } - return false; - } - - function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) { - const jsdocTypeParameters = isInJSFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined; - const range = node.typeParameters || jsdocTypeParameters && firstOrUndefined(jsdocTypeParameters); - if (range) { - const pos = range.pos === range.end ? range.pos : skipTrivia(getSourceFileOfNode(node).text, range.pos); - return grammarErrorAtPos(node, pos, range.end - pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration); - } - } - - function checkGrammarConstructorTypeAnnotation(node: ConstructorDeclaration) { - const type = getEffectiveReturnTypeNode(node); - if (type) { - return grammarErrorOnNode(type, Diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration); - } - } - - function checkGrammarProperty(node: PropertyDeclaration | PropertySignature) { - if (isClassLike(node.parent)) { - if (isStringLiteral(node.name) && node.name.text === "constructor") { - return grammarErrorOnNode(node.name, Diagnostics.Classes_may_not_have_a_field_named_constructor); - } - if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) { - return true; - } - if (languageVersion < ScriptTarget.ES2015 && isPrivateIdentifier(node.name)) { - return grammarErrorOnNode(node.name, Diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher); - } - } - else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) { - if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) { - return true; - } - if (node.initializer) { - return grammarErrorOnNode(node.initializer, Diagnostics.An_interface_property_cannot_have_an_initializer); - } - } - else if (node.parent.kind === SyntaxKind.TypeLiteral) { - if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) { - return true; - } - if (node.initializer) { - return grammarErrorOnNode(node.initializer, Diagnostics.A_type_literal_property_cannot_have_an_initializer); - } - } - - if (node.flags & NodeFlags.Ambient) { - checkAmbientInitializer(node); - } - - if (isPropertyDeclaration(node) && node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer || - node.flags & NodeFlags.Ambient || hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract))) { - return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); - } - } - - function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean { - // A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace - // interfaces and imports categories: - // - // DeclarationElement: - // ExportAssignment - // export_opt InterfaceDeclaration - // export_opt TypeAliasDeclaration - // export_opt ImportDeclaration - // export_opt ExternalImportDeclaration - // export_opt AmbientDeclaration - // - // TODO: The spec needs to be amended to reflect this grammar. - if (node.kind === SyntaxKind.InterfaceDeclaration || - node.kind === SyntaxKind.TypeAliasDeclaration || - node.kind === SyntaxKind.ImportDeclaration || - node.kind === SyntaxKind.ImportEqualsDeclaration || - node.kind === SyntaxKind.ExportDeclaration || - node.kind === SyntaxKind.ExportAssignment || - node.kind === SyntaxKind.NamespaceExportDeclaration || - hasModifier(node, ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) { - return false; - } - - return grammarErrorOnFirstToken(node, Diagnostics.Top_level_declarations_in_d_ts_files_must_start_with_either_a_declare_or_export_modifier); - } - - function checkGrammarTopLevelElementsForRequiredDeclareModifier(file: SourceFile): boolean { - for (const decl of file.statements) { - if (isDeclaration(decl) || decl.kind === SyntaxKind.VariableStatement) { - if (checkGrammarTopLevelElementForRequiredDeclareModifier(decl)) { - return true; - } - } - } - return false; - } - - function checkGrammarSourceFile(node: SourceFile): boolean { - return !!(node.flags & NodeFlags.Ambient) && checkGrammarTopLevelElementsForRequiredDeclareModifier(node); - } - - function checkGrammarStatementInAmbientContext(node: Node): boolean { - if (node.flags & NodeFlags.Ambient) { - // Find containing block which is either Block, ModuleBlock, SourceFile - const links = getNodeLinks(node); - if (!links.hasReportedStatementInAmbientContext && (isFunctionLike(node.parent) || isAccessor(node.parent))) { - return getNodeLinks(node).hasReportedStatementInAmbientContext = grammarErrorOnFirstToken(node, Diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts); - } - - // We are either parented by another statement, or some sort of block. - // If we're in a block, we only want to really report an error once - // to prevent noisiness. So use a bit on the block to indicate if - // this has already been reported, and don't report if it has. - // - if (node.parent.kind === SyntaxKind.Block || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { - const links = getNodeLinks(node.parent); - // Check if the containing block ever report this error - if (!links.hasReportedStatementInAmbientContext) { - return links.hasReportedStatementInAmbientContext = grammarErrorOnFirstToken(node, Diagnostics.Statements_are_not_allowed_in_ambient_contexts); - } - } - else { - // We must be parented by a statement. If so, there's no need - // to report the error as our parent will have already done it. - // Debug.assert(isStatement(node.parent)); - } - } - return false; - } - - function checkGrammarNumericLiteral(node: NumericLiteral): boolean { - // Grammar checking - if (node.numericLiteralFlags & TokenFlags.Octal) { - let diagnosticMessage: DiagnosticMessage | undefined; - if (languageVersion >= ScriptTarget.ES5) { - diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0; - } - else if (isChildOfNodeWithKind(node, SyntaxKind.LiteralType)) { - diagnosticMessage = Diagnostics.Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0; - } - else if (isChildOfNodeWithKind(node, SyntaxKind.EnumMember)) { - diagnosticMessage = Diagnostics.Octal_literals_are_not_allowed_in_enums_members_initializer_Use_the_syntax_0; - } - if (diagnosticMessage) { - const withMinus = isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.MinusToken; - const literal = (withMinus ? "-" : "") + "0o" + node.text; - return grammarErrorOnNode(withMinus ? node.parent : node, diagnosticMessage, literal); - } - } - - // Realism (size) checking - checkNumericLiteralValueSize(node); - - return false; - } - - function checkNumericLiteralValueSize(node: NumericLiteral) { - // Scientific notation (e.g. 2e54 and 1e00000000010) can't be converted to bigint - // Literals with 15 or fewer characters aren't long enough to reach past 2^53 - 1 - // Fractional numbers (e.g. 9000000000000000.001) are inherently imprecise anyway - if (node.numericLiteralFlags & TokenFlags.Scientific || node.text.length <= 15 || node.text.indexOf(".") !== -1) { - return; - } - - // We can't rely on the runtime to accurately store and compare extremely large numeric values - // Even for internal use, we use getTextOfNode: https://github.com/microsoft/TypeScript/issues/33298 - // Thus, if the runtime claims a too-large number is lower than Number.MAX_SAFE_INTEGER, - // it's likely addition operations on it will fail too - const apparentValue = +getTextOfNode(node); - if (apparentValue <= 2 ** 53 - 1 && apparentValue + 1 > apparentValue) { - return; - } - - addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(node, Diagnostics.Numeric_literals_with_absolute_values_equal_to_2_53_or_greater_are_too_large_to_be_represented_accurately_as_integers)); - } - - function checkGrammarBigIntLiteral(node: BigIntLiteral): boolean { - const literalType = isLiteralTypeNode(node.parent) || - isPrefixUnaryExpression(node.parent) && isLiteralTypeNode(node.parent.parent); - if (!literalType) { - if (languageVersion < ScriptTarget.ES2020) { - if (grammarErrorOnNode(node, Diagnostics.BigInt_literals_are_not_available_when_targeting_lower_than_ES2020)) { - return true; - } - } - } - return false; - } - - function grammarErrorAfterFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition(sourceFile, node.pos); - diagnostics.add(createFileDiagnostic(sourceFile, textSpanEnd(span), /*length*/ 0, message, arg0, arg1, arg2)); - return true; - } - return false; - } - - function getAmbientModules(): Symbol[] { - if (!ambientModulesCache) { - ambientModulesCache = []; - globals.forEach((global, sym) => { - // No need to `unescapeLeadingUnderscores`, an escaped symbol is never an ambient module. - if (ambientModuleSymbolRegex.test(sym as string)) { - ambientModulesCache!.push(global); - } - }); - } - return ambientModulesCache; - } - - function checkGrammarImportClause(node: ImportClause): boolean { - if (node.isTypeOnly && node.name && node.namedBindings) { - return grammarErrorOnNode(node, Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both); - } - return false; - } - - function checkGrammarImportCallExpression(node: ImportCall): boolean { - if (moduleKind === ModuleKind.ES2015) { - return grammarErrorOnNode(node, Diagnostics.Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_esnext_commonjs_amd_system_or_umd); - } - - if (node.typeArguments) { - return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_have_type_arguments); - } - - const nodeArguments = node.arguments; - if (nodeArguments.length !== 1) { - return grammarErrorOnNode(node, Diagnostics.Dynamic_import_must_have_one_specifier_as_an_argument); - } - checkGrammarForDisallowedTrailingComma(nodeArguments); - // see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import. - // parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed as specifier in dynamic import. - if (isSpreadElement(nodeArguments[0])) { - return grammarErrorOnNode(nodeArguments[0], Diagnostics.Specifier_of_dynamic_import_cannot_be_spread_element); - } - return false; - } - - function findMatchingTypeReferenceOrTypeAliasReference(source: Type, unionTarget: UnionOrIntersectionType) { - const sourceObjectFlags = getObjectFlags(source); - if (sourceObjectFlags & (ObjectFlags.Reference | ObjectFlags.Anonymous) && unionTarget.flags & TypeFlags.Union) { - return find(unionTarget.types, target => { - if (target.flags & TypeFlags.Object) { - const overlapObjFlags = sourceObjectFlags & getObjectFlags(target); - if (overlapObjFlags & ObjectFlags.Reference) { - return (source as TypeReference).target === (target as TypeReference).target; - } - if (overlapObjFlags & ObjectFlags.Anonymous) { - return !!(source as AnonymousType).aliasSymbol && (source as AnonymousType).aliasSymbol === (target as AnonymousType).aliasSymbol; - } - } - return false; - }); - } - } - - function findBestTypeForObjectLiteral(source: Type, unionTarget: UnionOrIntersectionType) { - if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && forEachType(unionTarget, isArrayLikeType)) { - return find(unionTarget.types, t => !isArrayLikeType(t)); - } - } - - function findBestTypeForInvokable(source: Type, unionTarget: UnionOrIntersectionType) { - let signatureKind = SignatureKind.Call; - const hasSignatures = getSignaturesOfType(source, signatureKind).length > 0 || - (signatureKind = SignatureKind.Construct, getSignaturesOfType(source, signatureKind).length > 0); - if (hasSignatures) { - return find(unionTarget.types, t => getSignaturesOfType(t, signatureKind).length > 0); - } - } - - function findMostOverlappyType(source: Type, unionTarget: UnionOrIntersectionType) { - let bestMatch: Type | undefined; - let matchingCount = 0; - for (const target of unionTarget.types) { - const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]); - if (overlap.flags & TypeFlags.Index) { - // perfect overlap of keys - bestMatch = target; - matchingCount = Infinity; - } - else if (overlap.flags & TypeFlags.Union) { - // We only want to account for literal types otherwise. - // If we have a union of index types, it seems likely that we - // needed to elaborate between two generic mapped types anyway. - const len = length(filter((overlap as UnionType).types, isUnitType)); - if (len >= matchingCount) { - bestMatch = target; - matchingCount = len; - } - } - else if (isUnitType(overlap) && 1 >= matchingCount) { - bestMatch = target; - matchingCount = 1; - } - } - return bestMatch; - } - - function filterPrimitivesIfContainsNonPrimitive(type: UnionType) { - if (maybeTypeOfKind(type, TypeFlags.NonPrimitive)) { - const result = filterType(type, t => !(t.flags & TypeFlags.Primitive)); - if (!(result.flags & TypeFlags.Never)) { - return result; - } - } - return type; - } - - // Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly - function findMatchingDiscriminantType(source: Type, target: Type, isRelatedTo: (source: Type, target: Type) => Ternary) { - if (target.flags & TypeFlags.Union && source.flags & (TypeFlags.Intersection | TypeFlags.Object)) { - const sourceProperties = getPropertiesOfType(source); - if (sourceProperties) { - const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target); - if (sourcePropertiesFiltered) { - return discriminateTypeByDiscriminableItems(target, map(sourcePropertiesFiltered, p => ([() => getTypeOfSymbol(p), p.escapedName] as [() => Type, __String])), isRelatedTo); - } - } - } - return undefined; - } - } - - function isNotAccessor(declaration: Declaration): boolean { - // Accessors check for their own matching duplicates, and in contexts where they are valid, there are already duplicate identifier checks - return !isAccessor(declaration); - } - - function isNotOverload(declaration: Declaration): boolean { - return (declaration.kind !== SyntaxKind.FunctionDeclaration && declaration.kind !== SyntaxKind.MethodDeclaration) || - !!(declaration as FunctionDeclaration).body; - } - - /** Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. */ - function isDeclarationNameOrImportPropertyName(name: Node): boolean { - switch (name.parent.kind) { - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - return isIdentifier(name); - default: - return isDeclarationName(name); - } - } - - function isSomeImportDeclaration(decl: Node): boolean { - switch (decl.kind) { - case SyntaxKind.ImportClause: // For default import - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportSpecifier: // For rename import `x as y` - return true; - case SyntaxKind.Identifier: - // For regular import, `decl` is an Identifier under the ImportSpecifier. - return decl.parent.kind === SyntaxKind.ImportSpecifier; - default: - return false; - } - } - - namespace JsxNames { - export const JSX = "JSX" as __String; - export const IntrinsicElements = "IntrinsicElements" as __String; - export const ElementClass = "ElementClass" as __String; - export const ElementAttributesPropertyNameContainer = "ElementAttributesProperty" as __String; // TODO: Deprecate and remove support - export const ElementChildrenAttributeNameContainer = "ElementChildrenAttribute" as __String; - export const Element = "Element" as __String; - export const IntrinsicAttributes = "IntrinsicAttributes" as __String; - export const IntrinsicClassAttributes = "IntrinsicClassAttributes" as __String; - export const LibraryManagedAttributes = "LibraryManagedAttributes" as __String; - } - - function getIterationTypesKeyFromIterationTypeKind(typeKind: IterationTypeKind) { - switch (typeKind) { - case IterationTypeKind.Yield: return "yieldType"; - case IterationTypeKind.Return: return "returnType"; - case IterationTypeKind.Next: return "nextType"; - } - } - - export function signatureHasRestParameter(s: Signature) { - return !!(s.flags & SignatureFlags.HasRestParameter); - } - - export function signatureHasLiteralTypes(s: Signature) { - return !!(s.flags & SignatureFlags.HasLiteralTypes); - } - -} diff --git a/tests/performance/checker_output.txt b/tests/performance/checker_output.txt deleted file mode 100644 index 3b8cb29a..00000000 --- a/tests/performance/checker_output.txt +++ /dev/null @@ -1,60929 +0,0 @@ -/* @internal */ -namespace ts { - const ambientModuleSymbolRegex = /^".+"$/; - const anon = '(anonymous)' as __String & string; - - let nextSymbolId = 1; - let nextNodeId = 1; - let nextMergeId = 1; - let nextFlowId = 1; - - const enum IterationUse { - AllowsSyncIterablesFlag = 1 << 0, - AllowsAsyncIterablesFlag = 1 << 1, - AllowsStringInputFlag = 1 << 2, - ForOfFlag = 1 << 3, - YieldStarFlag = 1 << 4, - SpreadFlag = 1 << 5, - DestructuringFlag = 1 << 6, - - // Spread, Destructuring, Array element assignment - Element = AllowsSyncIterablesFlag, - Spread = AllowsSyncIterablesFlag | SpreadFlag, - Destructuring = AllowsSyncIterablesFlag | DestructuringFlag, - - ForOf = AllowsSyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, - ForAwaitOf = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag - | AllowsStringInputFlag | ForOfFlag, - - YieldStar = AllowsSyncIterablesFlag | YieldStarFlag, - AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag - | YieldStarFlag, - - GeneratorReturnType = AllowsSyncIterablesFlag, - AsyncGeneratorReturnType = AllowsAsyncIterablesFlag - } - - const enum IterationTypeKind { - Yield, - Return, - Next - } - - interface IterationTypesResolver { - iterableCacheKey: 'iterationTypesOfAsyncIterable' - | 'iterationTypesOfIterable'; - iteratorCacheKey: 'iterationTypesOfAsyncIterator' - | 'iterationTypesOfIterator'; - iteratorSymbolName: 'asyncIterator' | 'iterator'; - getGlobalIteratorType: (reportErrors: boolean) => GenericType; - getGlobalIterableType: (reportErrors: boolean) => GenericType; - getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType; - getGlobalGeneratorType: (reportErrors: boolean) => GenericType; - resolveIterationType: (type: Type, errorNode: Node | undefined) => Type - | undefined; - mustHaveANextMethodDiagnostic: DiagnosticMessage; - mustBeAMethodDiagnostic: DiagnosticMessage; - mustHaveAValueDiagnostic: DiagnosticMessage; - } - - const enum WideningKind { - Normal, - GeneratorYield - } - - const enum TypeFacts { - None = 0, - TypeofEQString = 1 << 0, // typeof x === "string" - TypeofEQNumber = 1 << 1, // typeof x === "number" - TypeofEQBigInt = 1 << 2, // typeof x === "bigint" - TypeofEQBoolean = 1 << 3, // typeof x === "boolean" - TypeofEQSymbol = 1 << 4, // typeof x === "symbol" - TypeofEQObject = 1 << 5, // typeof x === "object" - TypeofEQFunction = 1 << 6, // typeof x === "function" - TypeofEQHostObject = 1 << 7, // typeof x === "xxx" - TypeofNEString = 1 << 8, // typeof x !== "string" - TypeofNENumber = 1 << 9, // typeof x !== "number" - TypeofNEBigInt = 1 << 10, // typeof x !== "bigint" - TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" - TypeofNESymbol = 1 << 12, // typeof x !== "symbol" - TypeofNEObject = 1 << 13, // typeof x !== "object" - TypeofNEFunction = 1 << 14, // typeof x !== "function" - TypeofNEHostObject = 1 << 15, // typeof x !== "xxx" - EQUndefined = 1 << 16, // x === undefined - EQNull = 1 << 17, // x === null - EQUndefinedOrNull = 1 << 18, // x === undefined / x === null - NEUndefined = 1 << 19, // x !== undefined - NENull = 1 << 20, // x !== null - NEUndefinedOrNull = 1 << 21, // x != undefined / x != null - Truthy = 1 << 22, // x - Falsy = 1 << 23, // !x - All = (1 << 24) - 1, - // The following members encode facts about particular kinds of types for use in the getTypeFacts function. - // The presence of a particular fact means that the given test is true for some (and possibly all) values - // of that kind of type. - BaseStringStrictFacts = TypeofEQString | TypeofNENumber - | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol - | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject - | NEUndefined | NENull | NEUndefinedOrNull, - BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy, - StringFacts = BaseStringFacts | Truthy, - EmptyStringStrictFacts = BaseStringStrictFacts | Falsy, - EmptyStringFacts = BaseStringFacts, - NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy, - NonEmptyStringFacts = BaseStringFacts | Truthy, - BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString - | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol - | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject - | NEUndefined | NENull | NEUndefinedOrNull, - BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy, - NumberFacts = BaseNumberFacts | Truthy, - ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy, - ZeroNumberFacts = BaseNumberFacts, - NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy, - NonZeroNumberFacts = BaseNumberFacts | Truthy, - BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString - | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol - | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject - | NEUndefined | NENull | NEUndefinedOrNull, - BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy, - BigIntFacts = BaseBigIntFacts | Truthy, - ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy, - ZeroBigIntFacts = BaseBigIntFacts, - NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy, - NonZeroBigIntFacts = BaseBigIntFacts | Truthy, - BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString - | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject - | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull - | NEUndefinedOrNull, - BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy, - BooleanFacts = BaseBooleanFacts | Truthy, - FalseStrictFacts = BaseBooleanStrictFacts | Falsy, - FalseFacts = BaseBooleanFacts, - TrueStrictFacts = BaseBooleanStrictFacts | Truthy, - TrueFacts = BaseBooleanFacts | Truthy, - SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber - | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject - | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull - | NEUndefinedOrNull | Truthy, - SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject - | TypeofNEString | TypeofNENumber | TypeofNEBigInt - | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined - | NENull | NEUndefinedOrNull | Truthy, - ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject - | TypeofNEString | TypeofNENumber | TypeofNEBigInt - | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined - | NENull | NEUndefinedOrNull | Truthy, - FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull - | EQUndefinedOrNull | Falsy, - UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt - | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject - | TypeofNEFunction | TypeofNEHostObject | EQUndefined - | EQUndefinedOrNull | NENull | Falsy, - NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber - | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol - | TypeofNEFunction | TypeofNEHostObject | EQNull - | EQUndefinedOrNull | NEUndefined | Falsy, - EmptyObjectStrictFacts = All - & ~(EQUndefined | EQNull | EQUndefinedOrNull), - EmptyObjectFacts = All - } - - const typeofEQFacts: ReadonlyMap = createMapFromTemplate({ - string: TypeFacts.TypeofEQString, - number: TypeFacts.TypeofEQNumber, - bigint: TypeFacts.TypeofEQBigInt, - boolean: TypeFacts.TypeofEQBoolean, - symbol: TypeFacts.TypeofEQSymbol, - undefined: TypeFacts.EQUndefined, - object: TypeFacts.TypeofEQObject, - function: TypeFacts.TypeofEQFunction - }); - - const typeofNEFacts: ReadonlyMap = createMapFromTemplate({ - string: TypeFacts.TypeofNEString, - number: TypeFacts.TypeofNENumber, - bigint: TypeFacts.TypeofNEBigInt, - boolean: TypeFacts.TypeofNEBoolean, - symbol: TypeFacts.TypeofNESymbol, - undefined: TypeFacts.NEUndefined, - object: TypeFacts.TypeofNEObject, - function: TypeFacts.TypeofNEFunction - }); - - type TypeSystemEntity = Node | Symbol | Type | Signature; - - const enum TypeSystemPropertyName { - Type, - ResolvedBaseConstructorType, - DeclaredType, - ResolvedReturnType, - ImmediateBaseConstraint, - EnumTagType, - JSDocTypeReference, - ResolvedTypeArguments - } - - const enum CheckMode { - Normal = 0, // Normal type checking - Contextual = 1 - << 0, // Explicitly assigned contextual type, therefore not cacheable - Inferential = 1 << 1, // Inferential typing - SkipContextSensitive = 1 - << 2, // Skip context sensitive function expressions - SkipGenericFunctions = 1 - << 3, // Skip single signature generic functions - IsForSignatureHelp = 1 - << 4 // Call resolution for purposes of signature help - } - - const enum AccessFlags { - None = 0, - NoIndexSignatures = 1 << 0, - Writing = 1 << 1, - CacheSymbol = 1 << 2, - NoTupleBoundsCheck = 1 << 3 - } - - const enum SignatureCheckMode { - BivariantCallback = 1 << 0, - StrictCallback = 1 << 1, - IgnoreReturnTypes = 1 << 2, - StrictArity = 1 << 3, - Callback = BivariantCallback | StrictCallback - } - - const enum MappedTypeModifiers { - IncludeReadonly = 1 << 0, - ExcludeReadonly = 1 << 1, - IncludeOptional = 1 << 2, - ExcludeOptional = 1 << 3 - } - - const enum ExpandingFlags { - None = 0, - Source = 1, - Target = 1 << 1, - Both = Source | Target - } - - const enum MembersOrExportsResolutionKind { - resolvedExports = 'resolvedExports', - resolvedMembers = 'resolvedMembers' - } - - const enum UnusedKind { - Local, - Parameter - } - - /** @param containingNode Node to check for parse error */ - type AddUnusedDiagnostic = ( - containingNode: Node, - type: UnusedKind, - diagnostic: DiagnosticWithLocation - ) => void; - - const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor); - - const enum DeclarationMeaning { - GetAccessor = 1, - SetAccessor = 2, - PropertyAssignment = 4, - Method = 8, - GetOrSetAccessor = GetAccessor | SetAccessor, - PropertyAssignmentOrMethod = PropertyAssignment | Method - } - - const enum DeclarationSpaces { - None = 0, - ExportValue = 1 << 0, - ExportType = 1 << 1, - ExportNamespace = 1 << 2 - } - - export function getNodeId(node: Node): number { - if (!node.id) { - node.id = nextNodeId; - nextNodeId++; - } - return node.id; - } - - export function getSymbolId(symbol: Symbol): number { - if (!symbol.id) { - symbol.id = nextSymbolId; - nextSymbolId++; - } - - return symbol.id; - } - - export function isInstantiatedModule( - node: ModuleDeclaration, - preserveConstEnums: boolean - ) { - const moduleState = getModuleInstanceState(node); - return moduleState === ModuleInstanceState.Instantiated - || (preserveConstEnums - && moduleState === ModuleInstanceState.ConstEnumOnly); - } - - export function createTypeChecker( - host: TypeCheckerHost, - produceDiagnostics: boolean - ): TypeChecker { - const getPackagesSet: () => Map = memoize(() => { - const set = createMap(); - host.getSourceFiles().forEach(sf => { - if (!sf.resolvedModules) return; - - forEachEntry(sf.resolvedModules, r => { - if (r && r.packageId) set.set(r.packageId.name, true); - }); - }); - return set; - }); - - // Cancellation that controls whether or not we can cancel in the middle of type checking. - // In general cancelling is *not* safe for the type checker. We might be in the middle of - // computing something, and we will leave our internals in an inconsistent state. Callers - // who set the cancellation token should catch if a cancellation exception occurs, and - // should throw away and create a new TypeChecker. - // - // Currently we only support setting the cancellation token when getting diagnostics. This - // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if - // they no longer need the information (for example, if the user started editing again). - let cancellationToken: CancellationToken | undefined; - let requestedExternalEmitHelpers: ExternalEmitHelpers; - let externalHelpersModule: Symbol; - - const Symbol = objectAllocator.getSymbolConstructor(); - const Type = objectAllocator.getTypeConstructor(); - const Signature = objectAllocator.getSignatureConstructor(); - - let typeCount = 0; - let symbolCount = 0; - let enumCount = 0; - let instantiationCount = 0; - let instantiationDepth = 0; - let constraintDepth = 0; - let currentNode: Node | undefined; - - const emptySymbols = createSymbolTable(); - const identityMapper: (type: Type) => Type = identity; - - const compilerOptions = host.getCompilerOptions(); - const languageVersion = getEmitScriptTarget(compilerOptions); - const moduleKind = getEmitModuleKind(compilerOptions); - const allowSyntheticDefaultImports = - getAllowSyntheticDefaultImports(compilerOptions); - const strictNullChecks = getStrictOptionValue( - compilerOptions, - 'strictNullChecks' - ); - const strictFunctionTypes = getStrictOptionValue( - compilerOptions, - 'strictFunctionTypes' - ); - const strictBindCallApply = getStrictOptionValue( - compilerOptions, - 'strictBindCallApply' - ); - const strictPropertyInitialization = getStrictOptionValue( - compilerOptions, - 'strictPropertyInitialization' - ); - const noImplicitAny = getStrictOptionValue( - compilerOptions, - 'noImplicitAny' - ); - const noImplicitThis = getStrictOptionValue( - compilerOptions, - 'noImplicitThis' - ); - const keyofStringsOnly = !!compilerOptions.keyofStringsOnly; - const freshObjectLiteralFlag = - compilerOptions.suppressExcessPropertyErrors - ? 0 - : ObjectFlags.FreshLiteral; - - const emitResolver = createResolver(); - const nodeBuilder = createNodeBuilder(); - - const globals = createSymbolTable(); - const undefinedSymbol = createSymbol( - SymbolFlags.Property, - 'undefined' as __String - ); - undefinedSymbol.declarations = []; - - const globalThisSymbol = createSymbol( - SymbolFlags.Module, - 'globalThis' as __String, - CheckFlags.Readonly - ); - globalThisSymbol.exports = globals; - globalThisSymbol.declarations = []; - globals.set(globalThisSymbol.escapedName, globalThisSymbol); - - const argumentsSymbol = createSymbol( - SymbolFlags.Property, - 'arguments' as __String - ); - const requireSymbol = createSymbol( - SymbolFlags.Property, - 'require' as __String - ); - - /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ - let apparentArgumentCount: number | undefined; - - // for public members that accept a Node or one of its subtypes, we must guard against - // synthetic nodes created during transformations by calling `getParseTreeNode`. - // for most of these, we perform the guard only on `checker` to avoid any possible - // extra cost of calling `getParseTreeNode` when calling these functions from inside the - // checker. - const checker: TypeChecker = { - getNodeCount: () => sum(host.getSourceFiles(), 'nodeCount'), - getIdentifierCount: () => sum( - host.getSourceFiles(), - 'identifierCount' - ), - getSymbolCount: () => sum(host.getSourceFiles(), 'symbolCount') - + symbolCount, - getTypeCount: () => typeCount, - getRelationCacheSizes: () => ({ - assignable: assignableRelation.size, - identity: identityRelation.size, - subtype: subtypeRelation.size, - strictSubtype: strictSubtypeRelation.size - }), - isUndefinedSymbol: symbol => symbol === undefinedSymbol, - isArgumentsSymbol: symbol => symbol === argumentsSymbol, - isUnknownSymbol: symbol => symbol === unknownSymbol, - getMergedSymbol, - getDiagnostics, - getGlobalDiagnostics, - getTypeOfSymbolAtLocation: (symbol, location) => { - location = getParseTreeNode(location); - return location - ? getTypeOfSymbolAtLocation(symbol, location) - : errorType; - }, - getSymbolsOfParameterPropertyDeclaration: ( - parameterIn, - parameterName - ) => { - const parameter = getParseTreeNode(parameterIn, isParameter); - if (parameter === undefined) { - return Debug - .fail('Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node.'); - } - return getSymbolsOfParameterPropertyDeclaration( - parameter, - escapeLeadingUnderscores(parameterName) - ); - }, - getDeclaredTypeOfSymbol, - getPropertiesOfType, - getPropertyOfType: (type, name) => getPropertyOfType( - type, - escapeLeadingUnderscores(name) - ), - getPrivateIdentifierPropertyOfType: ( - leftType: Type, - name: string, - location: Node - ) => { - const node = getParseTreeNode(location); - if (!node) { - return undefined; - } - const propName = escapeLeadingUnderscores(name); - const lexicallyScopedIdentifier = - lookupSymbolForPrivateIdentifierDeclaration( - propName, - node - ); - return lexicallyScopedIdentifier - ? getPrivateIdentifierPropertyOfType( - leftType, - lexicallyScopedIdentifier - ) - : undefined; - }, - getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType( - type, - escapeLeadingUnderscores(name) - ), - getIndexInfoOfType, - getSignaturesOfType, - getIndexTypeOfType, - getBaseTypes, - getBaseTypeOfLiteralType, - getWidenedType, - getTypeFromTypeNode: nodeIn => { - const node = getParseTreeNode(nodeIn, isTypeNode); - return node ? getTypeFromTypeNode(node) : errorType; - }, - getParameterType: getTypeAtPosition, - getPromisedTypeOfPromise, - getReturnTypeOfSignature, - isNullableType, - getNullableType, - getNonNullableType, - getNonOptionalType: removeOptionalTypeMarker, - getTypeArguments, - typeToTypeNode: nodeBuilder.typeToTypeNode, - indexInfoToIndexSignatureDeclaration: nodeBuilder - .indexInfoToIndexSignatureDeclaration, - signatureToSignatureDeclaration: nodeBuilder - .signatureToSignatureDeclaration, - symbolToEntityName: nodeBuilder.symbolToEntityName, - symbolToExpression: nodeBuilder.symbolToExpression, - symbolToTypeParameterDeclarations: nodeBuilder - .symbolToTypeParameterDeclarations, - symbolToParameterDeclaration: nodeBuilder - .symbolToParameterDeclaration, - typeParameterToDeclaration: nodeBuilder.typeParameterToDeclaration, - getSymbolsInScope: (location, meaning) => { - location = getParseTreeNode(location); - return location ? getSymbolsInScope(location, meaning) : []; - }, - getSymbolAtLocation: node => { - node = getParseTreeNode(node); - return node ? getSymbolAtLocation(node) : undefined; - }, - getShorthandAssignmentValueSymbol: node => { - node = getParseTreeNode(node); - return node - ? getShorthandAssignmentValueSymbol(node) - : undefined; - }, - getExportSpecifierLocalTargetSymbol: nodeIn => { - const node = getParseTreeNode(nodeIn, isExportSpecifier); - return node - ? getExportSpecifierLocalTargetSymbol(node) - : undefined; - }, - getExportSymbolOfSymbol(symbol) { - return getMergedSymbol(symbol.exportSymbol || symbol); - }, - getTypeAtLocation: node => { - node = getParseTreeNode(node); - return node ? getTypeOfNode(node) : errorType; - }, - getTypeOfAssignmentPattern: nodeIn => { - const node = getParseTreeNode(nodeIn, isAssignmentPattern); - return node && getTypeOfAssignmentPattern(node) || errorType; - }, - getPropertySymbolOfDestructuringAssignment: locationIn => { - const location = getParseTreeNode(locationIn, isIdentifier); - return location - ? getPropertySymbolOfDestructuringAssignment(location) - : undefined; - }, - signatureToString: ( - signature, - enclosingDeclaration, - flags, - kind - ) => { - return signatureToString( - signature, - getParseTreeNode(enclosingDeclaration), - flags, - kind - ); - }, - typeToString: (type, enclosingDeclaration, flags) => { - return typeToString( - type, - getParseTreeNode(enclosingDeclaration), - flags - ); - }, - symbolToString: (symbol, enclosingDeclaration, meaning, flags) => { - return symbolToString( - symbol, - getParseTreeNode(enclosingDeclaration), - meaning, - flags - ); - }, - typePredicateToString: ( - predicate, - enclosingDeclaration, - flags - ) => { - return typePredicateToString( - predicate, - getParseTreeNode(enclosingDeclaration), - flags - ); - }, - writeSignature: ( - signature, - enclosingDeclaration, - flags, - kind, - writer - ) => { - return signatureToString( - signature, - getParseTreeNode(enclosingDeclaration), - flags, - kind, - writer - ); - }, - writeType: (type, enclosingDeclaration, flags, writer) => { - return typeToString( - type, - getParseTreeNode(enclosingDeclaration), - flags, - writer - ); - }, - writeSymbol: ( - symbol, - enclosingDeclaration, - meaning, - flags, - writer - ) => { - return symbolToString( - symbol, - getParseTreeNode(enclosingDeclaration), - meaning, - flags, - writer - ); - }, - writeTypePredicate: ( - predicate, - enclosingDeclaration, - flags, - writer - ) => { - return typePredicateToString( - predicate, - getParseTreeNode(enclosingDeclaration), - flags, - writer - ); - }, - getAugmentedPropertiesOfType, - getRootSymbols, - getContextualType: ( - nodeIn: Expression, - contextFlags?: ContextFlags - ) => { - const node = getParseTreeNode(nodeIn, isExpression); - return node - ? getContextualType(node, contextFlags) - : undefined; - }, - getContextualTypeForObjectLiteralElement: nodeIn => { - const node = getParseTreeNode( - nodeIn, - isObjectLiteralElementLike - ); - return node - ? getContextualTypeForObjectLiteralElement(node) - : undefined; - }, - getContextualTypeForArgumentAtIndex: (nodeIn, argIndex) => { - const node = getParseTreeNode(nodeIn, isCallLikeExpression); - return node - && getContextualTypeForArgumentAtIndex(node, argIndex); - }, - getContextualTypeForJsxAttribute: (nodeIn) => { - const node = getParseTreeNode(nodeIn, isJsxAttributeLike); - return node && getContextualTypeForJsxAttribute(node); - }, - isContextSensitive, - getFullyQualifiedName, - getResolvedSignature: ( - node, - candidatesOutArray, - argumentCount - ) => getResolvedSignatureWorker( - node, - candidatesOutArray, - argumentCount, - CheckMode.Normal - ), - getResolvedSignatureForSignatureHelp: ( - node, - candidatesOutArray, - argumentCount - ) => getResolvedSignatureWorker( - node, - candidatesOutArray, - argumentCount, - CheckMode.IsForSignatureHelp - ), - getExpandedParameters, - hasEffectiveRestParameter, - getConstantValue: nodeIn => { - const node = getParseTreeNode(nodeIn, canHaveConstantValue); - return node ? getConstantValue(node) : undefined; - }, - isValidPropertyAccess: (nodeIn, propertyName) => { - const node = getParseTreeNode( - nodeIn, - isPropertyAccessOrQualifiedNameOrImportTypeNode - ); - return !!node - && isValidPropertyAccess( - node, - escapeLeadingUnderscores(propertyName) - ); - }, - isValidPropertyAccessForCompletions: (nodeIn, type, property) => { - const node = getParseTreeNode( - nodeIn, - isPropertyAccessExpression - ); - return !!node - && isValidPropertyAccessForCompletions( - node, - type, - property - ); - }, - getSignatureFromDeclaration: declarationIn => { - const declaration = getParseTreeNode( - declarationIn, - isFunctionLike - ); - return declaration - ? getSignatureFromDeclaration(declaration) - : undefined; - }, - isImplementationOfOverload: node => { - const parsed = getParseTreeNode(node, isFunctionLike); - return parsed ? isImplementationOfOverload(parsed) : undefined; - }, - getImmediateAliasedSymbol, - getAliasedSymbol: resolveAlias, - getEmitResolver, - getExportsOfModule: getExportsOfModuleAsArray, - getExportsAndPropertiesOfModule, - getSymbolWalker: createGetSymbolWalker( - getRestTypeOfSignature, - getTypePredicateOfSignature, - getReturnTypeOfSignature, - getBaseTypes, - resolveStructuredTypeMembers, - getTypeOfSymbol, - getResolvedSymbol, - getIndexTypeOfStructuredType, - getConstraintOfTypeParameter, - getFirstIdentifier, - getTypeArguments - ), - getAmbientModules, - getJsxIntrinsicTagNamesAt, - isOptionalParameter: nodeIn => { - const node = getParseTreeNode(nodeIn, isParameter); - return node ? isOptionalParameter(node) : false; - }, - tryGetMemberInModuleExports: ( - name, - symbol - ) => tryGetMemberInModuleExports( - escapeLeadingUnderscores(name), - symbol - ), - tryGetMemberInModuleExportsAndProperties: ( - name, - symbol - ) => tryGetMemberInModuleExportsAndProperties( - escapeLeadingUnderscores(name), - symbol - ), - tryFindAmbientModuleWithoutAugmentations: moduleName => { - // we deliberately exclude augmentations - // since we are only interested in declarations of the module itself - return tryFindAmbientModule( - moduleName, /*withAugmentations*/ - false - ); - }, - getApparentType, - getUnionType, - isTypeAssignableTo: (source, target) => { - return isTypeAssignableTo(source, target); - }, - createAnonymousType, - createSignature, - createSymbol, - createIndexInfo, - getAnyType: () => anyType, - getStringType: () => stringType, - getNumberType: () => numberType, - createPromiseType, - createArrayType, - getElementTypeOfArrayType, - getBooleanType: () => booleanType, - getFalseType: (fresh?) => fresh ? falseType : regularFalseType, - getTrueType: (fresh?) => fresh ? trueType : regularTrueType, - getVoidType: () => voidType, - getUndefinedType: () => undefinedType, - getNullType: () => nullType, - getESSymbolType: () => esSymbolType, - getNeverType: () => neverType, - getOptionalType: () => optionalType, - isSymbolAccessible, - isArrayType, - isTupleType, - isArrayLikeType, - isTypeInvalidDueToUnionDiscriminant, - getAllPossiblePropertiesOfTypes, - getSuggestionForNonexistentProperty: ( - node, - type - ) => getSuggestionForNonexistentProperty(node, type), - getSuggestionForNonexistentSymbol: ( - location, - name, - meaning - ) => getSuggestionForNonexistentSymbol( - location, - escapeLeadingUnderscores(name), - meaning - ), - getSuggestionForNonexistentExport: ( - node, - target - ) => getSuggestionForNonexistentExport(node, target), - getBaseConstraintOfType, - getDefaultFromTypeParameter: - type => type && type.flags & TypeFlags.TypeParameter - ? getDefaultFromTypeParameter(type as TypeParameter) - : undefined, - resolveName(name, location, meaning, excludeGlobals) { - return resolveName( - location, - escapeLeadingUnderscores(name), - meaning, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false, - excludeGlobals - ); - }, - getJsxNamespace: - n => unescapeLeadingUnderscores(getJsxNamespace(n)), - getAccessibleSymbolChain, - getTypePredicateOfSignature, - resolveExternalModuleSymbol, - tryGetThisTypeAt: (node, includeGlobalThis) => { - node = getParseTreeNode(node); - return node && tryGetThisTypeAt(node, includeGlobalThis); - }, - getTypeArgumentConstraint: nodeIn => { - const node = getParseTreeNode(nodeIn, isTypeNode); - return node && getTypeArgumentConstraint(node); - }, - getSuggestionDiagnostics: (file, ct) => { - if (skipTypeChecking(file, compilerOptions, host)) { - return emptyArray; - } - - let diagnostics: DiagnosticWithLocation[] | undefined; - try { - // Record the cancellation token so it can be checked later on during checkSourceElement. - // Do this in a finally block so we can ensure that it gets reset back to nothing after - // this call is done. - cancellationToken = ct; - - // Ensure file is type checked - checkSourceFile(file); - Debug - .assert(!!(getNodeLinks(file).flags - & NodeCheckFlags.TypeChecked)); - - diagnostics = addRange( - diagnostics, - suggestionDiagnostics.getDiagnostics(file.fileName) - ); - checkUnusedIdentifiers( - getPotentiallyUnusedIdentifiers(file), - (containingNode, kind, diag) => { - if (!containsParseError(containingNode) - && !unusedIsError( - kind, - !!(containingNode.flags - & NodeFlags.Ambient) - )) - { - (diagnostics || (diagnostics = [])) - .push({ ...diag, - category: DiagnosticCategory - .Suggestion }); - } - } - ); - - return diagnostics || emptyArray; - } finally { - cancellationToken = undefined; - } - }, - - runWithCancellationToken: (token, callback) => { - try { - cancellationToken = token; - return callback(checker); - } finally { - cancellationToken = undefined; - } - }, - - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, - isDeclarationVisible - }; - - function getResolvedSignatureWorker( - nodeIn: CallLikeExpression, - candidatesOutArray: Signature[] | undefined, - argumentCount: number | undefined, - checkMode: CheckMode - ): Signature | undefined { - const node = getParseTreeNode(nodeIn, isCallLikeExpression); - apparentArgumentCount = argumentCount; - const res = node - ? getResolvedSignature(node, candidatesOutArray, checkMode) - : undefined; - apparentArgumentCount = undefined; - return res; - } - - const tupleTypes = createMap(); - const unionTypes = createMap(); - const intersectionTypes = createMap(); - const literalTypes = createMap(); - const indexedAccessTypes = createMap(); - const substitutionTypes = createMap(); - const evolvingArrayTypes: EvolvingArrayType[] = []; - const undefinedProperties = - createMap() as UnderscoreEscapedMap; - - const unknownSymbol = createSymbol( - SymbolFlags.Property, - 'unknown' as __String - ); - const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving); - - const anyType = createIntrinsicType(TypeFlags.Any, 'any'); - const autoType = createIntrinsicType(TypeFlags.Any, 'any'); - const wildcardType = createIntrinsicType(TypeFlags.Any, 'any'); - const errorType = createIntrinsicType(TypeFlags.Any, 'error'); - const nonInferrableAnyType = createIntrinsicType( - TypeFlags.Any, - 'any', - ObjectFlags.ContainsWideningType - ); - const unknownType = createIntrinsicType(TypeFlags.Unknown, 'unknown'); - const undefinedType = createIntrinsicType( - TypeFlags.Undefined, - 'undefined' - ); - const undefinedWideningType = strictNullChecks - ? undefinedType - : createIntrinsicType( - TypeFlags.Undefined, - 'undefined', - ObjectFlags.ContainsWideningType - ); - const optionalType = createIntrinsicType( - TypeFlags.Undefined, - 'undefined' - ); - const nullType = createIntrinsicType(TypeFlags.Null, 'null'); - const nullWideningType = strictNullChecks - ? nullType - : createIntrinsicType( - TypeFlags.Null, - 'null', - ObjectFlags.ContainsWideningType - ); - const stringType = createIntrinsicType(TypeFlags.String, 'string'); - const numberType = createIntrinsicType(TypeFlags.Number, 'number'); - const bigintType = createIntrinsicType(TypeFlags.BigInt, 'bigint'); - const falseType = createIntrinsicType( - TypeFlags.BooleanLiteral, - 'false' - ) as FreshableIntrinsicType; - const regularFalseType = createIntrinsicType( - TypeFlags.BooleanLiteral, - 'false' - ) as FreshableIntrinsicType; - const trueType = createIntrinsicType( - TypeFlags.BooleanLiteral, - 'true' - ) as FreshableIntrinsicType; - const regularTrueType = createIntrinsicType( - TypeFlags.BooleanLiteral, - 'true' - ) as FreshableIntrinsicType; - trueType.regularType = regularTrueType; - trueType.freshType = trueType; - regularTrueType.regularType = regularTrueType; - regularTrueType.freshType = trueType; - falseType.regularType = regularFalseType; - falseType.freshType = falseType; - regularFalseType.regularType = regularFalseType; - regularFalseType.freshType = falseType; - const booleanType = - createBooleanType([regularFalseType, regularTrueType]); - // Also mark all combinations of fresh/regular booleans as "Boolean" so they print as `boolean` instead of `true | false` - // (The union is cached, so simply doing the marking here is sufficient) - createBooleanType([regularFalseType, trueType]); - createBooleanType([falseType, regularTrueType]); - createBooleanType([falseType, trueType]); - const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, 'symbol'); - const voidType = createIntrinsicType(TypeFlags.Void, 'void'); - const neverType = createIntrinsicType(TypeFlags.Never, 'never'); - const silentNeverType = createIntrinsicType(TypeFlags.Never, 'never'); - const nonInferrableType = createIntrinsicType( - TypeFlags.Never, - 'never', - ObjectFlags.NonInferrableType - ); - const implicitNeverType = createIntrinsicType( - TypeFlags.Never, - 'never' - ); - const unreachableNeverType = createIntrinsicType( - TypeFlags.Never, - 'never' - ); - const nonPrimitiveType = createIntrinsicType( - TypeFlags.NonPrimitive, - 'object' - ); - const stringNumberSymbolType = - getUnionType([stringType, numberType, esSymbolType]); - const keyofConstraintType = keyofStringsOnly - ? stringType - : stringNumberSymbolType; - const numberOrBigIntType = getUnionType([numberType, bigintType]); - - const emptyObjectType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - const emptyJsxObjectType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - emptyJsxObjectType.objectFlags |= ObjectFlags.JsxAttributes; - - const emptyTypeLiteralSymbol = createSymbol( - SymbolFlags.TypeLiteral, - InternalSymbolName.Type - ); - emptyTypeLiteralSymbol.members = createSymbolTable(); - const emptyTypeLiteralType = createAnonymousType( - emptyTypeLiteralSymbol, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - - const emptyGenericType = - createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - emptyGenericType.instantiations = createMap(); - - const anyFunctionType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated - // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes. - anyFunctionType.objectFlags |= ObjectFlags.NonInferrableType; - - const noConstraintType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - const circularConstraintType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - const resolvingDefaultType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - - const markerSuperType = createTypeParameter(); - const markerSubType = createTypeParameter(); - markerSubType.constraint = markerSuperType; - const markerOtherType = createTypeParameter(); - - const noTypePredicate = createTypePredicate( - TypePredicateKind.Identifier, - '<>', - 0, - anyType - ); - - const anySignature = createSignature( - undefined, - undefined, - undefined, - emptyArray, - anyType, /*resolvedTypePredicate*/ - undefined, - 0, - SignatureFlags.None - ); - const unknownSignature = createSignature( - undefined, - undefined, - undefined, - emptyArray, - errorType, /*resolvedTypePredicate*/ - undefined, - 0, - SignatureFlags.None - ); - const resolvingSignature = createSignature( - undefined, - undefined, - undefined, - emptyArray, - anyType, /*resolvedTypePredicate*/ - undefined, - 0, - SignatureFlags.None - ); - const silentNeverSignature = createSignature( - undefined, - undefined, - undefined, - emptyArray, - silentNeverType, /*resolvedTypePredicate*/ - undefined, - 0, - SignatureFlags.None - ); - - const enumNumberIndexInfo = createIndexInfo( - stringType, /*isReadonly*/ - true - ); - - const iterationTypesCache = - createMap(); // cache for common IterationTypes instances - const noIterationTypes: IterationTypes = { - get yieldType(): Type { - throw new Error('Not supported'); - }, - get returnType(): Type { - throw new Error('Not supported'); - }, - get nextType(): Type { - throw new Error('Not supported'); - } - }; - const anyIterationTypes = createIterationTypes( - anyType, - anyType, - anyType - ); - const anyIterationTypesExceptNext = createIterationTypes( - anyType, - anyType, - unknownType - ); - const defaultIterationTypes = createIterationTypes( - neverType, - anyType, - undefinedType - ); // default iteration types for `Iterator`. - - const asyncIterationTypesResolver: IterationTypesResolver = { - iterableCacheKey: 'iterationTypesOfAsyncIterable', - iteratorCacheKey: 'iterationTypesOfAsyncIterator', - iteratorSymbolName: 'asyncIterator', - getGlobalIteratorType: getGlobalAsyncIteratorType, - getGlobalIterableType: getGlobalAsyncIterableType, - getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType, - getGlobalGeneratorType: getGlobalAsyncGeneratorType, - resolveIterationType: getAwaitedType, - mustHaveANextMethodDiagnostic: Diagnostics - .An_async_iterator_must_have_a_next_method, - mustBeAMethodDiagnostic: Diagnostics - .The_0_property_of_an_async_iterator_must_be_a_method, - mustHaveAValueDiagnostic: Diagnostics - .The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property - }; - - const syncIterationTypesResolver: IterationTypesResolver = { - iterableCacheKey: 'iterationTypesOfIterable', - iteratorCacheKey: 'iterationTypesOfIterator', - iteratorSymbolName: 'iterator', - getGlobalIteratorType, - getGlobalIterableType, - getGlobalIterableIteratorType, - getGlobalGeneratorType, - resolveIterationType: (type, _errorNode) => type, - mustHaveANextMethodDiagnostic: Diagnostics - .An_iterator_must_have_a_next_method, - mustBeAMethodDiagnostic: Diagnostics - .The_0_property_of_an_iterator_must_be_a_method, - mustHaveAValueDiagnostic: Diagnostics - .The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property - }; - - interface DuplicateInfoForSymbol { - readonly firstFileLocations: Node[]; - readonly secondFileLocations: Node[]; - readonly isBlockScoped: boolean; - } - interface DuplicateInfoForFiles { - readonly firstFile: SourceFile; - readonly secondFile: SourceFile; - /** Key is symbol name. */ - readonly conflictingSymbols: Map; - } - /** Key is "/path/to/a.ts|/path/to/b.ts". */ - let amalgamatedDuplicates: Map | undefined; - const reverseMappedCache = createMap(); - let ambientModulesCache: Symbol[] | undefined; - /** - * List of every ambient module with a "*" wildcard. - * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. - * This is only used if there is no exact match. - */ - let patternAmbientModules: PatternAmbientModule[]; - let patternAmbientModuleAugmentations: Map | undefined; - - let globalObjectType: ObjectType; - let globalFunctionType: ObjectType; - let globalCallableFunctionType: ObjectType; - let globalNewableFunctionType: ObjectType; - let globalArrayType: GenericType; - let globalReadonlyArrayType: GenericType; - let globalStringType: ObjectType; - let globalNumberType: ObjectType; - let globalBooleanType: ObjectType; - let globalRegExpType: ObjectType; - let globalThisType: GenericType; - let anyArrayType: Type; - let autoArrayType: Type; - let anyReadonlyArrayType: Type; - let deferredGlobalNonNullableTypeAlias: Symbol; - - // The library files are only loaded when the feature is used. - // This allows users to just specify library files they want to used through --lib - // and they will not get an error from not having unrelated library files - let deferredGlobalESSymbolConstructorSymbol: Symbol | undefined; - let deferredGlobalESSymbolType: ObjectType; - let deferredGlobalTypedPropertyDescriptorType: GenericType; - let deferredGlobalPromiseType: GenericType; - let deferredGlobalPromiseLikeType: GenericType; - let deferredGlobalPromiseConstructorSymbol: Symbol | undefined; - let deferredGlobalPromiseConstructorLikeType: ObjectType; - let deferredGlobalIterableType: GenericType; - let deferredGlobalIteratorType: GenericType; - let deferredGlobalIterableIteratorType: GenericType; - let deferredGlobalGeneratorType: GenericType; - let deferredGlobalIteratorYieldResultType: GenericType; - let deferredGlobalIteratorReturnResultType: GenericType; - let deferredGlobalAsyncIterableType: GenericType; - let deferredGlobalAsyncIteratorType: GenericType; - let deferredGlobalAsyncIterableIteratorType: GenericType; - let deferredGlobalAsyncGeneratorType: GenericType; - let deferredGlobalTemplateStringsArrayType: ObjectType; - let deferredGlobalImportMetaType: ObjectType; - let deferredGlobalExtractSymbol: Symbol; - let deferredGlobalOmitSymbol: Symbol; - let deferredGlobalBigIntType: ObjectType; - - const allPotentiallyUnusedIdentifiers = - createMap(); // key is file name - - let flowLoopStart = 0; - let flowLoopCount = 0; - let sharedFlowCount = 0; - let flowAnalysisDisabled = false; - let flowInvocationCount = 0; - let lastFlowNode: FlowNode | undefined; - let lastFlowNodeReachable: boolean; - let flowTypeCache: Type[] | undefined; - - const emptyStringType = getLiteralType(''); - const zeroType = getLiteralType(0); - const zeroBigIntType = - getLiteralType({ negative: false, base10Value: '0' }); - - const resolutionTargets: TypeSystemEntity[] = []; - const resolutionResults: boolean[] = []; - const resolutionPropertyNames: TypeSystemPropertyName[] = []; - - let suggestionCount = 0; - const maximumSuggestionCount = 10; - const mergedSymbols: Symbol[] = []; - const symbolLinks: SymbolLinks[] = []; - const nodeLinks: NodeLinks[] = []; - const flowLoopCaches: Map[] = []; - const flowLoopNodes: FlowNode[] = []; - const flowLoopKeys: string[] = []; - const flowLoopTypes: Type[][] = []; - const sharedFlowNodes: FlowNode[] = []; - const sharedFlowTypes: FlowType[] = []; - const flowNodeReachable: (boolean | undefined)[] = []; - const potentialThisCollisions: Node[] = []; - const potentialNewTargetCollisions: Node[] = []; - const potentialWeakMapCollisions: Node[] = []; - const awaitedTypeStack: number[] = []; - - const diagnostics = createDiagnosticCollection(); - const suggestionDiagnostics = createDiagnosticCollection(); - - const typeofTypesByName: ReadonlyMap = - createMapFromTemplate({ - string: stringType, - number: numberType, - bigint: bigintType, - boolean: booleanType, - symbol: esSymbolType, - undefined: undefinedType - }); - const typeofType = createTypeofType(); - - let _jsxNamespace: __String; - let _jsxFactoryEntity: EntityName | undefined; - let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) - | undefined; - - const subtypeRelation = createMap(); - const strictSubtypeRelation = createMap(); - const assignableRelation = createMap(); - const comparableRelation = createMap(); - const identityRelation = createMap(); - const enumRelation = createMap(); - - const builtinGlobals = createSymbolTable(); - builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol); - - initializeTypeChecker(); - - return checker; - - function getJsxNamespace(location: Node | undefined): __String { - if (location) { - const file = getSourceFileOfNode(location); - if (file) { - if (file.localJsxNamespace) { - return file.localJsxNamespace; - } - const jsxPragma = file.pragmas.get('jsx'); - if (jsxPragma) { - const chosenpragma = isArray(jsxPragma) - ? jsxPragma[0] - : jsxPragma; - file.localJsxFactory = parseIsolatedEntityName( - chosenpragma.arguments.factory, - languageVersion - ); - if (file.localJsxFactory) { - return file - .localJsxNamespace = getFirstIdentifier(file - .localJsxFactory).escapedText; - } - } - } - } - if (!_jsxNamespace) { - _jsxNamespace = 'React' as __String; - if (compilerOptions.jsxFactory) { - _jsxFactoryEntity = parseIsolatedEntityName( - compilerOptions.jsxFactory, - languageVersion - ); - if (_jsxFactoryEntity) { - _jsxNamespace = getFirstIdentifier(_jsxFactoryEntity) - .escapedText; - } - } else if (compilerOptions.reactNamespace) { - _jsxNamespace = escapeLeadingUnderscores(compilerOptions - .reactNamespace); - } - } - return _jsxNamespace; - } - - function getEmitResolver( - sourceFile: SourceFile, - cancellationToken: CancellationToken - ) { - // Ensure we have all the type information in place for this file so that all the - // emitter questions of this resolver will return the right information. - getDiagnostics(sourceFile, cancellationToken); - return emitResolver; - } - - function lookupOrIssueError( - location: Node | undefined, - message: DiagnosticMessage, - arg0?: string | number, - arg1?: string | number, - arg2?: string | number, - arg3?: string | number - ): Diagnostic { - const diagnostic = location - ? createDiagnosticForNode( - location, - message, - arg0, - arg1, - arg2, - arg3 - ) - : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3); - const existing = diagnostics.lookup(diagnostic); - if (existing) { - return existing; - } else { - diagnostics.add(diagnostic); - return diagnostic; - } - } - - function error( - location: Node | undefined, - message: DiagnosticMessage, - arg0?: string | number, - arg1?: string | number, - arg2?: string | number, - arg3?: string | number - ): Diagnostic { - const diagnostic = location - ? createDiagnosticForNode( - location, - message, - arg0, - arg1, - arg2, - arg3 - ) - : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3); - diagnostics.add(diagnostic); - return diagnostic; - } - - function addErrorOrSuggestion( - isError: boolean, - diagnostic: DiagnosticWithLocation - ) { - if (isError) { - diagnostics.add(diagnostic); - } else { - suggestionDiagnostics - .add({ ...diagnostic, - category: DiagnosticCategory.Suggestion }); - } - } - function errorOrSuggestion( - isError: boolean, - location: Node, - message: DiagnosticMessage | DiagnosticMessageChain, - arg0?: string | number, - arg1?: string | number, - arg2?: string | number, - arg3?: string | number - ): void { - addErrorOrSuggestion( - isError, - 'message' in message - ? createDiagnosticForNode( - location, - message, - arg0, - arg1, - arg2, - arg3 - ) - : createDiagnosticForNodeFromMessageChain( - location, - message - ) - ); // eslint-disable-line no-in-operator - } - - function errorAndMaybeSuggestAwait( - location: Node, - maybeMissingAwait: boolean, - message: DiagnosticMessage, - arg0?: string | number | undefined, - arg1?: string | number | undefined, - arg2?: string | number | undefined, - arg3?: string | number | undefined - ): Diagnostic { - const diagnostic = error( - location, - message, - arg0, - arg1, - arg2, - arg3 - ); - if (maybeMissingAwait) { - const related = createDiagnosticForNode( - location, - Diagnostics.Did_you_forget_to_use_await - ); - addRelatedInfo(diagnostic, related); - } - return diagnostic; - } - - function createSymbol( - flags: SymbolFlags, - name: __String, - checkFlags?: CheckFlags - ) { - symbolCount++; - const symbol = - (new Symbol( - flags | SymbolFlags.Transient, - name - )); - symbol.checkFlags = checkFlags || 0; - return symbol; - } - - function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags { - let result: SymbolFlags = 0; - if (flags & SymbolFlags.BlockScopedVariable) { - result |= SymbolFlags.BlockScopedVariableExcludes; - } - if (flags - & SymbolFlags.FunctionScopedVariable) - result |= SymbolFlags.FunctionScopedVariableExcludes; - if (flags & SymbolFlags.Property) { - result |= SymbolFlags.PropertyExcludes; - } - if (flags & SymbolFlags.EnumMember) { - result |= SymbolFlags.EnumMemberExcludes; - } - if (flags & SymbolFlags.Function) { - result |= SymbolFlags.FunctionExcludes; - } - if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes; - if (flags & SymbolFlags.Interface) { - result |= SymbolFlags.InterfaceExcludes; - } - if (flags & SymbolFlags.RegularEnum) { - result |= SymbolFlags.RegularEnumExcludes; - } - if (flags & SymbolFlags.ConstEnum) { - result |= SymbolFlags.ConstEnumExcludes; - } - if (flags & SymbolFlags.ValueModule) { - result |= SymbolFlags.ValueModuleExcludes; - } - if (flags & SymbolFlags.Method) { - result |= SymbolFlags.MethodExcludes; - } - if (flags & SymbolFlags.GetAccessor) { - result |= SymbolFlags.GetAccessorExcludes; - } - if (flags & SymbolFlags.SetAccessor) { - result |= SymbolFlags.SetAccessorExcludes; - } - if (flags & SymbolFlags.TypeParameter) { - result |= SymbolFlags.TypeParameterExcludes; - } - if (flags & SymbolFlags.TypeAlias) { - result |= SymbolFlags.TypeAliasExcludes; - } - if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes; - return result; - } - - function recordMergedSymbol(target: Symbol, source: Symbol) { - if (!source.mergeId) { - source.mergeId = nextMergeId; - nextMergeId++; - } - mergedSymbols[source.mergeId] = target; - } - - function cloneSymbol(symbol: Symbol): Symbol { - const result = createSymbol(symbol.flags, symbol.escapedName); - result.declarations = symbol.declarations - ? symbol.declarations.slice() - : []; - result.parent = symbol.parent; - if (symbol.valueDeclaration) { - result.valueDeclaration = symbol.valueDeclaration; - } - if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; - if (symbol.members) result.members = cloneMap(symbol.members); - if (symbol.exports) result.exports = cloneMap(symbol.exports); - recordMergedSymbol(result, symbol); - return result; - } - - /** - * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it. - * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it. - */ - function mergeSymbol( - target: Symbol, - source: Symbol, - unidirectional = false - ): Symbol { - if (!(target.flags & getExcludedSymbolFlags(source.flags)) - || (source.flags | target.flags) & SymbolFlags.Assignment) - { - if (source === target) { - // This can happen when an export assigned namespace exports something also erroneously exported at the top level - // See `declarationFileNoCrashOnExtraExportModifier` for an example - return target; - } - if (!(target.flags & SymbolFlags.Transient)) { - const resolvedTarget = resolveSymbol(target); - if (resolvedTarget === unknownSymbol) { - return source; - } - target = cloneSymbol(resolvedTarget); - } - // Javascript static-property-assignment declarations always merge, even though they are also values - if (source.flags & SymbolFlags.ValueModule - && target.flags & SymbolFlags.ValueModule - && target.constEnumOnlyModule - && !source.constEnumOnlyModule) - { - // reset flag when merging instantiated module into value module that has only const enums - target.constEnumOnlyModule = false; - } - target.flags |= source.flags; - if (source.valueDeclaration - && (!target.valueDeclaration - || isAssignmentDeclaration(target.valueDeclaration) - && !isAssignmentDeclaration(source.valueDeclaration) - || isEffectiveModuleDeclaration(target - .valueDeclaration) - && !isEffectiveModuleDeclaration(source - .valueDeclaration))) - { - // other kinds of value declarations take precedence over modules and assignment declarations - target.valueDeclaration = source.valueDeclaration; - } - addRange(target.declarations, source.declarations); - if (source.members) { - if (!target.members) target.members = createSymbolTable(); - mergeSymbolTable( - target.members, - source.members, - unidirectional - ); - } - if (source.exports) { - if (!target.exports) target.exports = createSymbolTable(); - mergeSymbolTable( - target.exports, - source.exports, - unidirectional - ); - } - if (!unidirectional) { - recordMergedSymbol(target, source); - } - } else if (target.flags & SymbolFlags.NamespaceModule) { - // Do not report an error when merging `var globalThis` with the built-in `globalThis`, - // as we will already report a "Declaration name conflicts..." error, and this error - // won't make much sense. - if (target !== globalThisSymbol) { - error( - getNameOfDeclaration(source.declarations[0]), - Diagnostics - .Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, - symbolToString(target) - ); - } - } else { // error - const isEitherEnum = - !!(target.flags & SymbolFlags.Enum - || source.flags & SymbolFlags.Enum); - const isEitherBlockScoped = - !!(target.flags & SymbolFlags.BlockScopedVariable - || source.flags & SymbolFlags.BlockScopedVariable); - const message = isEitherEnum - ? Diagnostics - .Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations - : isEitherBlockScoped - ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 - : Diagnostics.Duplicate_identifier_0; - const sourceSymbolFile = source.declarations - && getSourceFileOfNode(source.declarations[0]); - const targetSymbolFile = target.declarations - && getSourceFileOfNode(target.declarations[0]); - const symbolName = symbolToString(source); - - // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch - if (sourceSymbolFile && targetSymbolFile - && amalgamatedDuplicates && !isEitherEnum - && sourceSymbolFile !== targetSymbolFile) - { - const firstFile = - comparePaths( - sourceSymbolFile.path, - targetSymbolFile.path - ) === Comparison.LessThan - ? sourceSymbolFile - : targetSymbolFile; - const secondFile = firstFile === sourceSymbolFile - ? targetSymbolFile - : sourceSymbolFile; - const filesDuplicates = getOrUpdate( - amalgamatedDuplicates, - `${firstFile.path}|${secondFile.path}`, - () => ({ firstFile, secondFile, - conflictingSymbols: createMap() }) - ); - const conflictingSymbolInfo = - getOrUpdate( - filesDuplicates.conflictingSymbols, - symbolName, - () => ({ isBlockScoped: isEitherBlockScoped, - firstFileLocations: [], - secondFileLocations: [] }) - ); - addDuplicateLocations( - conflictingSymbolInfo.firstFileLocations, - source - ); - addDuplicateLocations( - conflictingSymbolInfo.secondFileLocations, - target - ); - } else { - addDuplicateDeclarationErrorsForSymbols( - source, - message, - symbolName, - target - ); - addDuplicateDeclarationErrorsForSymbols( - target, - message, - symbolName, - source - ); - } - } - return target; - - function addDuplicateLocations( - locs: Node[], - symbol: Symbol - ): void { - for (const decl of symbol.declarations) { - pushIfUnique( - locs, - (getExpandoInitializer( - decl, /*isPrototypeAssignment*/ - false - ) - ? getNameOfExpando(decl) - : getNameOfDeclaration(decl)) || decl - ); - } - } - } - - function addDuplicateDeclarationErrorsForSymbols( - target: Symbol, - message: DiagnosticMessage, - symbolName: string, - source: Symbol - ) { - forEach(target.declarations, node => { - const errorNode = - (getExpandoInitializer( - node, /*isPrototypeAssignment*/ - false - ) - ? getNameOfExpando(node) - : getNameOfDeclaration(node)) || node; - addDuplicateDeclarationError( - errorNode, - message, - symbolName, - source.declarations - ); - }); - } - - function addDuplicateDeclarationError( - errorNode: Node, - message: DiagnosticMessage, - symbolName: string, - relatedNodes: readonly Node[] | undefined - ) { - const err = lookupOrIssueError(errorNode, message, symbolName); - for (const relatedNode of relatedNodes || emptyArray) { - err.relatedInformation = err.relatedInformation || []; - if (length(err.relatedInformation) >= 5) continue; - addRelatedInfo( - err, - !length(err.relatedInformation) - ? createDiagnosticForNode( - relatedNode, - Diagnostics._0_was_also_declared_here, - symbolName - ) - : createDiagnosticForNode( - relatedNode, - Diagnostics.and_here - ) - ); - } - } - - function combineSymbolTables( - first: SymbolTable | undefined, - second: SymbolTable | undefined - ): SymbolTable | undefined { - if (!hasEntries(first)) return second; - if (!hasEntries(second)) return first; - const combined = createSymbolTable(); - mergeSymbolTable(combined, first); - mergeSymbolTable(combined, second); - return combined; - } - - function mergeSymbolTable( - target: SymbolTable, - source: SymbolTable, - unidirectional = false - ) { - source.forEach((sourceSymbol, id) => { - const targetSymbol = target.get(id); - target.set( - id, - targetSymbol - ? mergeSymbol( - targetSymbol, - sourceSymbol, - unidirectional - ) - : sourceSymbol - ); - }); - } - - function mergeModuleAugmentation(moduleName: StringLiteral - | Identifier): void - { - const moduleAugmentation = moduleName.parent; - if (moduleAugmentation.symbol.declarations[0] - !== moduleAugmentation) - { - // this is a combined symbol for multiple augmentations within the same file. - // its symbol already has accumulated information for all declarations - // so we need to add it just once - do the work only for first declaration - Debug - .assert(moduleAugmentation.symbol.declarations.length > 1); - return; - } - - if (isGlobalScopeAugmentation(moduleAugmentation)) { - mergeSymbolTable(globals, moduleAugmentation.symbol.exports!); - } else { - // find a module that about to be augmented - // do not validate names of augmentations that are defined in ambient context - const moduleNotFoundError = - !(moduleName.parent.parent.flags & NodeFlags.Ambient) - ? Diagnostics - .Invalid_module_name_in_augmentation_module_0_cannot_be_found - : undefined; - let mainModule = resolveExternalModuleNameWorker( - moduleName, - moduleName, - moduleNotFoundError, /*isForAugmentation*/ - true - ); - if (!mainModule) { - return; - } - // obtain item referenced by 'export=' - mainModule = resolveExternalModuleSymbol(mainModule); - if (mainModule.flags & SymbolFlags.Namespace) { - // If we're merging an augmentation to a pattern ambient module, we want to - // perform the merge unidirectionally from the augmentation ('a.foo') to - // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you - // all the exports both from the pattern and from the augmentation, but - // 'getMergedSymbol()' on *.foo only gives you exports from *.foo. - if (some( - patternAmbientModules, - module => mainModule === module.symbol - )) { - const merged = mergeSymbol( - moduleAugmentation.symbol, - mainModule, /*unidirectional*/ - true - ); - if (!patternAmbientModuleAugmentations) { - patternAmbientModuleAugmentations = createMap(); - } - // moduleName will be a StringLiteral since this is not `declare global`. - patternAmbientModuleAugmentations.set( - (moduleName as StringLiteral).text, - merged - ); - } else { - mergeSymbol(mainModule, moduleAugmentation.symbol); - } - } else { - // moduleName will be a StringLiteral since this is not `declare global`. - error( - moduleName, - Diagnostics - .Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, - (moduleName as StringLiteral).text - ); - } - } - } - - function addToSymbolTable( - target: SymbolTable, - source: SymbolTable, - message: DiagnosticMessage - ) { - source.forEach((sourceSymbol, id) => { - const targetSymbol = target.get(id); - if (targetSymbol) { - // Error on redeclarations - forEach( - targetSymbol.declarations, - addDeclarationDiagnostic( - unescapeLeadingUnderscores(id), - message - ) - ); - } else { - target.set(id, sourceSymbol); - } - }); - - function addDeclarationDiagnostic( - id: string, - message: DiagnosticMessage - ) { - return (declaration: Declaration) => diagnostics - .add(createDiagnosticForNode(declaration, message, id)); - } - } - - function getSymbolLinks(symbol: Symbol): SymbolLinks { - if (symbol.flags - & SymbolFlags.Transient) - return symbol; - const id = getSymbolId(symbol); - return symbolLinks[id] || (symbolLinks[id] = {}); - } - - function getNodeLinks(node: Node): NodeLinks { - const nodeId = getNodeId(node); - return nodeLinks[nodeId] - || (nodeLinks[nodeId] = { flags: 0 } as NodeLinks); - } - - function isGlobalSourceFile(node: Node) { - return node.kind === SyntaxKind.SourceFile - && !isExternalOrCommonJsModule( node); - } - - function getSymbol( - symbols: SymbolTable, - name: __String, - meaning: SymbolFlags - ): Symbol | undefined { - if (meaning) { - const symbol = symbols.get(name); - if (symbol) { - Debug.assert( - (getCheckFlags(symbol) & CheckFlags.Instantiated) - === 0, - 'Should never get an instantiated symbol here.' - ); - if (symbol.flags & meaning) { - return symbol; - } - if (symbol.flags & SymbolFlags.Alias) { - const target = resolveAlias(symbol); - // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors - if (target === unknownSymbol - || target.flags & meaning) - { - return symbol; - } - } - } - } - // return undefined if we can't find a symbol. - } - - /** - * Get symbols that represent parameter-property-declaration as parameter and as property declaration - * @param parameter a parameterDeclaration node - * @param parameterName a name of the parameter to get the symbols for. - * @return a tuple of two symbols - */ - function getSymbolsOfParameterPropertyDeclaration( - parameter: ParameterDeclaration, - parameterName: __String - ): [Symbol, Symbol] { - const constructorDeclaration = parameter.parent; - const classDeclaration = parameter.parent.parent; - - const parameterSymbol = getSymbol( - constructorDeclaration.locals!, - parameterName, - SymbolFlags.Value - ); - const propertySymbol = getSymbol( - getMembersOfSymbol(classDeclaration.symbol), - parameterName, - SymbolFlags.Value - ); - - if (parameterSymbol && propertySymbol) { - return [parameterSymbol, propertySymbol]; - } - - return Debug - .fail('There should exist two symbols, one as property declaration and one as parameter declaration'); - } - - function isBlockScopedNameDeclaredBeforeUse( - declaration: Declaration, - usage: Node - ): boolean { - const declarationFile = getSourceFileOfNode(declaration); - const useFile = getSourceFileOfNode(usage); - if (declarationFile !== useFile) { - if ((moduleKind - && (declarationFile.externalModuleIndicator - || useFile.externalModuleIndicator)) - || (!compilerOptions.outFile && !compilerOptions.out) - || isInTypeQuery(usage) - || declaration.flags & NodeFlags.Ambient) - { - // nodes are in different files and order cannot be determined - return true; - } - // declaration is after usage - // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) - if (isUsedInFunctionOrInstanceProperty(usage, declaration)) { - return true; - } - const sourceFiles = host.getSourceFiles(); - return sourceFiles.indexOf(declarationFile) - <= sourceFiles.indexOf(useFile); - } - - if (declaration.pos <= usage.pos) { - // declaration is before usage - if (declaration.kind === SyntaxKind.BindingElement) { - // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2]) - const errorBindingElement = getAncestor( - usage, - SyntaxKind.BindingElement - ) as BindingElement; - if (errorBindingElement) { - return findAncestor( - errorBindingElement, - isBindingElement - ) !== findAncestor(declaration, isBindingElement) - || declaration.pos < errorBindingElement.pos; - } - // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a) - return isBlockScopedNameDeclaredBeforeUse( - getAncestor( - declaration, - SyntaxKind.VariableDeclaration - ) as Declaration, - usage - ); - } else if (declaration.kind - === SyntaxKind.VariableDeclaration) - { - // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a) - return !isImmediatelyUsedInInitializerOfBlockScopedVariable( - declaration as VariableDeclaration, - usage - ); - } else if (isClassDeclaration(declaration)) { - // still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} }) - return !findAncestor( - usage, - n => isComputedPropertyName(n) - && n.parent.parent === declaration - ); - } else if (isPropertyDeclaration(declaration)) { - // still might be illegal if a self-referencing property initializer (eg private x = this.x) - return !isPropertyImmediatelyReferencedWithinDeclaration( - declaration, - usage - ); - } - return true; - } - - // declaration is after usage, but it can still be legal if usage is deferred: - // 1. inside an export specifier - // 2. inside a function - // 3. inside an instance property initializer, a reference to a non-instance property - // 4. inside a static property initializer, a reference to a static method in the same class - // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ) - // or if usage is in a type context: - // 1. inside a type query (typeof in type position) - // 2. inside a jsdoc comment - if (usage.parent.kind === SyntaxKind.ExportSpecifier - || (usage.parent.kind === SyntaxKind.ExportAssignment - && (usage.parent as ExportAssignment).isExportEquals)) - { - // export specifiers do not use the variable, they only make it available for use - return true; - } - // When resolving symbols for exports, the `usage` location passed in can be the export site directly - if (usage.kind === SyntaxKind.ExportAssignment - && (usage as ExportAssignment).isExportEquals) - { - return true; - } - - const container = getEnclosingBlockScopeContainer(declaration); - return !!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) - || isUsedInFunctionOrInstanceProperty( - usage, - declaration, - container - ); - - function isImmediatelyUsedInInitializerOfBlockScopedVariable( - declaration: VariableDeclaration, - usage: Node - ): boolean { - const container = getEnclosingBlockScopeContainer(declaration); - - switch (declaration.parent.parent.kind) { - case SyntaxKind.VariableStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForOfStatement: - // variable statement/for/for-of statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - if (isSameScopeDescendentOf( - usage, - declaration, - container - )) { - return true; - } - break; - } - - // ForIn/ForOf case - use site should not be used in expression part - const grandparent = declaration.parent.parent; - return isForInOrOfStatement(grandparent) - && isSameScopeDescendentOf( - usage, - grandparent.expression, - container - ); - } - - function isUsedInFunctionOrInstanceProperty( - usage: Node, - declaration: Node, - container?: Node - ): boolean { - return !!findAncestor(usage, current => { - if (current === container) { - return 'quit'; - } - if (isFunctionLike(current)) { - return true; - } - - const initializerOfProperty = current.parent - && current.parent.kind - === SyntaxKind.PropertyDeclaration - && ( current.parent).initializer - === current; - - if (initializerOfProperty) { - if (hasModifier( - current.parent, - ModifierFlags.Static - )) { - if (declaration.kind - === SyntaxKind.MethodDeclaration) - { - return true; - } - } else { - const isDeclarationInstanceProperty = - declaration.kind - === SyntaxKind.PropertyDeclaration - && !hasModifier( - declaration, - ModifierFlags.Static - ); - if (!isDeclarationInstanceProperty - || getContainingClass(usage) - !== getContainingClass(declaration)) - { - return true; - } - } - } - return false; - }); - } - - function isPropertyImmediatelyReferencedWithinDeclaration( - declaration: PropertyDeclaration, - usage: Node - ) { - // always legal if usage is after declaration - if (usage.end > declaration.end) { - return false; - } - - // still might be legal if usage is deferred (e.g. x: any = () => this.x) - // otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x) - const ancestorChangingReferenceScope = findAncestor( - usage, - (node: Node) => { - if (node === declaration) { - return 'quit'; - } - - switch (node.kind) { - case SyntaxKind.ArrowFunction: - case SyntaxKind.PropertyDeclaration: - return true; - case SyntaxKind.Block: - switch (node.parent.kind) { - case SyntaxKind.GetAccessor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.SetAccessor: - return true; - default: - return false; - } - default: - return false; - } - } - ); - - return ancestorChangingReferenceScope === undefined; - } - } - - /** - * Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and - * the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with - * the given name can be found. - * - * @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters. - */ - function resolveName( - location: Node | undefined, - name: __String, - meaning: SymbolFlags, - nameNotFoundMessage: DiagnosticMessage | undefined, - nameArg: __String | Identifier | undefined, - isUse: boolean, - excludeGlobals = false, - suggestedNameNotFoundMessage?: DiagnosticMessage - ): Symbol | undefined { - return resolveNameHelper( - location, - name, - meaning, - nameNotFoundMessage, - nameArg, - isUse, - excludeGlobals, - getSymbol, - suggestedNameNotFoundMessage - ); - } - - function resolveNameHelper( - location: Node | undefined, - name: __String, - meaning: SymbolFlags, - nameNotFoundMessage: DiagnosticMessage | undefined, - nameArg: __String | Identifier | undefined, - isUse: boolean, - excludeGlobals: boolean, - lookup: typeof getSymbol, - suggestedNameNotFoundMessage?: DiagnosticMessage - ): Symbol | undefined { - const originalLocation = - location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location - let result: Symbol | undefined; - let lastLocation: Node | undefined; - let lastSelfReferenceLocation: Node | undefined; - let propertyWithInvalidInitializer: Node | undefined; - let associatedDeclarationForContainingInitializer: - ParameterDeclaration | BindingElement | undefined; - let withinDeferredContext = false; - const errorLocation = location; - let grandparent: Node; - let isInExternalModule = false; - - loop: - while (location) { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if (location.locals && !isGlobalSourceFile(location)) { - if (result = lookup(location.locals, name, meaning)) { - let useResult = true; - if (isFunctionLike(location) && lastLocation - && lastLocation - !== ( location).body) - { - // symbol lookup restrictions for function-like declarations - // - Type parameters of a function are in scope in the entire function declaration, including the parameter - // list and return type. However, local types are only in scope in the function body. - // - parameters are only in the scope of function body - // This restriction does not apply to JSDoc comment types because they are parented - // at a higher level than type parameters would normally be - if (meaning & result.flags & SymbolFlags.Type - && lastLocation.kind - !== SyntaxKind.JSDocComment) - { - useResult = result.flags - & SymbolFlags.TypeParameter - ? // type parameters are visible in parameter list, return type and type parameter list - lastLocation - === ( location) - .type - || lastLocation.kind - === SyntaxKind.Parameter - || lastLocation.kind - === SyntaxKind.TypeParameter - : // local types not visible outside the function body - false; - } - if (meaning & result.flags - & SymbolFlags.Variable) - { - // expression inside parameter will lookup as normal variable scope when targeting es2015+ - const functionLocation = - location; - if (compilerOptions.target - && compilerOptions.target - >= ScriptTarget.ES2015 - && isParameter(lastLocation) - && functionLocation.body - && result.valueDeclaration.pos - >= functionLocation.body.pos - && result.valueDeclaration.end - <= functionLocation.body.end) - { - useResult = false; - } else if (result.flags - & SymbolFlags.FunctionScopedVariable) - { - // parameters are visible only inside function body, parameter list and return type - // technically for parameter list case here we might mix parameters and variables declared in function, - // however it is detected separately when checking initializers of parameters - // to make sure that they reference no variables declared after them. - useResult = lastLocation.kind - === SyntaxKind.Parameter - || ( - lastLocation - === ( location) - .type - && !!findAncestor( - result.valueDeclaration, - isParameter - ) - ); - } - } - } else if (location.kind - === SyntaxKind.ConditionalType) - { - // A type parameter declared using 'infer T' in a conditional type is visible only in - // the true branch of the conditional type. - useResult = lastLocation - === ( location).trueType; - } - - if (useResult) { - break loop; - } else { - result = undefined; - } - } - } - withinDeferredContext = withinDeferredContext - || getIsDeferredContext(location, lastLocation); - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule( location)) break; - isInExternalModule = true; - // falls through - case SyntaxKind.ModuleDeclaration: - const moduleExports = - getSymbolOfNode(location as SourceFile - | ModuleDeclaration).exports || emptySymbols; - if (location.kind === SyntaxKind.SourceFile - || (isModuleDeclaration(location) - && location.flags & NodeFlags.Ambient - && !isGlobalScopeAugmentation(location))) - { - // It's an external module. First see if the module has an export default and if the local - // name of that export default matches. - if (result = moduleExports - .get(InternalSymbolName.Default)) - { - const localSymbol = - getLocalSymbolForExportDefault(result); - if (localSymbol && (result.flags & meaning) - && localSymbol.escapedName === name) - { - break loop; - } - result = undefined; - } - - // Because of module/namespace merging, a module's exports are in scope, - // yet we never want to treat an export specifier as putting a member in scope. - // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope. - // Two things to note about this: - // 1. We have to check this without calling getSymbol. The problem with calling getSymbol - // on an export specifier is that it might find the export specifier itself, and try to - // resolve it as an alias. This will cause the checker to consider the export specifier - // a circular alias reference when it might not be. - // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* - // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, - // which is not the desired behavior. - const moduleExport = moduleExports.get(name); - if (moduleExport - && moduleExport.flags === SymbolFlags.Alias - && (getDeclarationOfKind( - moduleExport, - SyntaxKind.ExportSpecifier - ) - || getDeclarationOfKind( - moduleExport, - SyntaxKind.NamespaceExport - ))) - { - break; - } - } - - // ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs) - if (name !== InternalSymbolName.Default - && (result = lookup( - moduleExports, - name, - meaning & SymbolFlags.ModuleMember - ))) - { - if (isSourceFile(location) - && location.commonJsModuleIndicator - && !result.declarations.some(isJSDocTypeAlias)) - { - result = undefined; - } else { - break loop; - } - } - break; - case SyntaxKind.EnumDeclaration: - if (result = lookup( - getSymbolOfNode(location)!.exports!, - name, - meaning & SymbolFlags.EnumMember - )) { - break loop; - } - break; - case SyntaxKind.PropertyDeclaration: - // TypeScript 1.0 spec (April 2014): 8.4.1 - // Initializer expressions for instance member variables are evaluated in the scope - // of the class constructor body but are not permitted to reference parameters or - // local variables of the constructor. This effectively means that entities from outer scopes - // by the same name as a constructor parameter or local variable are inaccessible - // in initializer expressions for instance member variables. - if (!hasModifier(location, ModifierFlags.Static)) { - const ctor = - findConstructorDeclaration(location - .parent as ClassLikeDeclaration); - if (ctor && ctor.locals) { - if (lookup( - ctor.locals, - name, - meaning & SymbolFlags.Value - )) { - // Remember the property node, it will be used later to report appropriate error - propertyWithInvalidInitializer = location; - } - } - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals - // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would - // trigger resolving late-bound names, which we may already be in the process of doing while we're here! - if (result = lookup( - getSymbolOfNode(location as ClassLikeDeclaration - | InterfaceDeclaration).members - || emptySymbols, - name, - meaning & SymbolFlags.Type - )) { - if (!isTypeParameterSymbolDeclaredInContainer( - result, - location - )) { - // ignore type parameters not declared in this container - result = undefined; - break; - } - if (lastLocation - && hasModifier( - lastLocation, - ModifierFlags.Static - )) - { - // TypeScript 1.0 spec (April 2014): 3.4.1 - // The scope of a type parameter extends over the entire declaration with which the type - // parameter list is associated, with the exception of static member declarations in classes. - error( - errorLocation, - Diagnostics - .Static_members_cannot_reference_class_type_parameters - ); - return undefined; - } - break loop; - } - if (location.kind === SyntaxKind.ClassExpression - && meaning & SymbolFlags.Class) - { - const className = ( location) - .name; - if (className && name === className.escapedText) { - result = location.symbol; - break loop; - } - } - break; - case SyntaxKind.ExpressionWithTypeArguments: - // The type parameters of a class are not in scope in the base class expression. - if (lastLocation - === ( location) - .expression - && ( location.parent).token - === SyntaxKind.ExtendsKeyword) - { - const container = location.parent.parent; - if (isClassLike(container) - && (result = lookup( - getSymbolOfNode(container).members!, - name, - meaning & SymbolFlags.Type - ))) - { - if (nameNotFoundMessage) { - error( - errorLocation, - Diagnostics - .Base_class_expressions_cannot_reference_class_type_parameters - ); - } - return undefined; - } - } - break; - // It is not legal to reference a class's own type parameters from a computed property name that - // belongs to the class. For example: - // - // function foo() { return '' } - // class C { // <-- Class's own type parameter T - // [foo()]() { } // <-- Reference to T from class's own computed property - // } - // - case SyntaxKind.ComputedPropertyName: - grandparent = location.parent.parent; - if (isClassLike(grandparent) - || grandparent.kind - === SyntaxKind.InterfaceDeclaration) - { - // A reference to this grandparent's type parameters would be an error - if (result = lookup( - getSymbolOfNode(grandparent as ClassLikeDeclaration - | InterfaceDeclaration).members!, - name, - meaning & SymbolFlags.Type - )) { - error( - errorLocation, - Diagnostics - .A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type - ); - return undefined; - } - } - break; - case SyntaxKind.ArrowFunction: - // when targeting ES6 or higher there is no 'arguments' in an arrow function - // for lower compile targets the resolved symbol is used to emit an error - if (compilerOptions.target! >= ScriptTarget.ES2015) { - break; - } - // falls through - case SyntaxKind.MethodDeclaration: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionDeclaration: - if (meaning & SymbolFlags.Variable - && name === 'arguments') - { - result = argumentsSymbol; - break loop; - } - break; - case SyntaxKind.FunctionExpression: - if (meaning & SymbolFlags.Variable - && name === 'arguments') - { - result = argumentsSymbol; - break loop; - } - - if (meaning & SymbolFlags.Function) { - const functionName = - ( location).name; - if (functionName - && name === functionName.escapedText) - { - result = location.symbol; - break loop; - } - } - break; - case SyntaxKind.Decorator: - // Decorators are resolved at the class declaration. Resolving at the parameter - // or member would result in looking up locals in the method. - // - // function y() {} - // class C { - // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter. - // } - // - if (location.parent - && location.parent.kind === SyntaxKind.Parameter) - { - location = location.parent; - } - // - // function y() {} - // class C { - // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method. - // } - // - - // class Decorators are resolved outside of the class to avoid referencing type parameters of that class. - // - // type T = number; - // declare function y(x: T): any; - // @param(1 as T) // <-- T should resolve to the type alias outside of class C - // class C {} - if (location.parent - && (isClassElement(location.parent) - || location.parent.kind - === SyntaxKind.ClassDeclaration)) - { - location = location.parent; - } - break; - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - // js type aliases do not resolve names from their host, so skip past it - location = getJSDocHost(location); - break; - case SyntaxKind.Parameter: - if (lastLocation - && lastLocation - === (location as ParameterDeclaration) - .initializer) - { - associatedDeclarationForContainingInitializer = location as ParameterDeclaration; - } - break; - case SyntaxKind.BindingElement: - if (lastLocation - && lastLocation - === (location as BindingElement).initializer) - { - const root = getRootDeclaration(location); - if (root.kind === SyntaxKind.Parameter) { - associatedDeclarationForContainingInitializer = location as BindingElement; - } - } - break; - } - if (isSelfReferenceLocation(location)) { - lastSelfReferenceLocation = location; - } - lastLocation = location; - location = location.parent; - } - - // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. - // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself. - // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. - if (isUse && result - && (!lastSelfReferenceLocation - || result !== lastSelfReferenceLocation.symbol)) - { - result.isReferenced! |= meaning; - } - - if (!result) { - if (lastLocation) { - Debug.assert(lastLocation.kind === SyntaxKind.SourceFile); - if ((lastLocation as SourceFile).commonJsModuleIndicator - && name === 'exports' - && meaning & lastLocation.symbol.flags) - { - return lastLocation.symbol; - } - } - - if (!excludeGlobals) { - result = lookup(globals, name, meaning); - } - } - if (!result) { - if (originalLocation && isInJSFile(originalLocation) - && originalLocation.parent) - { - if (isRequireCall( - originalLocation - .parent, /*checkArgumentIsStringLiteralLike*/ - false - )) { - return requireSymbol; - } - } - } - if (!result) { - if (nameNotFoundMessage) { - if (!errorLocation - || !checkAndReportErrorForMissingPrefix( - errorLocation, - name, - nameArg! - ) // TODO: GH#18217 - && !checkAndReportErrorForExtendingInterface(errorLocation) - && !checkAndReportErrorForUsingTypeAsNamespace( - errorLocation, - name, - meaning - ) - && !checkAndReportErrorForUsingTypeAsValue( - errorLocation, - name, - meaning - ) - && !checkAndReportErrorForUsingNamespaceModuleAsValue( - errorLocation, - name, - meaning - ) - && !checkAndReportErrorForUsingValueAsType( - errorLocation, - name, - meaning - )) - { - let suggestion: Symbol | undefined; - if (suggestedNameNotFoundMessage - && suggestionCount < maximumSuggestionCount) - { - suggestion = getSuggestedSymbolForNonexistentSymbol( - originalLocation, - name, - meaning - ); - if (suggestion) { - const suggestionName = - symbolToString(suggestion); - const diagnostic = error( - errorLocation, - suggestedNameNotFoundMessage, - diagnosticName(nameArg!), - suggestionName - ); - if (suggestion.valueDeclaration) { - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - suggestion.valueDeclaration, - Diagnostics._0_is_declared_here, - suggestionName - ) - ); - } - } - } - if (!suggestion) { - error( - errorLocation, - nameNotFoundMessage, - diagnosticName(nameArg!) - ); - } - suggestionCount++; - } - } - return undefined; - } - - // Perform extra checks only if error reporting was requested - if (nameNotFoundMessage) { - if (propertyWithInvalidInitializer) { - // We have a match, but the reference occurred within a property initializer and the identifier also binds - // to a local variable in the constructor where the code will be emitted. - const propertyName = - ( propertyWithInvalidInitializer) - .name; - error( - errorLocation, - Diagnostics - .Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, - declarationNameToString(propertyName), - diagnosticName(nameArg!) - ); - return undefined; - } - - // Only check for block-scoped variable if we have an error location and are looking for the - // name with variable meaning - // For example, - // declare module foo { - // interface bar {} - // } - // const foo/*1*/: foo/*2*/.bar; - // The foo at /*1*/ and /*2*/ will share same symbol with two meanings: - // block-scoped variable and namespace module. However, only when we - // try to resolve name in /*1*/ which is used in variable position, - // we want to check for block-scoped - if (errorLocation - && (meaning & SymbolFlags.BlockScopedVariable - || ((meaning & SymbolFlags.Class - || meaning & SymbolFlags.Enum) - && (meaning & SymbolFlags.Value) - === SymbolFlags.Value))) - { - const exportOrLocalSymbol = - getExportSymbolOfValueSymbolIfExported(result); - if (exportOrLocalSymbol.flags - & SymbolFlags.BlockScopedVariable - || exportOrLocalSymbol.flags & SymbolFlags.Class - || exportOrLocalSymbol.flags & SymbolFlags.Enum) - { - checkResolvedBlockScopedVariable( - exportOrLocalSymbol, - errorLocation - ); - } - } - - // If we're in an external module, we can't reference value symbols created from UMD export declarations - if (result && isInExternalModule - && (meaning & SymbolFlags.Value) === SymbolFlags.Value - && !(originalLocation!.flags & NodeFlags.JSDoc)) - { - const merged = getMergedSymbol(result); - if (length(merged.declarations) - && every( - merged.declarations, - d => isNamespaceExportDeclaration(d) - || isSourceFile(d) && !!d.symbol.globalExports - )) - { - errorOrSuggestion( - !compilerOptions.allowUmdGlobalAccess, - errorLocation!, - Diagnostics - ._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, - unescapeLeadingUnderscores(name) - ); - } - } - - // If we're in a parameter initializer, we can't reference the values of the parameter whose initializer we're within or parameters to the right - if (result && associatedDeclarationForContainingInitializer - && !withinDeferredContext - && (meaning & SymbolFlags.Value) === SymbolFlags.Value) - { - const candidate = - getMergedSymbol(getLateBoundSymbol(result)); - const root = - (getRootDeclaration(associatedDeclarationForContainingInitializer) as ParameterDeclaration); - // A parameter initializer or binding pattern initializer within a parameter cannot refer to itself - if (candidate - === getSymbolOfNode(associatedDeclarationForContainingInitializer)) - { - error( - errorLocation, - Diagnostics - .Parameter_0_cannot_be_referenced_in_its_initializer, - declarationNameToString(associatedDeclarationForContainingInitializer - .name) - ); - } // And it cannot refer to any declarations which come after it - else if (candidate.valueDeclaration - && candidate.valueDeclaration.pos - > associatedDeclarationForContainingInitializer.pos - && root.parent.locals - && lookup( - root.parent.locals, - candidate.escapedName, - meaning - ) === candidate) - { - error( - errorLocation, - Diagnostics - .Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, - declarationNameToString(associatedDeclarationForContainingInitializer - .name), - declarationNameToString( errorLocation) - ); - } - } - } - return result; - } - - function getIsDeferredContext( - location: Node, - lastLocation: Node | undefined - ): boolean { - if (location.kind !== SyntaxKind.ArrowFunction - && location.kind !== SyntaxKind.FunctionExpression) - { - // initializers in instance property declaration of class like entities are executed in constructor and thus deferred - return isTypeQueryNode(location) || (( - isFunctionLikeDeclaration(location) - || (location.kind === SyntaxKind.PropertyDeclaration - && !hasModifier(location, ModifierFlags.Static)) - ) - && (!lastLocation - || lastLocation - !== (location as FunctionLike - | PropertyDeclaration) - .name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred - } - if (lastLocation - && lastLocation - === (location as FunctionExpression | ArrowFunction).name) - { - return false; - } - // generator functions and async functions are not inlined in control flow when immediately invoked - if ((location as FunctionExpression | ArrowFunction).asteriskToken - || hasModifier(location, ModifierFlags.Async)) - { - return true; - } - return !getImmediatelyInvokedFunctionExpression(location); - } - - function isSelfReferenceLocation(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }` - return true; - default: - return false; - } - } - - function diagnosticName(nameArg: __String | Identifier - | PrivateIdentifier) - { - return isString(nameArg) - ? unescapeLeadingUnderscores(nameArg as __String) - : declarationNameToString(nameArg as Identifier); - } - - function isTypeParameterSymbolDeclaredInContainer( - symbol: Symbol, - container: Node - ) { - for (const decl of symbol.declarations) { - if (decl.kind === SyntaxKind.TypeParameter) { - const parent = isJSDocTemplateTag(decl.parent) - ? getJSDocHost(decl.parent) - : decl.parent; - if (parent === container) { - return !(isJSDocTemplateTag(decl.parent) - && find( - (decl.parent.parent as JSDoc).tags!, - isJSDocTypeAlias - )); // TODO: GH#18217 - } - } - } - - return false; - } - - function checkAndReportErrorForMissingPrefix( - errorLocation: Node, - name: __String, - nameArg: __String | Identifier - ): boolean { - if (!isIdentifier(errorLocation) - || errorLocation.escapedText !== name - || isTypeReferenceIdentifier(errorLocation) - || isInTypeQuery(errorLocation)) - { - return false; - } - - const container = getThisContainer( - errorLocation, /*includeArrowFunctions*/ - false - ); - let location = container; - while (location) { - if (isClassLike(location.parent)) { - const classSymbol = getSymbolOfNode(location.parent); - if (!classSymbol) { - break; - } - - // Check to see if a static member exists. - const constructorType = getTypeOfSymbol(classSymbol); - if (getPropertyOfType(constructorType, name)) { - error( - errorLocation, - Diagnostics - .Cannot_find_name_0_Did_you_mean_the_static_member_1_0, - diagnosticName(nameArg), - symbolToString(classSymbol) - ); - return true; - } - - // No static member is present. - // Check if we're in an instance method and look for a relevant instance member. - if (location === container - && !hasModifier(location, ModifierFlags.Static)) - { - const instanceType = - ( getDeclaredTypeOfSymbol(classSymbol)) - .thisType!; // TODO: GH#18217 - if (getPropertyOfType(instanceType, name)) { - error( - errorLocation, - Diagnostics - .Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, - diagnosticName(nameArg) - ); - return true; - } - } - } - - location = location.parent; - } - return false; - } - - function checkAndReportErrorForExtendingInterface(errorLocation: - Node): boolean - { - const expression = - getEntityNameForExtendingInterface(errorLocation); - if (expression - && resolveEntityName( - expression, - SymbolFlags.Interface, /*ignoreErrors*/ - true - )) - { - error( - errorLocation, - Diagnostics - .Cannot_extend_an_interface_0_Did_you_mean_implements, - getTextOfNode(expression) - ); - return true; - } - return false; - } - /** - * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, - * but returns undefined if that expression is not an EntityNameExpression. - */ - function getEntityNameForExtendingInterface(node: - Node): EntityNameExpression | undefined - { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PropertyAccessExpression: - return node.parent - ? getEntityNameForExtendingInterface(node.parent) - : undefined; - case SyntaxKind.ExpressionWithTypeArguments: - if (isEntityNameExpression(( node) - .expression)) - { - return ( node) - .expression; - } - // falls through - default: - return undefined; - } - } - - function checkAndReportErrorForUsingTypeAsNamespace( - errorLocation: Node, - name: __String, - meaning: SymbolFlags - ): boolean { - const namespaceMeaning = - SymbolFlags.Namespace - | (isInJSFile(errorLocation) ? SymbolFlags.Value : 0); - if (meaning === namespaceMeaning) { - const symbol = - resolveSymbol(resolveName( - errorLocation, - name, - SymbolFlags.Type - & ~namespaceMeaning, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - )); - const parent = errorLocation.parent; - if (symbol) { - if (isQualifiedName(parent)) { - Debug.assert( - parent.left === errorLocation, - 'Should only be resolving left side of qualified name as a namespace' - ); - const propName = parent.right.escapedText; - const propType = getPropertyOfType( - getDeclaredTypeOfSymbol(symbol), - propName - ); - if (propType) { - error( - parent, - Diagnostics - .Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, - unescapeLeadingUnderscores(name), - unescapeLeadingUnderscores(propName) - ); - return true; - } - } - error( - errorLocation, - Diagnostics - ._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, - unescapeLeadingUnderscores(name) - ); - return true; - } - } - - return false; - } - - function checkAndReportErrorForUsingValueAsType( - errorLocation: Node, - name: __String, - meaning: SymbolFlags - ): boolean { - if (meaning & (SymbolFlags.Type & ~SymbolFlags.Namespace)) { - const symbol = - resolveSymbol(resolveName( - errorLocation, - name, - ~SymbolFlags.Type - & SymbolFlags.Value, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - )); - if (symbol && !(symbol.flags & SymbolFlags.Namespace)) { - error( - errorLocation, - Diagnostics - ._0_refers_to_a_value_but_is_being_used_as_a_type_here, - unescapeLeadingUnderscores(name) - ); - return true; - } - } - return false; - } - - function checkAndReportErrorForUsingTypeAsValue( - errorLocation: Node, - name: __String, - meaning: SymbolFlags - ): boolean { - if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) { - if (name === 'any' || name === 'string' || name === 'number' - || name === 'boolean' || name === 'never') - { - error( - errorLocation, - Diagnostics - ._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, - unescapeLeadingUnderscores(name) - ); - return true; - } - const symbol = - resolveSymbol(resolveName( - errorLocation, - name, - SymbolFlags.Type - & ~SymbolFlags.Value, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - )); - if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) { - const message = isES2015OrLaterConstructorName(name) - ? Diagnostics - ._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later - : Diagnostics - ._0_only_refers_to_a_type_but_is_being_used_as_a_value_here; - error( - errorLocation, - message, - unescapeLeadingUnderscores(name) - ); - return true; - } - } - return false; - } - - function isES2015OrLaterConstructorName(n: __String) { - switch (n) { - case 'Promise': - case 'Symbol': - case 'Map': - case 'WeakMap': - case 'Set': - case 'WeakSet': - return true; - } - return false; - } - - function checkAndReportErrorForUsingNamespaceModuleAsValue( - errorLocation: Node, - name: __String, - meaning: SymbolFlags - ): boolean { - if (meaning - & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule - & ~SymbolFlags.Type)) - { - const symbol = - resolveSymbol(resolveName( - errorLocation, - name, - SymbolFlags.NamespaceModule - & ~SymbolFlags.Value, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - )); - if (symbol) { - error( - errorLocation, - isTypeOnlyEnumAlias(symbol) - ? Diagnostics - .Enum_0_cannot_be_used_as_a_value_because_only_its_type_has_been_imported - : Diagnostics.Cannot_use_namespace_0_as_a_value, - unescapeLeadingUnderscores(name) - ); - return true; - } - } else if (meaning - & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule - & ~SymbolFlags.Value)) - { - const symbol = - resolveSymbol(resolveName( - errorLocation, - name, - (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) - & ~SymbolFlags.Type, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - )); - if (symbol) { - error( - errorLocation, - Diagnostics.Cannot_use_namespace_0_as_a_type, - unescapeLeadingUnderscores(name) - ); - return true; - } - } - return false; - } - - function checkResolvedBlockScopedVariable( - result: Symbol, - errorLocation: Node - ): void { - Debug - .assert(!!(result.flags & SymbolFlags.BlockScopedVariable - || result.flags & SymbolFlags.Class - || result.flags & SymbolFlags.Enum)); - if (result.flags - & (SymbolFlags.Function | SymbolFlags.FunctionScopedVariable - | SymbolFlags.Assignment) - && result.flags & SymbolFlags.Class) - { - // constructor functions aren't block scoped - return; - } - // Block-scoped variables cannot be used before their definition - const declaration = find( - result.declarations, - d => isBlockOrCatchScoped(d) || isClassLike(d) - || (d.kind === SyntaxKind.EnumDeclaration) - ); - - if (declaration === undefined) { - return Debug - .fail('checkResolvedBlockScopedVariable could not find block-scoped declaration'); - } - - if (!(declaration.flags & NodeFlags.Ambient) - && !isBlockScopedNameDeclaredBeforeUse( - declaration, - errorLocation - )) - { - let diagnosticMessage; - const declarationName = - declarationNameToString(getNameOfDeclaration(declaration)); - if (result.flags & SymbolFlags.BlockScopedVariable) { - diagnosticMessage = error( - errorLocation, - Diagnostics - .Block_scoped_variable_0_used_before_its_declaration, - declarationName - ); - } else if (result.flags & SymbolFlags.Class) { - diagnosticMessage = error( - errorLocation, - Diagnostics.Class_0_used_before_its_declaration, - declarationName - ); - } else if (result.flags & SymbolFlags.RegularEnum) { - diagnosticMessage = error( - errorLocation, - Diagnostics.Enum_0_used_before_its_declaration, - declarationName - ); - } else { - Debug.assert(!!(result.flags & SymbolFlags.ConstEnum)); - if (compilerOptions.preserveConstEnums) { - diagnosticMessage = error( - errorLocation, - Diagnostics.Class_0_used_before_its_declaration, - declarationName - ); - } - } - - if (diagnosticMessage) { - addRelatedInfo( - diagnosticMessage, - createDiagnosticForNode( - declaration, - Diagnostics._0_is_declared_here, - declarationName - ) - ); - } - } - } - - /* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached. - * If at any point current node is equal to 'parent' node - return true. - * Return false if 'stopAt' node is reached or isFunctionLike(current) === true. - */ - function isSameScopeDescendentOf( - initial: Node, - parent: Node | undefined, - stopAt: Node - ): boolean { - return !!parent - && !!findAncestor( - initial, - n => n === stopAt || isFunctionLike(n) - ? 'quit' - : n === parent - ); - } - - function getAnyImportSyntax(node: Node): AnyImportSyntax | undefined { - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - return node as ImportEqualsDeclaration; - case SyntaxKind.ImportClause: - return (node as ImportClause).parent; - case SyntaxKind.NamespaceImport: - return (node as NamespaceImport).parent.parent; - case SyntaxKind.ImportSpecifier: - return (node as ImportSpecifier).parent.parent.parent; - default: - return undefined; - } - } - - function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration - | undefined - { - return find( - symbol.declarations, - isAliasSymbolDeclaration - ); - } - - function getTargetOfImportEqualsDeclaration( - node: ImportEqualsDeclaration, - dontResolveAlias: boolean - ): Symbol | undefined { - if (node.moduleReference.kind - === SyntaxKind.ExternalModuleReference) - { - return resolveExternalModuleSymbol(resolveExternalModuleName( - node, - getExternalModuleImportEqualsDeclarationExpression(node) - )); - } - return getSymbolOfPartOfRightHandSideOfImportEquals( - node.moduleReference, - dontResolveAlias - ); - } - - function resolveExportByName( - moduleSymbol: Symbol, - name: __String, - dontResolveAlias: boolean - ) { - const exportValue = moduleSymbol.exports! - .get(InternalSymbolName.ExportEquals); - return exportValue - ? getPropertyOfType(getTypeOfSymbol(exportValue), name) - : resolveSymbol( - moduleSymbol.exports!.get(name), - dontResolveAlias - ); - } - - function isSyntacticDefault(node: Node) { - return ((isExportAssignment(node) && !node.isExportEquals) - || hasModifier(node, ModifierFlags.Default) - || isExportSpecifier(node)); - } - - function canHaveSyntheticDefault( - file: SourceFile | undefined, - moduleSymbol: Symbol, - dontResolveAlias: boolean - ) { - if (!allowSyntheticDefaultImports) { - return false; - } - // Declaration files (and ambient modules) - if (!file || file.isDeclarationFile) { - // Definitely cannot have a synthetic default if they have a syntactic default member specified - const defaultExportSymbol = resolveExportByName( - moduleSymbol, - InternalSymbolName.Default, /*dontResolveAlias*/ - true - ); // Dont resolve alias because we want the immediately exported symbol's declaration - if (defaultExportSymbol - && some( - defaultExportSymbol.declarations, - isSyntacticDefault - )) - { - return false; - } - // It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member - // So we check a bit more, - if (resolveExportByName( - moduleSymbol, - escapeLeadingUnderscores('__esModule'), - dontResolveAlias - )) { - // If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code), - // it definitely is a module and does not have a synthetic default - return false; - } - // There are _many_ declaration files not written with esmodules in mind that still get compiled into a format with __esModule set - // Meaning there may be no default at runtime - however to be on the permissive side, we allow access to a synthetic default member - // as there is no marker to indicate if the accompanying JS has `__esModule` or not, or is even native esm - return true; - } - // TypeScript files never have a synthetic default (as they are always emitted with an __esModule marker) _unless_ they contain an export= statement - if (!isSourceFileJS(file)) { - return hasExportAssignmentSymbol(moduleSymbol); - } - // JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker - return !file.externalModuleIndicator - && !resolveExportByName( - moduleSymbol, - escapeLeadingUnderscores('__esModule'), - dontResolveAlias - ); - } - - function getTargetOfImportClause( - node: ImportClause, - dontResolveAlias: boolean - ): Symbol | undefined { - const moduleSymbol = resolveExternalModuleName( - node, - node.parent.moduleSpecifier - ); - - if (moduleSymbol) { - let exportDefaultSymbol: Symbol | undefined; - if (isShorthandAmbientModuleSymbol(moduleSymbol)) { - exportDefaultSymbol = moduleSymbol; - } else { - exportDefaultSymbol = resolveExportByName( - moduleSymbol, - InternalSymbolName.Default, - dontResolveAlias - ); - } - - const file = find(moduleSymbol.declarations, isSourceFile); - const hasSyntheticDefault = canHaveSyntheticDefault( - file, - moduleSymbol, - dontResolveAlias - ); - if (!exportDefaultSymbol && !hasSyntheticDefault) { - if (hasExportAssignmentSymbol(moduleSymbol)) { - const compilerOptionName = - moduleKind >= ModuleKind.ES2015 - ? 'allowSyntheticDefaultImports' - : 'esModuleInterop'; - const exportEqualsSymbol = moduleSymbol.exports! - .get(InternalSymbolName.ExportEquals); - const exportAssignment = exportEqualsSymbol! - .valueDeclaration; - const err = error( - node.name, - Diagnostics - .Module_0_can_only_be_default_imported_using_the_1_flag, - symbolToString(moduleSymbol), - compilerOptionName - ); - - addRelatedInfo(err, createDiagnosticForNode( - exportAssignment, - Diagnostics - .This_module_is_declared_with_using_export_and_can_only_be_used_with_a_default_import_when_using_the_0_flag, - compilerOptionName - )); - } else { - if (moduleSymbol.exports - && moduleSymbol.exports - .has(node.symbol.escapedName)) - { - error( - node.name, - Diagnostics - .Module_0_has_no_default_export_Did_you_mean_to_use_import_1_from_0_instead, - symbolToString(moduleSymbol), - symbolToString(node.symbol) - ); - } else { - error( - node.name, - Diagnostics.Module_0_has_no_default_export, - symbolToString(moduleSymbol) - ); - } - } - } else if (hasSyntheticDefault) { - // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present - return maybeTypeOnly( - resolveExternalModuleSymbol( - moduleSymbol, - dontResolveAlias - ) - || resolveSymbol(moduleSymbol, dontResolveAlias) - ); - } - return maybeTypeOnly(exportDefaultSymbol); - } - - function maybeTypeOnly(symbol: Symbol | undefined) { - if (symbol && node.isTypeOnly && node.name) { - return createTypeOnlyImportOrExport(node.name, symbol); - } - return symbol; - } - } - - function getTargetOfNamespaceImport( - node: NamespaceImport, - dontResolveAlias: boolean - ): Symbol | undefined { - const moduleSpecifier = node.parent.parent.moduleSpecifier; - const moduleSymbol = resolveESModuleSymbol( - resolveExternalModuleName(node, moduleSpecifier), - moduleSpecifier, - dontResolveAlias, /*suppressUsageError*/ - false - ); - return moduleSymbol && node.parent.isTypeOnly - ? createTypeOnlySymbol(moduleSymbol) - : moduleSymbol; - } - - function getTargetOfNamespaceExport( - node: NamespaceExport, - dontResolveAlias: boolean - ): Symbol | undefined { - const moduleSpecifier = node.parent.moduleSpecifier; - return moduleSpecifier - && resolveESModuleSymbol( - resolveExternalModuleName(node, moduleSpecifier), - moduleSpecifier, - dontResolveAlias, /*suppressUsageError*/ - false - ); - } - - // This function creates a synthetic symbol that combines the value side of one symbol with the - // type/namespace side of another symbol. Consider this example: - // - // declare module graphics { - // interface Point { - // x: number; - // y: number; - // } - // } - // declare var graphics: { - // Point: new (x: number, y: number) => graphics.Point; - // } - // declare module "graphics" { - // export = graphics; - // } - // - // An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point' - // property with the type/namespace side interface 'Point'. - function combineValueAndTypeSymbols( - valueSymbol: Symbol, - typeSymbol: Symbol - ): Symbol { - if (valueSymbol === unknownSymbol - && typeSymbol === unknownSymbol) - { - return unknownSymbol; - } - if (valueSymbol.flags - & (SymbolFlags.Type | SymbolFlags.Namespace)) - { - return valueSymbol; - } - const result = createSymbol( - valueSymbol.flags | typeSymbol.flags, - valueSymbol.escapedName - ); - result.declarations = deduplicate( - concatenate(valueSymbol.declarations, typeSymbol.declarations), - equateValues - ); - result.parent = valueSymbol.parent || typeSymbol.parent; - if (valueSymbol.valueDeclaration) { - result.valueDeclaration = valueSymbol.valueDeclaration; - } - if (typeSymbol.members) result.members = typeSymbol.members; - if (valueSymbol.exports) result.exports = valueSymbol.exports; - return result; - } - - function getExportOfModule( - symbol: Symbol, - name: __String, - dontResolveAlias: boolean - ): Symbol | undefined { - if (symbol.flags & SymbolFlags.Module) { - return resolveSymbol( - getExportsOfSymbol(symbol).get(name)!, - dontResolveAlias - ); - } - } - - function getPropertyOfVariable(symbol: Symbol, name: __String): Symbol - | undefined - { - if (symbol.flags & SymbolFlags.Variable) { - const typeAnnotation = - ( symbol.valueDeclaration).type; - if (typeAnnotation) { - return resolveSymbol(getPropertyOfType( - getTypeFromTypeNode(typeAnnotation), - name - )); - } - } - } - - function getExternalModuleMember( - node: ImportDeclaration | ExportDeclaration, - specifier: ImportOrExportSpecifier, - dontResolveAlias = false - ): Symbol | undefined { - const moduleSymbol = resolveExternalModuleName( - node, - node.moduleSpecifier! - )!; // TODO: GH#18217 - const name = specifier.propertyName || specifier.name; - const suppressInteropError = - name.escapedText === InternalSymbolName.Default - && !!(compilerOptions.allowSyntheticDefaultImports - || compilerOptions.esModuleInterop); - const targetSymbol = resolveESModuleSymbol( - moduleSymbol, - node.moduleSpecifier!, - dontResolveAlias, - suppressInteropError - ); - if (targetSymbol) { - if (name.escapedText) { - if (isShorthandAmbientModuleSymbol(moduleSymbol)) { - return moduleSymbol; - } - - let symbolFromVariable: Symbol | undefined; - // First check if module was specified with "export=". If so, get the member from the resolved type - if (moduleSymbol && moduleSymbol.exports - && moduleSymbol.exports - .get(InternalSymbolName.ExportEquals)) - { - symbolFromVariable = getPropertyOfType( - getTypeOfSymbol(targetSymbol), - name.escapedText - ); - } else { - symbolFromVariable = getPropertyOfVariable( - targetSymbol, - name.escapedText - ); - } - // if symbolFromVariable is export - get its final target - symbolFromVariable = resolveSymbol( - symbolFromVariable, - dontResolveAlias - ); - let symbolFromModule = getExportOfModule( - targetSymbol, - name.escapedText, - dontResolveAlias - ); - // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default - if (!symbolFromModule && allowSyntheticDefaultImports - && name.escapedText === InternalSymbolName.Default) - { - symbolFromModule = resolveExternalModuleSymbol( - moduleSymbol, - dontResolveAlias - ) || resolveSymbol(moduleSymbol, dontResolveAlias); - } - const symbol = - symbolFromModule && symbolFromVariable - && symbolFromModule !== symbolFromVariable - ? combineValueAndTypeSymbols( - symbolFromVariable, - symbolFromModule - ) - : symbolFromModule || symbolFromVariable; - if (!symbol) { - const moduleName = getFullyQualifiedName( - moduleSymbol, - node - ); - const declarationName = declarationNameToString(name); - const suggestion = - getSuggestedSymbolForNonexistentModule( - name, - targetSymbol - ); - if (suggestion !== undefined) { - const suggestionName = symbolToString(suggestion); - const diagnostic = error( - name, - Diagnostics - .Module_0_has_no_exported_member_1_Did_you_mean_2, - moduleName, - declarationName, - suggestionName - ); - if (suggestion.valueDeclaration) { - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - suggestion.valueDeclaration, - Diagnostics._0_is_declared_here, - suggestionName - ) - ); - } - } else { - if (moduleSymbol.exports - && moduleSymbol.exports - .has(InternalSymbolName.Default)) - { - error( - name, - Diagnostics - .Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead, - moduleName, - declarationName - ); - } else { - error( - name, - Diagnostics - .Module_0_has_no_exported_member_1, - moduleName, - declarationName - ); - } - } - } - return symbol; - } - } - } - - function getTargetOfImportSpecifier( - node: ImportSpecifier, - dontResolveAlias: boolean - ): Symbol | undefined { - const resolved = getExternalModuleMember( - node.parent.parent.parent, - node, - dontResolveAlias - ); - if (resolved && node.parent.parent.isTypeOnly) { - return createTypeOnlyImportOrExport(node.name, resolved); - } - return resolved; - } - - function getTargetOfNamespaceExportDeclaration( - node: NamespaceExportDeclaration, - dontResolveAlias: boolean - ): Symbol { - return resolveExternalModuleSymbol( - node.parent.symbol, - dontResolveAlias - ); - } - - /** - * Creates a type alias symbol with a target symbol for type-only imports and exports. - * The symbol for `A` in `export type { A }` or `export type { A } from "./mod"` has - * `TypeFlags.Alias` so that alias resolution works as usual, but once the target `A` - * has been resolved, we essentially want to pretend we have a type alias to that target. - */ - function createTypeOnlyImportOrExport( - sourceNode: ExportSpecifier | Identifier, - target: Symbol - ) { - const symbol = createTypeOnlySymbol(target); - if (!symbol && target !== unknownSymbol) { - const identifier = isExportSpecifier(sourceNode) - ? sourceNode.name - : sourceNode; - const nameText = idText(identifier); - const diagnostic = error( - identifier, - Diagnostics - .Type_only_0_must_reference_a_type_but_1_is_a_value, - isExportSpecifier(sourceNode) ? 'export' : 'import', - nameText - ); - const targetDeclaration = - target.valueDeclaration ?? target.declarations?.[0]; - if (targetDeclaration) { - addRelatedInfo(diagnostic, createDiagnosticForNode( - targetDeclaration, - Diagnostics._0_is_declared_here, - nameText - )); - } - } - - return symbol; - } - - function createTypeOnlySymbol(target: Symbol): Symbol | undefined { - if (target.flags & SymbolFlags.ValueModule) { - return createNamespaceModuleForModule(target); - } - if (target.flags & SymbolFlags.Enum) { - return createNamespaceModuleForEnum(target); - } - if (!(target.flags & SymbolFlags.Value)) { - return target; - } - if (target.flags & SymbolFlags.Type) { - const alias = createSymbol( - SymbolFlags.TypeAlias, - target.escapedName - ); - alias.declarations = emptyArray; - alias.immediateTarget = target; - return alias; - } - } - - function createNamespaceModuleForEnum(enumSymbol: Symbol) { - Debug.assert(!!(enumSymbol.flags & SymbolFlags.Enum)); - const symbol = createSymbol( - SymbolFlags.NamespaceModule | SymbolFlags.TypeAlias, - enumSymbol.escapedName - ); - symbol.immediateTarget = enumSymbol; - symbol.declarations = enumSymbol.declarations; - if (enumSymbol.exports) { - symbol.exports = createSymbolTable(); - enumSymbol.exports.forEach((exportSymbol, key) => { - symbol.exports!.set( - key, - Debug.assertDefined(createTypeOnlySymbol(exportSymbol)) - ); - }); - } - return symbol; - } - - function createNamespaceModuleForModule(moduleSymbol: Symbol) { - Debug.assert(!!(moduleSymbol.flags & SymbolFlags.ValueModule)); - const filtered = createSymbol( - SymbolFlags.NamespaceModule, - moduleSymbol.escapedName - ); - filtered.declarations = moduleSymbol.declarations; - if (moduleSymbol.exports) { - filtered.exports = createSymbolTable(); - moduleSymbol.exports.forEach((exportSymbol, key) => { - const typeOnlyExport = createTypeOnlySymbol(exportSymbol); - if (typeOnlyExport) { - filtered.exports!.set(key, typeOnlyExport); - } - }); - } - return filtered; - } - - function getTargetOfExportSpecifier( - node: ExportSpecifier, - meaning: SymbolFlags, - dontResolveAlias?: boolean - ) { - const target = node.parent.parent.moduleSpecifier - ? getExternalModuleMember( - node.parent.parent, - node, - dontResolveAlias - ) - : resolveEntityName( - node.propertyName || node.name, - meaning, /*ignoreErrors*/ - false, - dontResolveAlias - ); - return target && node.parent.parent.isTypeOnly - ? createTypeOnlyImportOrExport(node, target) - : target; - } - - function getTargetOfExportAssignment( - node: ExportAssignment | BinaryExpression, - dontResolveAlias: boolean - ): Symbol | undefined { - const expression = - (isExportAssignment(node) - ? node.expression - : node.right) as EntityNameExpression | ClassExpression; - return getTargetOfAliasLikeExpression( - expression, - dontResolveAlias - ); - } - - function getTargetOfAliasLikeExpression( - expression: Expression, - dontResolveAlias: boolean - ) { - if (isClassExpression(expression)) { - return checkExpressionCached(expression).symbol; - } - if (!isEntityName(expression) - && !isEntityNameExpression(expression)) - { - return undefined; - } - const aliasLike = resolveEntityName( - expression, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace, /*ignoreErrors*/ - true, - dontResolveAlias - ); - if (aliasLike) { - return aliasLike; - } - checkExpressionCached(expression); - return getNodeLinks(expression).resolvedSymbol; - } - - function getTargetOfPropertyAssignment( - node: PropertyAssignment, - dontRecursivelyResolve: boolean - ): Symbol | undefined { - const expression = node.initializer; - return getTargetOfAliasLikeExpression( - expression, - dontRecursivelyResolve - ); - } - - function getTargetOfPropertyAccessExpression( - node: PropertyAccessExpression, - dontRecursivelyResolve: boolean - ): Symbol | undefined { - if (!(isBinaryExpression(node.parent) && node.parent.left === node - && node.parent.operatorToken.kind === SyntaxKind.EqualsToken)) - { - return undefined; - } - - return getTargetOfAliasLikeExpression( - node.parent.right, - dontRecursivelyResolve - ); - } - - function getTargetOfAliasDeclaration( - node: Declaration, - dontRecursivelyResolve = false - ): Symbol | undefined { - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - return getTargetOfImportEqualsDeclaration( - node, - dontRecursivelyResolve - ); - case SyntaxKind.ImportClause: - return getTargetOfImportClause( - node, - dontRecursivelyResolve - ); - case SyntaxKind.NamespaceImport: - return getTargetOfNamespaceImport( - node, - dontRecursivelyResolve - ); - case SyntaxKind.NamespaceExport: - return getTargetOfNamespaceExport( - node, - dontRecursivelyResolve - ); - case SyntaxKind.ImportSpecifier: - return getTargetOfImportSpecifier( - node, - dontRecursivelyResolve - ); - case SyntaxKind.ExportSpecifier: - return getTargetOfExportSpecifier( - node, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace, - dontRecursivelyResolve - ); - case SyntaxKind.ExportAssignment: - case SyntaxKind.BinaryExpression: - return getTargetOfExportAssignment( - ( node), - dontRecursivelyResolve - ); - case SyntaxKind.NamespaceExportDeclaration: - return getTargetOfNamespaceExportDeclaration( - node, - dontRecursivelyResolve - ); - case SyntaxKind.ShorthandPropertyAssignment: - return resolveEntityName( - (node as ShorthandPropertyAssignment).name, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace, /*ignoreErrors*/ - true, - dontRecursivelyResolve - ); - case SyntaxKind.PropertyAssignment: - return getTargetOfPropertyAssignment( - node as PropertyAssignment, - dontRecursivelyResolve - ); - case SyntaxKind.PropertyAccessExpression: - return getTargetOfPropertyAccessExpression( - node as PropertyAccessExpression, - dontRecursivelyResolve - ); - default: - return Debug.fail(); - } - } - - /** - * Indicates that a symbol is an alias that does not merge with a local declaration. - * OR Is a JSContainer which may merge an alias with a local declaration - */ - function isNonLocalAlias( - symbol: Symbol | undefined, - excludes - = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace - ): symbol is Symbol { - if (!symbol) return false; - return (symbol.flags & (SymbolFlags.Alias | excludes)) - === SymbolFlags.Alias - || !!(symbol.flags & SymbolFlags.Alias - && symbol.flags & SymbolFlags.Assignment); - } - - function resolveSymbol( - symbol: Symbol, - dontResolveAlias?: boolean - ): Symbol; - function resolveSymbol( - symbol: Symbol | undefined, - dontResolveAlias?: boolean - ): Symbol | undefined; - function resolveSymbol( - symbol: Symbol | undefined, - dontResolveAlias?: boolean - ): Symbol | undefined { - return !dontResolveAlias && isNonLocalAlias(symbol) - ? resolveAlias(symbol) - : symbol; - } - - function resolveAlias(symbol: Symbol): Symbol { - Debug.assert( - (symbol.flags & SymbolFlags.Alias) !== 0, - 'Should only get Alias here.' - ); - const links = getSymbolLinks(symbol); - if (!links.target) { - links.target = resolvingSymbol; - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - const target = getTargetOfAliasDeclaration(node); - if (links.target === resolvingSymbol) { - links.target = target || unknownSymbol; - } else { - error( - node, - Diagnostics.Circular_definition_of_import_alias_0, - symbolToString(symbol) - ); - } - } else if (links.target === resolvingSymbol) { - links.target = unknownSymbol; - } - return links.target; - } - - function markExportAsReferenced(node: ImportEqualsDeclaration - | ExportSpecifier) - { - const symbol = getSymbolOfNode(node); - const target = resolveAlias(symbol); - if (target) { - const markAlias = target === unknownSymbol - || ((target.flags & SymbolFlags.Value) - && !isConstEnumOrConstEnumOnlyModule(target)); - - if (markAlias) { - markAliasSymbolAsReferenced(symbol); - } - } - } - - // When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until - // we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of - // the alias as an expression (which recursively takes us back here if the target references another alias). - function markAliasSymbolAsReferenced(symbol: Symbol) { - const links = getSymbolLinks(symbol); - if (!links.referenced) { - links.referenced = true; - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - // We defer checking of the reference of an `import =` until the import itself is referenced, - // This way a chain of imports can be elided if ultimately the final input is only used in a type - // position. - if (isInternalModuleImportEqualsDeclaration(node)) { - const target = resolveSymbol(symbol); - if (target === unknownSymbol - || target.flags & SymbolFlags.Value) - { - // import foo = - checkExpressionCached( node - .moduleReference); - } - } - } - } - - // This function is only for imports with entity names - function getSymbolOfPartOfRightHandSideOfImportEquals( - entityName: EntityName, - dontResolveAlias?: boolean - ): Symbol | undefined { - // There are three things we might try to look for. In the following examples, - // the search term is enclosed in |...|: - // - // import a = |b|; // Namespace - // import a = |b.c|; // Value, type, namespace - // import a = |b.c|.d; // Namespace - if (entityName.kind === SyntaxKind.Identifier - && isRightSideOfQualifiedNameOrPropertyAccess(entityName)) - { - entityName = entityName.parent; - } - // Check for case 1 and 3 in the above example - if (entityName.kind === SyntaxKind.Identifier - || entityName.parent.kind === SyntaxKind.QualifiedName) - { - return resolveEntityName( - entityName, - SymbolFlags.Namespace, /*ignoreErrors*/ - false, - dontResolveAlias - ); - } else { - // Case 2 in above example - // entityName.kind could be a QualifiedName or a Missing identifier - Debug - .assert(entityName.parent.kind - === SyntaxKind.ImportEqualsDeclaration); - return resolveEntityName( - entityName, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace, /*ignoreErrors*/ - false, - dontResolveAlias - ); - } - } - - function getFullyQualifiedName( - symbol: Symbol, - containingLocation?: Node - ): string { - return symbol.parent - ? getFullyQualifiedName(symbol.parent, containingLocation) - + '.' + symbolToString(symbol) - : symbolToString( - symbol, - containingLocation, /*meaning*/ - undefined, - SymbolFormatFlags.DoNotIncludeSymbolChain - | SymbolFormatFlags.AllowAnyNodeKind - ); - } - - /** - * Resolves a qualified name and any involved aliases. - */ - function resolveEntityName( - name: EntityNameOrEntityNameExpression, - meaning: SymbolFlags, - ignoreErrors?: boolean, - dontResolveAlias?: boolean, - location?: Node - ): Symbol | undefined { - if (nodeIsMissing(name)) { - return undefined; - } - - const namespaceMeaning = - SymbolFlags.Namespace - | (isInJSFile(name) ? meaning & SymbolFlags.Value : 0); - let symbol: Symbol | undefined; - if (name.kind === SyntaxKind.Identifier) { - const message = meaning === namespaceMeaning - ? Diagnostics.Cannot_find_namespace_0 - : getCannotFindNameDiagnosticForName(getFirstIdentifier(name)); - const symbolFromJSPrototype = isInJSFile(name) - ? resolveEntityNameFromAssignmentDeclaration(name, meaning) - : undefined; - symbol = resolveName( - location || name, - name.escapedText, - meaning, - ignoreErrors || symbolFromJSPrototype - ? undefined - : message, - name, /*isUse*/ - true - ); - if (!symbol) { - return symbolFromJSPrototype; - } - } else if (name.kind === SyntaxKind.QualifiedName - || name.kind === SyntaxKind.PropertyAccessExpression) - { - const left = name.kind === SyntaxKind.QualifiedName - ? name.left - : name.expression; - const right = name.kind === SyntaxKind.QualifiedName - ? name.right - : name.name; - let namespace = resolveEntityName( - left, - namespaceMeaning, - ignoreErrors, /*dontResolveAlias*/ - false, - location - ); - if (!namespace || nodeIsMissing(right)) { - return undefined; - } else if (namespace === unknownSymbol) { - return namespace; - } - if (isInJSFile(name)) { - if (namespace.valueDeclaration - && isVariableDeclaration(namespace.valueDeclaration) - && namespace.valueDeclaration.initializer - && isCommonJsRequire(namespace.valueDeclaration - .initializer)) - { - const moduleName = - (namespace.valueDeclaration - .initializer as CallExpression).arguments - [0] as StringLiteral; - const moduleSym = resolveExternalModuleName( - moduleName, - moduleName - ); - if (moduleSym) { - const resolvedModuleSymbol = - resolveExternalModuleSymbol(moduleSym); - if (resolvedModuleSymbol) { - namespace = resolvedModuleSymbol; - } - } - } - } - symbol = getSymbol( - getExportsOfSymbol(namespace), - right.escapedText, - meaning - ); - if (!symbol) { - if (!ignoreErrors) { - error( - right, - Diagnostics.Namespace_0_has_no_exported_member_1, - getFullyQualifiedName(namespace), - declarationNameToString(right) - ); - } - return undefined; - } - } else { - throw Debug.assertNever(name, 'Unknown entity name kind.'); - } - Debug.assert( - (getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, - 'Should never get an instantiated symbol here.' - ); - return (symbol.flags & meaning) || dontResolveAlias - ? symbol - : resolveAlias(symbol); - } - - /** - * 1. For prototype-property methods like `A.prototype.m = function () ...`, try to resolve names in the scope of `A` too. - * Note that prototype-property assignment to locations outside the current file (eg globals) doesn't work, so - * name resolution won't work either. - * 2. For property assignments like `{ x: function f () { } }`, try to resolve names in the scope of `f` too. - */ - function resolveEntityNameFromAssignmentDeclaration( - name: Identifier, - meaning: SymbolFlags - ) { - if (isJSDocTypeReference(name.parent)) { - const secondaryLocation = - getAssignmentDeclarationLocation(name.parent); - if (secondaryLocation) { - return resolveName( - secondaryLocation, - name.escapedText, - meaning, /*nameNotFoundMessage*/ - undefined, - name, /*isUse*/ - true - ); - } - } - } - - function getAssignmentDeclarationLocation(node: - TypeReferenceNode): Node | undefined - { - const typeAlias = findAncestor( - node, - node => !(isJSDocNode(node) || node.flags & NodeFlags.JSDoc) - ? 'quit' - : isJSDocTypeAlias(node) - ); - if (typeAlias) { - return; - } - const host = getJSDocHost(node); - if (isExpressionStatement(host) - && isBinaryExpression(host.expression) - && getAssignmentDeclarationKind(host.expression) - === AssignmentDeclarationKind.PrototypeProperty) - { - // X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration - const symbol = getSymbolOfNode(host.expression.left); - if (symbol) { - return getDeclarationOfJSPrototypeContainer(symbol); - } - } - if ((isObjectLiteralMethod(host) || isPropertyAssignment(host)) - && isBinaryExpression(host.parent.parent) - && getAssignmentDeclarationKind(host.parent.parent) - === AssignmentDeclarationKind.Prototype) - { - // X.prototype = { /** @param {K} p */m() { } } <-- look for K on X's declaration - const symbol = getSymbolOfNode(host.parent.parent.left); - if (symbol) { - return getDeclarationOfJSPrototypeContainer(symbol); - } - } - const sig = getHostSignatureFromJSDocHost(host); - if (sig) { - const symbol = getSymbolOfNode(sig); - return symbol && symbol.valueDeclaration; - } - } - - function getDeclarationOfJSPrototypeContainer(symbol: Symbol) { - const decl = symbol.parent!.valueDeclaration; - if (!decl) { - return undefined; - } - const initializer = isAssignmentDeclaration(decl) - ? getAssignedExpandoInitializer(decl) - : hasOnlyExpressionInitializer(decl) - ? getDeclaredExpandoInitializer(decl) - : undefined; - return initializer || decl; - } - - /** - * Get the real symbol of a declaration with an expando initializer. - * - * Normally, declarations have an associated symbol, but when a declaration has an expando - * initializer, the expando's symbol is the one that has all the members merged into it. - */ - function getExpandoSymbol(symbol: Symbol): Symbol | undefined { - const decl = symbol.valueDeclaration; - if (!decl || !isInJSFile(decl) - || symbol.flags & SymbolFlags.TypeAlias) - { - return undefined; - } - const init = isVariableDeclaration(decl) - ? getDeclaredExpandoInitializer(decl) - : getAssignedExpandoInitializer(decl); - if (init) { - const initSymbol = getSymbolOfNode(init); - if (initSymbol) { - return mergeJSSymbols(initSymbol, symbol); - } - } - } - - function resolveExternalModuleName( - location: Node, - moduleReferenceExpression: Expression, - ignoreErrors?: boolean - ): Symbol | undefined { - return resolveExternalModuleNameWorker( - location, - moduleReferenceExpression, - ignoreErrors ? undefined : Diagnostics.Cannot_find_module_0 - ); - } - - function resolveExternalModuleNameWorker( - location: Node, - moduleReferenceExpression: Expression, - moduleNotFoundError: DiagnosticMessage | undefined, - isForAugmentation = false - ): Symbol | undefined { - return isStringLiteralLike(moduleReferenceExpression) - ? resolveExternalModule( - location, - moduleReferenceExpression.text, - moduleNotFoundError, - moduleReferenceExpression, - isForAugmentation - ) - : undefined; - } - - function resolveExternalModule( - location: Node, - moduleReference: string, - moduleNotFoundError: DiagnosticMessage | undefined, - errorNode: Node, - isForAugmentation = false - ): Symbol | undefined { - if (startsWith(moduleReference, '@types/')) { - const diag = Diagnostics - .Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1; - const withoutAtTypePrefix = removePrefix( - moduleReference, - '@types/' - ); - error(errorNode, diag, withoutAtTypePrefix, moduleReference); - } - - const ambientModule = tryFindAmbientModule( - moduleReference, /*withAugmentations*/ - true - ); - if (ambientModule) { - return ambientModule; - } - const currentSourceFile = getSourceFileOfNode(location); - const resolvedModule = getResolvedModule( - currentSourceFile, - moduleReference - )!; // TODO: GH#18217 - const resolutionDiagnostic = resolvedModule - && getResolutionDiagnostic(compilerOptions, resolvedModule); - const sourceFile = resolvedModule && !resolutionDiagnostic - && host.getSourceFile(resolvedModule.resolvedFileName); - if (sourceFile) { - if (sourceFile.symbol) { - if (resolvedModule.isExternalLibraryImport - && !resolutionExtensionIsTSOrJson(resolvedModule - .extension)) - { - errorOnImplicitAnyModule( - /*isError*/ false, - errorNode, - resolvedModule, - moduleReference - ); - } - // merged symbol is module declaration symbol combined with all augmentations - return getMergedSymbol(sourceFile.symbol); - } - if (moduleNotFoundError) { - // report errors only if it was requested - error( - errorNode, - Diagnostics.File_0_is_not_a_module, - sourceFile.fileName - ); - } - return undefined; - } - - if (patternAmbientModules) { - const pattern = findBestPatternMatch( - patternAmbientModules, - _ => _.pattern, - moduleReference - ); - if (pattern) { - // If the module reference matched a pattern ambient module ('*.foo') but there's also a - // module augmentation by the specific name requested ('a.foo'), we store the merged symbol - // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports - // from a.foo. - const augmentation = patternAmbientModuleAugmentations - && patternAmbientModuleAugmentations - .get(moduleReference); - if (augmentation) { - return getMergedSymbol(augmentation); - } - return getMergedSymbol(pattern.symbol); - } - } - - // May be an untyped module. If so, ignore resolutionDiagnostic. - if (resolvedModule - && !resolutionExtensionIsTSOrJson(resolvedModule.extension) - && resolutionDiagnostic === undefined - || resolutionDiagnostic - === Diagnostics - .Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) - { - if (isForAugmentation) { - const diag = Diagnostics - .Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented; - error( - errorNode, - diag, - moduleReference, - resolvedModule.resolvedFileName - ); - } else { - errorOnImplicitAnyModule( - /*isError*/ noImplicitAny && !!moduleNotFoundError, - errorNode, - resolvedModule, - moduleReference - ); - } - // Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first. - return undefined; - } - - if (moduleNotFoundError) { - // See if this was possibly a projectReference redirect - if (resolvedModule) { - const redirect = host - .getProjectReferenceRedirect(resolvedModule - .resolvedFileName); - if (redirect) { - error( - errorNode, - Diagnostics - .Output_file_0_has_not_been_built_from_source_file_1, - redirect, - resolvedModule.resolvedFileName - ); - return undefined; - } - } - - if (resolutionDiagnostic) { - error( - errorNode, - resolutionDiagnostic, - moduleReference, - resolvedModule.resolvedFileName - ); - } else { - const tsExtension = tryExtractTSExtension(moduleReference); - if (tsExtension) { - const diag = Diagnostics - .An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead; - error( - errorNode, - diag, - tsExtension, - removeExtension(moduleReference, tsExtension) - ); - } else if (!compilerOptions.resolveJsonModule - && fileExtensionIs(moduleReference, Extension.Json) - && getEmitModuleResolutionKind(compilerOptions) - === ModuleResolutionKind.NodeJs - && hasJsonModuleEmitEnabled(compilerOptions)) - { - error( - errorNode, - Diagnostics - .Cannot_find_module_0_Consider_using_resolveJsonModule_to_import_module_with_json_extension, - moduleReference - ); - } else { - error(errorNode, moduleNotFoundError, moduleReference); - } - } - } - return undefined; - } - - function errorOnImplicitAnyModule( - isError: boolean, - errorNode: Node, - { packageId, resolvedFileName }: ResolvedModuleFull, - moduleReference: string - ): void { - const errorInfo = - !isExternalModuleNameRelative(moduleReference) && packageId - ? typesPackageExists(packageId.name) - ? chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, - packageId.name, - mangleScopedPackageName(packageId.name) - ) - : chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .Try_npm_install_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, - moduleReference, - mangleScopedPackageName(packageId.name) - ) - : undefined; - errorOrSuggestion(isError, errorNode, chainDiagnosticMessages( - errorInfo, - Diagnostics - .Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type, - moduleReference, - resolvedFileName - )); - } - function typesPackageExists(packageName: string): boolean { - return getPackagesSet().has(getTypesPackageName(packageName)); - } - - function resolveExternalModuleSymbol( - moduleSymbol: Symbol, - dontResolveAlias?: boolean - ): Symbol; - function resolveExternalModuleSymbol( - moduleSymbol: Symbol | undefined, - dontResolveAlias?: boolean - ): Symbol | undefined; - function resolveExternalModuleSymbol( - moduleSymbol: Symbol, - dontResolveAlias?: boolean - ): Symbol { - if (moduleSymbol) { - const exportEquals = resolveSymbol( - moduleSymbol.exports!.get(InternalSymbolName.ExportEquals), - dontResolveAlias - ); - const exported = getCommonJsExportEquals( - getMergedSymbol(exportEquals), - getMergedSymbol(moduleSymbol) - ); - return getMergedSymbol(exported) || moduleSymbol; - } - return undefined!; - } - - function getCommonJsExportEquals( - exported: Symbol | undefined, - moduleSymbol: Symbol - ): Symbol | undefined { - if (!exported || exported === unknownSymbol - || exported === moduleSymbol - || moduleSymbol.exports!.size === 1 - || exported.flags & SymbolFlags.Alias) - { - return exported; - } - const links = getSymbolLinks(exported); - if (links.cjsExportMerged) { - return links.cjsExportMerged; - } - const merged = exported.flags & SymbolFlags.Transient - ? exported - : cloneSymbol(exported); - merged.flags = merged.flags | SymbolFlags.ValueModule; - if (merged.exports === undefined) { - merged.exports = createSymbolTable(); - } - moduleSymbol.exports!.forEach((s, name) => { - if (name === InternalSymbolName.ExportEquals) return; - merged.exports!.set( - name, - merged.exports!.has(name) - ? mergeSymbol(merged.exports!.get(name)!, s) - : s - ); - }); - getSymbolLinks(merged).cjsExportMerged = merged; - return links.cjsExportMerged = merged; - } - - // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' - // references a symbol that is at least declared as a module or a variable. The target of the 'export =' may - // combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable). - function resolveESModuleSymbol( - moduleSymbol: Symbol | undefined, - referencingLocation: Node, - dontResolveAlias: boolean, - suppressInteropError: boolean - ): Symbol | undefined { - const symbol = resolveExternalModuleSymbol( - moduleSymbol, - dontResolveAlias - ); - - if (!dontResolveAlias && symbol) { - if (!suppressInteropError - && !(symbol.flags - & (SymbolFlags.Module | SymbolFlags.Variable)) - && !getDeclarationOfKind(symbol, SyntaxKind.SourceFile)) - { - const compilerOptionName = moduleKind >= ModuleKind.ES2015 - ? 'allowSyntheticDefaultImports' - : 'esModuleInterop'; - - error( - referencingLocation, - Diagnostics - .This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, - compilerOptionName - ); - - return symbol; - } - - if (compilerOptions.esModuleInterop) { - const referenceParent = referencingLocation.parent; - if ( - (isImportDeclaration(referenceParent) - && getNamespaceDeclarationNode(referenceParent)) - || isImportCall(referenceParent) - ) { - const type = getTypeOfSymbol(symbol); - let sigs = getSignaturesOfStructuredType( - type, - SignatureKind.Call - ); - if (!sigs || !sigs.length) { - sigs = getSignaturesOfStructuredType( - type, - SignatureKind.Construct - ); - } - if (sigs && sigs.length) { - const moduleType = - getTypeWithSyntheticDefaultImportType( - type, - symbol, - moduleSymbol! - ); - // Create a new symbol which has the module's type less the call and construct signatures - const result = createSymbol( - symbol.flags, - symbol.escapedName - ); - result.declarations = symbol.declarations - ? symbol.declarations.slice() - : []; - result.parent = symbol.parent; - result.target = symbol; - result.originatingImport = referenceParent; - if (symbol.valueDeclaration) { - result.valueDeclaration = symbol - .valueDeclaration; - } - if (symbol.constEnumOnlyModule) { - result.constEnumOnlyModule = true; - } - if (symbol.members) { - result.members = cloneMap(symbol.members); - } - if (symbol.exports) { - result.exports = cloneMap(symbol.exports); - } - const resolvedModuleType = - resolveStructuredTypeMembers(moduleType as StructuredType); // Should already be resolved from the signature checks above - result.type = createAnonymousType( - result, - resolvedModuleType.members, - emptyArray, - emptyArray, - resolvedModuleType.stringIndexInfo, - resolvedModuleType.numberIndexInfo - ); - return result; - } - } - } - } - return symbol; - } - - function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports!.get(InternalSymbolName.ExportEquals) - !== undefined; - } - - function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { - return symbolsToArray(getExportsOfModule(moduleSymbol)); - } - - function getExportsAndPropertiesOfModule(moduleSymbol: - Symbol): Symbol[] - { - const exports = getExportsOfModuleAsArray(moduleSymbol); - const exportEquals = resolveExternalModuleSymbol(moduleSymbol); - if (exportEquals !== moduleSymbol) { - addRange( - exports, - getPropertiesOfType(getTypeOfSymbol(exportEquals)) - ); - } - return exports; - } - - function tryGetMemberInModuleExports( - memberName: __String, - moduleSymbol: Symbol - ): Symbol | undefined { - const symbolTable = getExportsOfModule(moduleSymbol); - if (symbolTable) { - return symbolTable.get(memberName); - } - } - - function tryGetMemberInModuleExportsAndProperties( - memberName: __String, - moduleSymbol: Symbol - ): Symbol | undefined { - const symbol = tryGetMemberInModuleExports( - memberName, - moduleSymbol - ); - if (symbol) { - return symbol; - } - - const exportEquals = resolveExternalModuleSymbol(moduleSymbol); - if (exportEquals === moduleSymbol) { - return undefined; - } - - const type = getTypeOfSymbol(exportEquals); - return type.flags & TypeFlags.Primitive - || getObjectFlags(type) & ObjectFlags.Class - || isArrayOrTupleLikeType(type) - ? undefined - : getPropertyOfType(type, memberName); - } - - function getExportsOfSymbol(symbol: Symbol): SymbolTable { - return symbol.flags & SymbolFlags.LateBindingContainer - ? getResolvedMembersOrExportsOfSymbol( - symbol, - MembersOrExportsResolutionKind.resolvedExports - ) - : symbol.flags & SymbolFlags.Module - ? getExportsOfModule(symbol) - : symbol.exports || emptySymbols; - } - - function getExportsOfModule(moduleSymbol: Symbol): SymbolTable { - const links = getSymbolLinks(moduleSymbol); - return links.resolvedExports - || (links - .resolvedExports = getExportsOfModuleWorker(moduleSymbol)); - } - - interface ExportCollisionTracker { - specifierText: string; - exportsWithDuplicate: ExportDeclaration[]; - } - - type ExportCollisionTrackerTable = UnderscoreEscapedMap; - - /** - * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument - * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables - */ - function extendExportSymbols( - target: SymbolTable, - source: SymbolTable | undefined, - lookupTable?: ExportCollisionTrackerTable, - exportNode?: ExportDeclaration - ) { - if (!source) return; - source.forEach((sourceSymbol, id) => { - if (id === InternalSymbolName.Default) return; - - const targetSymbol = target.get(id); - if (!targetSymbol) { - target.set(id, sourceSymbol); - if (lookupTable && exportNode) { - lookupTable.set(id, { - specifierText: - getTextOfNode(exportNode.moduleSpecifier!) - } as ExportCollisionTracker); - } - } else if (lookupTable && exportNode && targetSymbol - && resolveSymbol(targetSymbol) - !== resolveSymbol(sourceSymbol)) - { - const collisionTracker = lookupTable.get(id)!; - if (!collisionTracker.exportsWithDuplicate) { - collisionTracker.exportsWithDuplicate = [exportNode]; - } else { - collisionTracker.exportsWithDuplicate.push(exportNode); - } - } - }); - } - - function getExportsOfModuleWorker(moduleSymbol: Symbol): SymbolTable { - const visitedSymbols: Symbol[] = []; - - // A module defined by an 'export=' consists of one export that needs to be resolved - moduleSymbol = resolveExternalModuleSymbol(moduleSymbol); - - return visit(moduleSymbol) || emptySymbols; - - // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, - // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. - function visit(symbol: Symbol | undefined): SymbolTable - | undefined - { - if (!(symbol && symbol.exports - && pushIfUnique(visitedSymbols, symbol))) - { - return; - } - const symbols = cloneMap(symbol.exports); - // All export * declarations are collected in an __export symbol by the binder - const exportStars = symbol.exports - .get(InternalSymbolName.ExportStar); - if (exportStars) { - const nestedSymbols = createSymbolTable(); - const lookupTable = - createMap() as ExportCollisionTrackerTable; - for (const node of exportStars.declarations) { - const resolvedModule = resolveExternalModuleName( - node, - (node as ExportDeclaration).moduleSpecifier! - ); - const exportedSymbols = visit(resolvedModule); - extendExportSymbols( - nestedSymbols, - exportedSymbols, - lookupTable, - node as ExportDeclaration - ); - } - lookupTable.forEach(({ exportsWithDuplicate }, id) => { - // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if (id === 'export=' - || !(exportsWithDuplicate - && exportsWithDuplicate.length) - || symbols.has(id)) - { - return; - } - for (const node of exportsWithDuplicate) { - diagnostics.add(createDiagnosticForNode( - node, - Diagnostics - .Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, - lookupTable.get(id)!.specifierText, - unescapeLeadingUnderscores(id) - )); - } - }); - extendExportSymbols(symbols, nestedSymbols); - } - return symbols; - } - } - - function getMergedSymbol(symbol: Symbol): Symbol; - function getMergedSymbol(symbol: Symbol | undefined): Symbol - | undefined; - function getMergedSymbol(symbol: Symbol | undefined): Symbol - | undefined - { - let merged: Symbol; - return symbol && symbol.mergeId - && (merged = mergedSymbols[symbol.mergeId]) - ? merged - : symbol; - } - - function getSymbolOfNode(node: Declaration): Symbol; - function getSymbolOfNode(node: Node): Symbol | undefined; - function getSymbolOfNode(node: Node): Symbol | undefined { - return getMergedSymbol(node.symbol - && getLateBoundSymbol(node.symbol)); - } - - function getParentOfSymbol(symbol: Symbol): Symbol | undefined { - return getMergedSymbol(symbol.parent - && getLateBoundSymbol(symbol.parent)); - } - - function getAlternativeContainingModules( - symbol: Symbol, - enclosingDeclaration: Node - ): Symbol[] { - const containingFile = getSourceFileOfNode(enclosingDeclaration); - const id = '' + getNodeId(containingFile); - const links = getSymbolLinks(symbol); - let results: Symbol[] | undefined; - if (links.extendedContainersByFile - && (results = links.extendedContainersByFile.get(id))) - { - return results; - } - if (containingFile && containingFile.imports) { - // Try to make an import using an import already in the enclosing file, if possible - for (const importRef of containingFile.imports) { - if (nodeIsSynthesized(importRef)) continue; // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error - const resolvedModule = resolveExternalModuleName( - enclosingDeclaration, - importRef, /*ignoreErrors*/ - true - ); - if (!resolvedModule) continue; - const ref = getAliasForSymbolInContainer( - resolvedModule, - symbol - ); - if (!ref) continue; - results = append(results, resolvedModule); - } - if (length(results)) { - (links.extendedContainersByFile - || (links.extendedContainersByFile = createMap())).set( - id, - results! - ); - return results!; - } - } - if (links.extendedContainers) { - return links.extendedContainers; - } - // No results from files already being imported by this file - expand search (expensive, but not location-specific, so cached) - const otherFiles = host.getSourceFiles(); - for (const file of otherFiles) { - if (!isExternalModule(file)) continue; - const sym = getSymbolOfNode(file); - const ref = getAliasForSymbolInContainer(sym, symbol); - if (!ref) continue; - results = append(results, sym); - } - return links.extendedContainers = results || emptyArray; - } - - /** - * Attempts to find the symbol corresponding to the container a symbol is in - usually this - * is just its' `.parent`, but for locals, this value is `undefined` - */ - function getContainersOfSymbol( - symbol: Symbol, - enclosingDeclaration: Node | undefined - ): Symbol[] | undefined { - const container = getParentOfSymbol(symbol); - // Type parameters end up in the `members` lists but are not externally visible - if (container && !(symbol.flags & SymbolFlags.TypeParameter)) { - const additionalContainers = mapDefined( - container.declarations, - fileSymbolIfFileSymbolExportEqualsContainer - ); - const reexportContainers = enclosingDeclaration - && getAlternativeContainingModules( - symbol, - enclosingDeclaration - ); - if (enclosingDeclaration - && getAccessibleSymbolChain( - container, - enclosingDeclaration, - SymbolFlags.Namespace, /*externalOnly*/ - false - )) - { - return concatenate( - concatenate([container], additionalContainers), - reexportContainers - ); // This order expresses a preference for the real container if it is in scope - } - const res = append(additionalContainers, container); - return concatenate(res, reexportContainers); - } - const candidates = mapDefined(symbol.declarations, d => { - if (!isAmbientModule(d) && d.parent - && hasNonGlobalAugmentationExternalModuleSymbol(d.parent)) - { - return getSymbolOfNode(d.parent); - } - if (isClassExpression(d) && isBinaryExpression(d.parent) - && d.parent.operatorToken.kind === SyntaxKind.EqualsToken - && isAccessExpression(d.parent.left) - && isEntityNameExpression(d.parent.left.expression)) - { - if (isModuleExportsAccessExpression(d.parent.left) - || isExportsIdentifier(d.parent.left.expression)) - { - return getSymbolOfNode(getSourceFileOfNode(d)); - } - checkExpressionCached(d.parent.left.expression); - return getNodeLinks(d.parent.left.expression) - .resolvedSymbol; - } - }); - if (!length(candidates)) { - return undefined; - } - return mapDefined( - candidates, - candidate => getAliasForSymbolInContainer(candidate, symbol) - ? candidate - : undefined - ); - - function fileSymbolIfFileSymbolExportEqualsContainer(d: - Declaration) - { - const fileSymbol = getExternalModuleContainer(d); - const exported = fileSymbol && fileSymbol.exports - && fileSymbol.exports.get(InternalSymbolName.ExportEquals); - return exported && container - && getSymbolIfSameReference(exported, container) - ? fileSymbol - : undefined; - } - } - - function getAliasForSymbolInContainer( - container: Symbol, - symbol: Symbol - ) { - if (container === getParentOfSymbol(symbol)) { - // fast path, `symbol` is either already the alias or isn't aliased - return symbol; - } - // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return - // the container itself as the alias for the symbol - const exportEquals = container.exports - && container.exports.get(InternalSymbolName.ExportEquals); - if (exportEquals - && getSymbolIfSameReference(exportEquals, symbol)) - { - return container; - } - const exports = getExportsOfSymbol(container); - const quick = exports.get(symbol.escapedName); - if (quick && getSymbolIfSameReference(quick, symbol)) { - return quick; - } - return forEachEntry(exports, exported => { - if (getSymbolIfSameReference(exported, symbol)) { - return exported; - } - }); - } - - /** - * Checks if two symbols, through aliasing and/or merging, refer to the same thing - */ - function getSymbolIfSameReference(s1: Symbol, s2: Symbol) { - if (getMergedSymbol(resolveSymbol(getMergedSymbol(s1))) - === getMergedSymbol(resolveSymbol(getMergedSymbol(s2)))) - { - return s1; - } - } - - function getExportSymbolOfValueSymbolIfExported(symbol: - Symbol): Symbol; - function getExportSymbolOfValueSymbolIfExported(symbol: Symbol - | undefined): Symbol | undefined; - function getExportSymbolOfValueSymbolIfExported(symbol: Symbol - | undefined): Symbol | undefined - { - return getMergedSymbol(symbol - && (symbol.flags & SymbolFlags.ExportValue) !== 0 - ? symbol.exportSymbol - : symbol); - } - - function symbolIsValue(symbol: Symbol): boolean { - return !!(symbol.flags & SymbolFlags.Value - || symbol.flags & SymbolFlags.Alias - && resolveAlias(symbol).flags & SymbolFlags.Value); - } - - function findConstructorDeclaration(node: - ClassLikeDeclaration): ConstructorDeclaration | undefined - { - const members = node.members; - for (const member of members) { - if (member.kind === SyntaxKind.Constructor - && nodeIsPresent(( member).body)) - { - return member; - } - } - } - - function createType(flags: TypeFlags): Type { - const result = new Type(checker, flags); - typeCount++; - result.id = typeCount; - return result; - } - - function createIntrinsicType( - kind: TypeFlags, - intrinsicName: string, - objectFlags: ObjectFlags = 0 - ): IntrinsicType { - const type = createType(kind); - type.intrinsicName = intrinsicName; - type.objectFlags = objectFlags; - return type; - } - - function createBooleanType(trueFalseTypes: - readonly Type[]): IntrinsicType & UnionType - { - const type = getUnionType(trueFalseTypes); - type.flags |= TypeFlags.Boolean; - type.intrinsicName = 'boolean'; - return type; - } - - function createObjectType( - objectFlags: ObjectFlags, - symbol?: Symbol - ): ObjectType { - const type = createType(TypeFlags.Object); - type.objectFlags = objectFlags; - type.symbol = symbol!; - type.members = undefined; - type.properties = undefined; - type.callSignatures = undefined; - type.constructSignatures = undefined; - type.stringIndexInfo = undefined; - type.numberIndexInfo = undefined; - return type; - } - - function createTypeofType() { - return getUnionType(arrayFrom( - typeofEQFacts.keys(), - getLiteralType - )); - } - - function createTypeParameter(symbol?: Symbol) { - const type = createType(TypeFlags.TypeParameter); - if (symbol) type.symbol = symbol; - return type; - } - - // A reserved member name starts with two underscores, but the third character cannot be an underscore, - // @, or #. A third underscore indicates an escaped form of an identifier that started - // with at least two underscores. The @ character indicates that the name is denoted by a well known ES - // Symbol instance and the # character indicates that the name is a PrivateIdentifier. - function isReservedMemberName(name: __String) { - return (name as string).charCodeAt(0) === CharacterCodes._ - && (name as string).charCodeAt(1) === CharacterCodes._ - && (name as string).charCodeAt(2) !== CharacterCodes._ - && (name as string).charCodeAt(2) !== CharacterCodes.at - && (name as string).charCodeAt(2) !== CharacterCodes.hash; - } - - function getNamedMembers(members: SymbolTable): Symbol[] { - let result: Symbol[] | undefined; - members.forEach((symbol, id) => { - if (!isReservedMemberName(id) && symbolIsValue(symbol)) { - (result || (result = [])).push(symbol); - } - }); - return result || emptyArray; - } - - function setStructuredTypeMembers( - type: StructuredType, - members: SymbolTable, - callSignatures: readonly Signature[], - constructSignatures: readonly Signature[], - stringIndexInfo: IndexInfo | undefined, - numberIndexInfo: IndexInfo | undefined - ): ResolvedType { - ( type).members = members; - ( type).properties = members === emptySymbols - ? emptyArray - : getNamedMembers(members); - ( type).callSignatures = callSignatures; - ( type).constructSignatures = constructSignatures; - ( type).stringIndexInfo = stringIndexInfo; - ( type).numberIndexInfo = numberIndexInfo; - return type; - } - - function createAnonymousType( - symbol: Symbol | undefined, - members: SymbolTable, - callSignatures: readonly Signature[], - constructSignatures: readonly Signature[], - stringIndexInfo: IndexInfo | undefined, - numberIndexInfo: IndexInfo | undefined - ): ResolvedType { - return setStructuredTypeMembers( - createObjectType(ObjectFlags.Anonymous, symbol), - members, - callSignatures, - constructSignatures, - stringIndexInfo, - numberIndexInfo - ); - } - - function forEachSymbolTableInScope( - enclosingDeclaration: Node | undefined, - callback: (symbolTable: SymbolTable) => T - ): T { - let result: T; - for (let location = enclosingDeclaration; location; - location = location.parent) - { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if (location.locals && !isGlobalSourceFile(location)) { - if (result = callback(location.locals)) { - return result; - } - } - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule( location)) { - break; - } - // falls through - case SyntaxKind.ModuleDeclaration: - const sym = - getSymbolOfNode(location as ModuleDeclaration); - // `sym` may not have exports if this module declaration is backed by the symbol for a `const` that's being rewritten - // into a namespace - in such cases, it's best to just let the namespace appear empty (the const members couldn't have referred - // to one another anyway) - if (result = callback(sym.exports || emptySymbols)) { - return result; - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - // Type parameters are bound into `members` lists so they can merge across declarations - // This is troublesome, since in all other respects, they behave like locals :cries: - // TODO: the below is shared with similar code in `resolveName` - in fact, rephrasing all this symbol - // lookup logic in terms of `resolveName` would be nice - // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals - // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would - // trigger resolving late-bound names, which we may already be in the process of doing while we're here! - let table: UnderscoreEscapedMap | undefined; - // TODO: Should this filtered table be cached in some way? - (getSymbolOfNode(location as ClassLikeDeclaration - | InterfaceDeclaration).members || emptySymbols) - .forEach((memberSymbol, key) => { - if (memberSymbol.flags - & (SymbolFlags.Type - & ~SymbolFlags.Assignment)) - { - (table || (table = createSymbolTable())) - .set(key, memberSymbol); - } - }); - if (table && (result = callback(table))) { - return result; - } - break; - } - } - - return callback(globals); - } - - function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) { - // If we are looking in value space, the parent meaning is value, other wise it is namespace - return rightMeaning === SymbolFlags.Value - ? SymbolFlags.Value - : SymbolFlags.Namespace; - } - - function getAccessibleSymbolChain( - symbol: Symbol | undefined, - enclosingDeclaration: Node | undefined, - meaning: SymbolFlags, - useOnlyExternalAliasing: boolean, - visitedSymbolTablesMap: Map = createMap() - ): Symbol[] | undefined { - if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) { - return undefined; - } - - const id = '' + getSymbolId(symbol); - let visitedSymbolTables = visitedSymbolTablesMap.get(id); - if (!visitedSymbolTables) { - visitedSymbolTablesMap.set(id, visitedSymbolTables = []); - } - return forEachSymbolTableInScope( - enclosingDeclaration, - getAccessibleSymbolChainFromSymbolTable - ); - - /** - * @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) - */ - function getAccessibleSymbolChainFromSymbolTable( - symbols: SymbolTable, - ignoreQualification?: boolean - ): Symbol[] | undefined { - if (!pushIfUnique(visitedSymbolTables!, symbols)) { - return undefined; - } - - const result = trySymbolTable(symbols, ignoreQualification); - visitedSymbolTables!.pop(); - return result; - } - - function canQualifySymbol( - symbolFromSymbolTable: Symbol, - meaning: SymbolFlags - ) { - // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible - return !needsQualification( - symbolFromSymbolTable, - enclosingDeclaration, - meaning - ) - // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too - || !!getAccessibleSymbolChain( - symbolFromSymbolTable.parent, - enclosingDeclaration, - getQualifiedLeftMeaning(meaning), - useOnlyExternalAliasing, - visitedSymbolTablesMap - ); - } - - function isAccessible( - symbolFromSymbolTable: Symbol, - resolvedAliasSymbol?: Symbol, - ignoreQualification?: boolean - ) { - return (symbol - === (resolvedAliasSymbol || symbolFromSymbolTable) - || getMergedSymbol(symbol) - === getMergedSymbol(resolvedAliasSymbol - || symbolFromSymbolTable)) - // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) - // and if symbolFromSymbolTable or alias resolution matches the symbol, - // check the symbol can be qualified, it is only then this symbol is accessible - && !some( - symbolFromSymbolTable.declarations, - hasNonGlobalAugmentationExternalModuleSymbol - ) - && (ignoreQualification - || canQualifySymbol( - getMergedSymbol(symbolFromSymbolTable), - meaning - )); - } - - function trySymbolTable( - symbols: SymbolTable, - ignoreQualification: boolean | undefined - ): Symbol[] | undefined { - // If symbol is directly available by its name in the symbol table - if (isAccessible( - symbols.get(symbol!.escapedName)!, /*resolvedAliasSymbol*/ - undefined, - ignoreQualification - )) { - return [symbol!]; - } - - // Check if symbol is any of the aliases in scope - const result = forEachEntry(symbols, symbolFromSymbolTable => { - if (symbolFromSymbolTable.flags & SymbolFlags.Alias - && symbolFromSymbolTable.escapedName - !== InternalSymbolName.ExportEquals - && symbolFromSymbolTable.escapedName - !== InternalSymbolName.Default - && !(isUMDExportSymbol(symbolFromSymbolTable) - && enclosingDeclaration - && isExternalModule(getSourceFileOfNode(enclosingDeclaration))) - // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name - && (!useOnlyExternalAliasing - || some( - symbolFromSymbolTable.declarations, - isExternalModuleImportEqualsDeclaration - )) - // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ - // See similar comment in `resolveName` for details - && (ignoreQualification - || !getDeclarationOfKind( - symbolFromSymbolTable, - SyntaxKind.ExportSpecifier - ))) - { - const resolvedImportedSymbol = - resolveAlias(symbolFromSymbolTable); - const candidate = getCandidateListForSymbol( - symbolFromSymbolTable, - resolvedImportedSymbol, - ignoreQualification - ); - if (candidate) { - return candidate; - } - } - if (symbolFromSymbolTable.escapedName - === symbol!.escapedName - && symbolFromSymbolTable.exportSymbol) - { - if (isAccessible( - getMergedSymbol(symbolFromSymbolTable - .exportSymbol), /*aliasSymbol*/ - undefined, - ignoreQualification - )) { - return [symbol!]; - } - } - }); - - // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that - return result - || (symbols === globals - ? getCandidateListForSymbol( - globalThisSymbol, - globalThisSymbol, - ignoreQualification - ) - : undefined); - } - - function getCandidateListForSymbol( - symbolFromSymbolTable: Symbol, - resolvedImportedSymbol: Symbol, - ignoreQualification: boolean | undefined - ) { - if (isAccessible( - symbolFromSymbolTable, - resolvedImportedSymbol, - ignoreQualification - )) { - return [symbolFromSymbolTable]; - } - - // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain - // but only if the symbolFromSymbolTable can be qualified - const candidateTable = - getExportsOfSymbol(resolvedImportedSymbol); - const accessibleSymbolsFromExports = candidateTable - && getAccessibleSymbolChainFromSymbolTable( - candidateTable, /*ignoreQualification*/ - true - ); - if (accessibleSymbolsFromExports - && canQualifySymbol( - symbolFromSymbolTable, - getQualifiedLeftMeaning(meaning) - )) - { - return [symbolFromSymbolTable] - .concat(accessibleSymbolsFromExports); - } - } - } - - function needsQualification( - symbol: Symbol, - enclosingDeclaration: Node | undefined, - meaning: SymbolFlags - ) { - let qualify = false; - forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { - // If symbol of this name is not available in the symbol table we are ok - let symbolFromSymbolTable = - getMergedSymbol(symbolTable.get(symbol.escapedName)); - if (!symbolFromSymbolTable) { - // Continue to the next symbol table - return false; - } - // If the symbol with this name is present it should refer to the symbol - if (symbolFromSymbolTable === symbol) { - // No need to qualify - return true; - } - - // Qualify if the symbol from symbol table has same meaning as expected - symbolFromSymbolTable = (symbolFromSymbolTable.flags - & SymbolFlags.Alias - && !getDeclarationOfKind( - symbolFromSymbolTable, - SyntaxKind.ExportSpecifier - )) - ? resolveAlias(symbolFromSymbolTable) - : symbolFromSymbolTable; - if (symbolFromSymbolTable.flags & meaning) { - qualify = true; - return true; - } - - // Continue to the next symbol table - return false; - }); - - return qualify; - } - - function isPropertyOrMethodDeclarationSymbol(symbol: Symbol) { - if (symbol.declarations && symbol.declarations.length) { - for (const declaration of symbol.declarations) { - switch (declaration.kind) { - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - continue; - default: - return false; - } - } - return true; - } - return false; - } - - function isTypeSymbolAccessible( - typeSymbol: Symbol, - enclosingDeclaration: Node | undefined - ): boolean { - const access = isSymbolAccessible( - typeSymbol, - enclosingDeclaration, - SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ - false - ); - return access.accessibility === SymbolAccessibility.Accessible; - } - - function isValueSymbolAccessible( - typeSymbol: Symbol, - enclosingDeclaration: Node | undefined - ): boolean { - const access = isSymbolAccessible( - typeSymbol, - enclosingDeclaration, - SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ - false - ); - return access.accessibility === SymbolAccessibility.Accessible; - } - - function isAnySymbolAccessible( - symbols: Symbol[] | undefined, - enclosingDeclaration: Node | undefined, - initialSymbol: Symbol, - meaning: SymbolFlags, - shouldComputeAliasesToMakeVisible: boolean - ): SymbolAccessibilityResult | undefined { - if (!length(symbols)) return; - - let hadAccessibleChain: Symbol | undefined; - for (const symbol of symbols!) { - // Symbol is accessible if it by itself is accessible - const accessibleSymbolChain = getAccessibleSymbolChain( - symbol, - enclosingDeclaration, - meaning, /*useOnlyExternalAliasing*/ - false - ); - if (accessibleSymbolChain) { - hadAccessibleChain = symbol; - const hasAccessibleDeclarations = hasVisibleDeclarations( - accessibleSymbolChain[0], - shouldComputeAliasesToMakeVisible - ); - if (hasAccessibleDeclarations) { - return hasAccessibleDeclarations; - } - } else { - if (some( - symbol.declarations, - hasNonGlobalAugmentationExternalModuleSymbol - )) { - // Any meaning of a module symbol is always accessible via an `import` type - return { - accessibility: SymbolAccessibility.Accessible - }; - } - } - - // If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible. - // It could be a qualified symbol and hence verify the path - // e.g.: - // module m { - // export class c { - // } - // } - // const x: typeof m.c - // In the above example when we start with checking if typeof m.c symbol is accessible, - // we are going to see if c can be accessed in scope directly. - // But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible - // It is accessible if the parent m is accessible because then m.c can be accessed through qualification - - let containers = getContainersOfSymbol( - symbol, - enclosingDeclaration - ); - // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct - // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, - // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. - const firstDecl: Node = first(symbol.declarations); - if (!length(containers) && meaning & SymbolFlags.Value - && firstDecl && isObjectLiteralExpression(firstDecl)) - { - if (firstDecl.parent - && isVariableDeclaration(firstDecl.parent) - && firstDecl === firstDecl.parent.initializer) - { - containers = [getSymbolOfNode(firstDecl.parent)]; - } - } - const parentResult = isAnySymbolAccessible( - containers, - enclosingDeclaration, - initialSymbol, - initialSymbol === symbol - ? getQualifiedLeftMeaning(meaning) - : meaning, - shouldComputeAliasesToMakeVisible - ); - if (parentResult) { - return parentResult; - } - } - - if (hadAccessibleChain) { - return { - accessibility: SymbolAccessibility.NotAccessible, - errorSymbolName: symbolToString( - initialSymbol, - enclosingDeclaration, - meaning - ), - errorModuleName: hadAccessibleChain !== initialSymbol - ? symbolToString( - hadAccessibleChain, - enclosingDeclaration, - SymbolFlags.Namespace - ) - : undefined - }; - } - } - - /** - * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested - * - * @param symbol a Symbol to check if accessible - * @param enclosingDeclaration a Node containing reference to the symbol - * @param meaning a SymbolFlags to check if such meaning of the symbol is accessible - * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible - */ - function isSymbolAccessible( - symbol: Symbol | undefined, - enclosingDeclaration: Node | undefined, - meaning: SymbolFlags, - shouldComputeAliasesToMakeVisible: boolean - ): SymbolAccessibilityResult { - if (symbol && enclosingDeclaration) { - const result = isAnySymbolAccessible( - [symbol], - enclosingDeclaration, - symbol, - meaning, - shouldComputeAliasesToMakeVisible - ); - if (result) { - return result; - } - - // This could be a symbol that is not exported in the external module - // or it could be a symbol from different external module that is not aliased and hence cannot be named - const symbolExternalModule = forEach( - symbol.declarations, - getExternalModuleContainer - ); - if (symbolExternalModule) { - const enclosingExternalModule = - getExternalModuleContainer(enclosingDeclaration); - if (symbolExternalModule !== enclosingExternalModule) { - // name from different external module that is not visible - return { - accessibility: SymbolAccessibility.CannotBeNamed, - errorSymbolName: symbolToString( - symbol, - enclosingDeclaration, - meaning - ), - errorModuleName: - symbolToString(symbolExternalModule) - }; - } - } - - // Just a local name that is not accessible - return { - accessibility: SymbolAccessibility.NotAccessible, - errorSymbolName: symbolToString( - symbol, - enclosingDeclaration, - meaning - ) - }; - } - - return { accessibility: SymbolAccessibility.Accessible }; - } - - function getExternalModuleContainer(declaration: Node) { - const node = findAncestor(declaration, hasExternalModuleSymbol); - return node && getSymbolOfNode(node); - } - - function hasExternalModuleSymbol(declaration: Node) { - return isAmbientModule(declaration) - || (declaration.kind === SyntaxKind.SourceFile - && isExternalOrCommonJsModule( declaration)); - } - - function hasNonGlobalAugmentationExternalModuleSymbol(declaration: - Node) - { - return isModuleWithStringLiteralName(declaration) - || (declaration.kind === SyntaxKind.SourceFile - && isExternalOrCommonJsModule( declaration)); - } - - function hasVisibleDeclarations( - symbol: Symbol, - shouldComputeAliasToMakeVisible: boolean - ): SymbolVisibilityResult | undefined { - let aliasesToMakeVisible: LateVisibilityPaintedStatement[] - | undefined; - if (!every( - filter( - symbol.declarations, - d => d.kind !== SyntaxKind.Identifier - ), - getIsDeclarationVisible - )) { - return undefined; - } - return { accessibility: SymbolAccessibility.Accessible, - aliasesToMakeVisible }; - - function getIsDeclarationVisible(declaration: Declaration) { - if (!isDeclarationVisible(declaration)) { - // Mark the unexported alias as visible if its parent is visible - // because these kind of aliases can be used to name types in declaration file - - const anyImportSyntax = getAnyImportSyntax(declaration); - if (anyImportSyntax - && !hasModifier( - anyImportSyntax, - ModifierFlags.Export - ) // import clause without export - && isDeclarationVisible(anyImportSyntax.parent)) - { - return addVisibleAlias(declaration, anyImportSyntax); - } else if (isVariableDeclaration(declaration) - && isVariableStatement(declaration.parent.parent) - && !hasModifier( - declaration.parent.parent, - ModifierFlags.Export - ) // unexported variable statement - && isDeclarationVisible(declaration.parent.parent - .parent)) - { - return addVisibleAlias( - declaration, - declaration.parent.parent - ); - } else if (isLateVisibilityPaintedStatement(declaration) // unexported top-level statement - && !hasModifier(declaration, ModifierFlags.Export) - && isDeclarationVisible(declaration.parent)) - { - return addVisibleAlias(declaration, declaration); - } - - // Declaration is not visible - return false; - } - - return true; - } - - function addVisibleAlias( - declaration: Declaration, - aliasingStatement: LateVisibilityPaintedStatement - ) { - // In function "buildTypeDisplay" where we decide whether to write type-alias or serialize types, - // we want to just check if type- alias is accessible or not but we don't care about emitting those alias at that time - // since we will do the emitting later in trackSymbol. - if (shouldComputeAliasToMakeVisible) { - getNodeLinks(declaration).isVisible = true; - aliasesToMakeVisible = appendIfUnique( - aliasesToMakeVisible, - aliasingStatement - ); - } - return true; - } - } - - function isEntityNameVisible( - entityName: EntityNameOrEntityNameExpression, - enclosingDeclaration: Node - ): SymbolVisibilityResult { - // get symbol of the first identifier of the entityName - let meaning: SymbolFlags; - if (entityName.parent.kind === SyntaxKind.TypeQuery - || isExpressionWithTypeArgumentsInClassExtendsClause(entityName - .parent) - || entityName.parent.kind === SyntaxKind.ComputedPropertyName) - { - // Typeof value - meaning = SymbolFlags.Value | SymbolFlags.ExportValue; - } else if (entityName.kind === SyntaxKind.QualifiedName - || entityName.kind === SyntaxKind.PropertyAccessExpression - || entityName.parent.kind - === SyntaxKind.ImportEqualsDeclaration) - { - // Left identifier from type reference or TypeAlias - // Entity name of the import declaration - meaning = SymbolFlags.Namespace; - } else { - // Type Reference or TypeAlias entity = Identifier - meaning = SymbolFlags.Type; - } - - const firstIdentifier = getFirstIdentifier(entityName); - const symbol = resolveName( - enclosingDeclaration, - firstIdentifier.escapedText, - meaning, /*nodeNotFoundErrorMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - ); - - // Verify if the symbol is accessible - return (symbol - && hasVisibleDeclarations( - symbol, /*shouldComputeAliasToMakeVisible*/ - true - )) || { - accessibility: SymbolAccessibility.NotAccessible, - errorSymbolName: getTextOfNode(firstIdentifier), - errorNode: firstIdentifier - }; - } - - function symbolToString( - symbol: Symbol, - enclosingDeclaration?: Node, - meaning?: SymbolFlags, - flags: SymbolFormatFlags = SymbolFormatFlags.AllowAnyNodeKind, - writer?: EmitTextWriter - ): string { - let nodeFlags = NodeBuilderFlags.IgnoreErrors; - if (flags & SymbolFormatFlags.UseOnlyExternalAliasing) { - nodeFlags |= NodeBuilderFlags.UseOnlyExternalAliasing; - } - if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) { - nodeFlags |= NodeBuilderFlags - .WriteTypeParametersInQualifiedName; - } - if (flags & SymbolFormatFlags.UseAliasDefinedOutsideCurrentScope) { - nodeFlags |= NodeBuilderFlags - .UseAliasDefinedOutsideCurrentScope; - } - if (flags & SymbolFormatFlags.DoNotIncludeSymbolChain) { - nodeFlags |= NodeBuilderFlags.DoNotIncludeSymbolChain; - } - const builder = flags & SymbolFormatFlags.AllowAnyNodeKind - ? nodeBuilder.symbolToExpression - : nodeBuilder.symbolToEntityName; - return writer - ? symbolToStringWorker(writer).getText() - : usingSingleLineStringWriter(symbolToStringWorker); - - function symbolToStringWorker(writer: EmitTextWriter) { - const entity = builder( - symbol, - meaning!, - enclosingDeclaration, - nodeFlags - )!; // TODO: GH#18217 - const printer = createPrinter({ removeComments: true }); - const sourceFile = enclosingDeclaration - && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode( - EmitHint.Unspecified, - entity, /*sourceFile*/ - sourceFile, - writer - ); - return writer; - } - } - - function signatureToString( - signature: Signature, - enclosingDeclaration?: Node, - flags = TypeFormatFlags.None, - kind?: SignatureKind, - writer?: EmitTextWriter - ): string { - return writer - ? signatureToStringWorker(writer).getText() - : usingSingleLineStringWriter(signatureToStringWorker); - - function signatureToStringWorker(writer: EmitTextWriter) { - let sigOutput: SyntaxKind; - if (flags & TypeFormatFlags.WriteArrowStyleSignature) { - sigOutput = kind === SignatureKind.Construct - ? SyntaxKind.ConstructorType - : SyntaxKind.FunctionType; - } else { - sigOutput = kind === SignatureKind.Construct - ? SyntaxKind.ConstructSignature - : SyntaxKind.CallSignature; - } - const sig = nodeBuilder.signatureToSignatureDeclaration( - signature, - sigOutput, - enclosingDeclaration, - toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors - | NodeBuilderFlags.WriteTypeParametersInQualifiedName - ); - const printer = - createPrinter({ removeComments: true, - omitTrailingSemicolon: true }); - const sourceFile = enclosingDeclaration - && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode( - EmitHint.Unspecified, - sig!, /*sourceFile*/ - sourceFile, - getTrailingSemicolonDeferringWriter(writer) - ); // TODO: GH#18217 - return writer; - } - } - - function typeToString( - type: Type, - enclosingDeclaration?: Node, - flags: TypeFormatFlags - = TypeFormatFlags.AllowUniqueESSymbolType - | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, - writer: EmitTextWriter = createTextWriter('') - ): string { - const noTruncation = compilerOptions.noErrorTruncation - || flags & TypeFormatFlags.NoTruncation; - const typeNode = nodeBuilder.typeToTypeNode( - type, - enclosingDeclaration, - toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors - | (noTruncation ? NodeBuilderFlags.NoTruncation : 0), - writer - ); - if (typeNode === undefined) { - return Debug.fail('should always get typenode'); - } - const options = { removeComments: true }; - const printer = createPrinter(options); - const sourceFile = enclosingDeclaration - && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode( - EmitHint.Unspecified, - typeNode, /*sourceFile*/ - sourceFile, - writer - ); - const result = writer.getText(); - - const maxLength = noTruncation - ? undefined - : defaultMaximumTruncationLength * 2; - if (maxLength && result && result.length >= maxLength) { - return result.substr(0, maxLength - '...'.length) + '...'; - } - return result; - } - - function getTypeNamesForErrorDisplay(left: Type, right: Type): [string, - string] - { - let leftStr = symbolValueDeclarationIsContextSensitive(left.symbol) - ? typeToString(left, left.symbol.valueDeclaration) - : typeToString(left); - let rightStr = - symbolValueDeclarationIsContextSensitive(right.symbol) - ? typeToString(right, right.symbol.valueDeclaration) - : typeToString(right); - if (leftStr === rightStr) { - leftStr = typeToString( - left, /*enclosingDeclaration*/ - undefined, - TypeFormatFlags.UseFullyQualifiedType - ); - rightStr = typeToString( - right, /*enclosingDeclaration*/ - undefined, - TypeFormatFlags.UseFullyQualifiedType - ); - } - return [leftStr, rightStr]; - } - - function symbolValueDeclarationIsContextSensitive(symbol: - Symbol): boolean - { - return symbol && symbol.valueDeclaration - && isExpression(symbol.valueDeclaration) - && !isContextSensitive(symbol.valueDeclaration); - } - - function toNodeBuilderFlags(flags = TypeFormatFlags - .None): NodeBuilderFlags - { - return flags & TypeFormatFlags.NodeBuilderFlagsMask; - } - - function createNodeBuilder() { - return { - typeToTypeNode: ( - type: Type, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => typeToTypeNodeHelper(type, context) - ), - indexInfoToIndexSignatureDeclaration: ( - indexInfo: IndexInfo, - kind: IndexKind, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => indexInfoToIndexSignatureDeclarationHelper( - indexInfo, - kind, - context - ) - ), - signatureToSignatureDeclaration: ( - signature: Signature, - kind: SyntaxKind, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => signatureToSignatureDeclarationHelper( - signature, - kind, - context - ) - ), - symbolToEntityName: ( - symbol: Symbol, - meaning: SymbolFlags, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => symbolToName( - symbol, - context, - meaning, /*expectsIdentifier*/ - false - ) - ), - symbolToExpression: ( - symbol: Symbol, - meaning: SymbolFlags, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => symbolToExpression(symbol, context, meaning) - ), - symbolToTypeParameterDeclarations: ( - symbol: Symbol, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => typeParametersToTypeParameterDeclarations( - symbol, - context - ) - ), - symbolToParameterDeclaration: ( - symbol: Symbol, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => symbolToParameterDeclaration(symbol, context) - ), - typeParameterToDeclaration: ( - parameter: TypeParameter, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => typeParameterToDeclaration(parameter, context) - ), - symbolTableToDeclarationStatements: ( - symbolTable: SymbolTable, - enclosingDeclaration?: Node, - flags?: NodeBuilderFlags, - tracker?: SymbolTracker, - bundled?: boolean - ) => withContext( - enclosingDeclaration, - flags, - tracker, - context => symbolTableToDeclarationStatements( - symbolTable, - context, - bundled - ) - ) - }; - - function withContext( - enclosingDeclaration: Node | undefined, - flags: NodeBuilderFlags | undefined, - tracker: SymbolTracker | undefined, - cb: (context: NodeBuilderContext) => T - ): T | undefined { - Debug - .assert(enclosingDeclaration === undefined - || (enclosingDeclaration.flags & NodeFlags.Synthesized) - === 0); - const context: NodeBuilderContext = { - enclosingDeclaration, - flags: flags || NodeBuilderFlags.None, - // If no full tracker is provided, fake up a dummy one with a basic limited-functionality moduleResolverHost - tracker: tracker && tracker.trackSymbol - ? tracker - : { trackSymbol: noop, - moduleResolverHost: - flags! - & NodeBuilderFlags.DoNotIncludeSymbolChain - ? { - getCommonSourceDirectory: - (host as Program) - .getCommonSourceDirectory - ? () => (host as Program) - .getCommonSourceDirectory() - : () => '', - getSourceFiles: () => host - .getSourceFiles(), - getCurrentDirectory: maybeBind( - host, - host.getCurrentDirectory - ), - getProbableSymlinks: maybeBind( - host, - host.getProbableSymlinks - ) - } - : undefined }, - encounteredError: false, - visitedTypes: undefined, - symbolDepth: undefined, - inferTypeParameters: undefined, - approximateLength: 0 - }; - const resultingNode = cb(context); - return context.encounteredError ? undefined : resultingNode; - } - - function checkTruncationLength(context: - NodeBuilderContext): boolean - { - if (context.truncating) return context.truncating; - return context - .truncating = !(context.flags - & NodeBuilderFlags.NoTruncation) - && context.approximateLength - > defaultMaximumTruncationLength; - } - - function typeToTypeNodeHelper( - type: Type, - context: NodeBuilderContext - ): TypeNode { - if (cancellationToken - && cancellationToken.throwIfCancellationRequested) - { - cancellationToken.throwIfCancellationRequested(); - } - const inTypeAlias = - context.flags & NodeBuilderFlags.InTypeAlias; - context.flags &= ~NodeBuilderFlags.InTypeAlias; - - if (!type) { - context.encounteredError = true; - return undefined!; // TODO: GH#18217 - } - - if (type.flags & TypeFlags.Any) { - context.approximateLength += 3; - return createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - if (type.flags & TypeFlags.Unknown) { - return createKeywordTypeNode(SyntaxKind.UnknownKeyword); - } - if (type.flags & TypeFlags.String) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.StringKeyword); - } - if (type.flags & TypeFlags.Number) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.NumberKeyword); - } - if (type.flags & TypeFlags.BigInt) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.BigIntKeyword); - } - if (type.flags & TypeFlags.Boolean) { - context.approximateLength += 7; - return createKeywordTypeNode(SyntaxKind.BooleanKeyword); - } - if (type.flags & TypeFlags.EnumLiteral - && !(type.flags & TypeFlags.Union)) - { - const parentSymbol = getParentOfSymbol(type.symbol)!; - const parentName = symbolToTypeNode( - parentSymbol, - context, - SymbolFlags.Type - ); - const enumLiteralName = - getDeclaredTypeOfSymbol(parentSymbol) === type - ? parentName - : appendReferenceToType( - parentName as TypeReferenceNode - | ImportTypeNode, - createTypeReferenceNode( - symbolName(type.symbol), /*typeArguments*/ - undefined - ) - ); - return enumLiteralName; - } - if (type.flags & TypeFlags.EnumLike) { - return symbolToTypeNode( - type.symbol, - context, - SymbolFlags.Type - ); - } - if (type.flags & TypeFlags.StringLiteral) { - context - .approximateLength += (( type).value - .length + 2); - return createLiteralTypeNode(setEmitFlags( - createLiteral(( type).value), - EmitFlags.NoAsciiEscaping - )); - } - if (type.flags & TypeFlags.NumberLiteral) { - const value = ( type).value; - context.approximateLength += ('' + value).length; - return createLiteralTypeNode(value < 0 - ? createPrefix( - SyntaxKind.MinusToken, - createLiteral(-value) - ) - : createLiteral(value)); - } - if (type.flags & TypeFlags.BigIntLiteral) { - context - .approximateLength += (pseudoBigIntToString(( type) - .value).length) + 1; - return createLiteralTypeNode((createLiteral(( type) - .value))); - } - if (type.flags & TypeFlags.BooleanLiteral) { - context.approximateLength += ( type) - .intrinsicName.length; - return ( type).intrinsicName === 'true' - ? createTrue() - : createFalse(); - } - if (type.flags & TypeFlags.UniqueESSymbol) { - if (!(context.flags - & NodeBuilderFlags.AllowUniqueESSymbolType)) - { - if (isValueSymbolAccessible( - type.symbol, - context.enclosingDeclaration - )) { - context.approximateLength += 6; - return symbolToTypeNode( - type.symbol, - context, - SymbolFlags.Value - ); - } - if (context.tracker - .reportInaccessibleUniqueSymbolError) - { - context.tracker - .reportInaccessibleUniqueSymbolError(); - } - } - context.approximateLength += 13; - return createTypeOperatorNode( - SyntaxKind.UniqueKeyword, - createKeywordTypeNode(SyntaxKind.SymbolKeyword) - ); - } - if (type.flags & TypeFlags.Void) { - context.approximateLength += 4; - return createKeywordTypeNode(SyntaxKind.VoidKeyword); - } - if (type.flags & TypeFlags.Undefined) { - context.approximateLength += 9; - return createKeywordTypeNode(SyntaxKind.UndefinedKeyword); - } - if (type.flags & TypeFlags.Null) { - context.approximateLength += 4; - return createKeywordTypeNode(SyntaxKind.NullKeyword); - } - if (type.flags & TypeFlags.Never) { - context.approximateLength += 5; - return createKeywordTypeNode(SyntaxKind.NeverKeyword); - } - if (type.flags & TypeFlags.ESSymbol) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.SymbolKeyword); - } - if (type.flags & TypeFlags.NonPrimitive) { - context.approximateLength += 6; - return createKeywordTypeNode(SyntaxKind.ObjectKeyword); - } - if (isThisTypeParameter(type)) { - if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) { - if (!context.encounteredError - && !(context.flags - & NodeBuilderFlags.AllowThisInObjectLiteral)) - { - context.encounteredError = true; - } - if (context.tracker.reportInaccessibleThisError) { - context.tracker.reportInaccessibleThisError(); - } - } - context.approximateLength += 4; - return createThis(); - } - - if (!inTypeAlias && type.aliasSymbol - && (context.flags - & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope - || isTypeSymbolAccessible( - type.aliasSymbol, - context.enclosingDeclaration - ))) - { - const typeArgumentNodes = mapToTypeNodes( - type.aliasTypeArguments, - context - ); - if (isReservedMemberName(type.aliasSymbol.escapedName) - && !(type.aliasSymbol.flags & SymbolFlags.Class)) - { - return createTypeReferenceNode( - createIdentifier(''), - typeArgumentNodes - ); - } - return symbolToTypeNode( - type.aliasSymbol, - context, - SymbolFlags.Type, - typeArgumentNodes - ); - } - - const objectFlags = getObjectFlags(type); - - if (objectFlags & ObjectFlags.Reference) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - return ( type).node - ? visitAndTransformType(type, typeReferenceToTypeNode) - : typeReferenceToTypeNode( type); - } - if (type.flags & TypeFlags.TypeParameter - || objectFlags & ObjectFlags.ClassOrInterface) - { - if (type.flags & TypeFlags.TypeParameter - && contains(context.inferTypeParameters, type)) - { - context - .approximateLength += (symbolName(type.symbol) - .length + 6); - return createInferTypeNode(typeParameterToDeclarationWithConstraint( - type as TypeParameter, - context, /*constraintNode*/ - undefined - )); - } - if (context.flags - & NodeBuilderFlags.GenerateNamesForShadowedTypeParams - && type.flags & TypeFlags.TypeParameter - && !isTypeSymbolAccessible( - type.symbol, - context.enclosingDeclaration - )) - { - const name = typeParameterToName(type, context); - context.approximateLength += idText(name).length; - return createTypeReferenceNode( - createIdentifier(idText(name)), /*typeArguments*/ - undefined - ); - } - // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. - return type.symbol - ? symbolToTypeNode( - type.symbol, - context, - SymbolFlags.Type - ) - : createTypeReferenceNode( - createIdentifier('?'), /*typeArguments*/ - undefined - ); - } - if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { - const types = type.flags & TypeFlags.Union - ? formatUnionTypes(( type).types) - : ( type).types; - if (length(types) === 1) { - return typeToTypeNodeHelper(types[0], context); - } - const typeNodes = mapToTypeNodes( - types, - context, /*isBareList*/ - true - ); - if (typeNodes && typeNodes.length > 0) { - const unionOrIntersectionTypeNode = - createUnionOrIntersectionTypeNode( - type.flags & TypeFlags.Union - ? SyntaxKind.UnionType - : SyntaxKind.IntersectionType, - typeNodes - ); - return unionOrIntersectionTypeNode; - } else { - if (!context.encounteredError - && !(context.flags - & NodeBuilderFlags - .AllowEmptyUnionOrIntersection)) - { - context.encounteredError = true; - } - return undefined!; // TODO: GH#18217 - } - } - if (objectFlags - & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) - { - Debug.assert(!!(type.flags & TypeFlags.Object)); - // The type is an object literal type. - return createAnonymousTypeNode( type); - } - if (type.flags & TypeFlags.Index) { - const indexedType = ( type).type; - context.approximateLength += 6; - const indexTypeNode = typeToTypeNodeHelper( - indexedType, - context - ); - return createTypeOperatorNode(indexTypeNode); - } - if (type.flags & TypeFlags.IndexedAccess) { - const objectTypeNode = typeToTypeNodeHelper( - ( type).objectType, - context - ); - const indexTypeNode = typeToTypeNodeHelper( - ( type).indexType, - context - ); - context.approximateLength += 2; - return createIndexedAccessTypeNode( - objectTypeNode, - indexTypeNode - ); - } - if (type.flags & TypeFlags.Conditional) { - const checkTypeNode = typeToTypeNodeHelper( - ( type).checkType, - context - ); - const saveInferTypeParameters = context - .inferTypeParameters; - context.inferTypeParameters = ( type).root - .inferTypeParameters; - const extendsTypeNode = typeToTypeNodeHelper( - ( type).extendsType, - context - ); - context.inferTypeParameters = saveInferTypeParameters; - const trueTypeNode = typeToTypeNodeHelper( - getTrueTypeFromConditionalType( type), - context - ); - const falseTypeNode = typeToTypeNodeHelper( - getFalseTypeFromConditionalType( type), - context - ); - context.approximateLength += 15; - return createConditionalTypeNode( - checkTypeNode, - extendsTypeNode, - trueTypeNode, - falseTypeNode - ); - } - if (type.flags & TypeFlags.Substitution) { - return typeToTypeNodeHelper( - ( type).typeVariable, - context - ); - } - - return Debug.fail('Should be unreachable.'); - - function createMappedTypeNodeFromType(type: MappedType) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - const readonlyToken = type.declaration.readonlyToken - ? createToken(type.declaration - .readonlyToken.kind) - : undefined; - const questionToken = type.declaration.questionToken - ? createToken(type.declaration - .questionToken.kind) - : undefined; - let appropriateConstraintTypeNode: TypeNode; - if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - // We have a { [P in keyof T]: X } - // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType` - appropriateConstraintTypeNode = createTypeOperatorNode(typeToTypeNodeHelper( - getModifiersTypeFromMappedType(type), - context - )); - } else { - appropriateConstraintTypeNode = typeToTypeNodeHelper( - getConstraintTypeFromMappedType(type), - context - ); - } - const typeParameterNode = - typeParameterToDeclarationWithConstraint( - getTypeParameterFromMappedType(type), - context, - appropriateConstraintTypeNode - ); - const templateTypeNode = typeToTypeNodeHelper( - getTemplateTypeFromMappedType(type), - context - ); - const mappedTypeNode = createMappedTypeNode( - readonlyToken, - typeParameterNode, - questionToken, - templateTypeNode - ); - context.approximateLength += 10; - return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); - } - - function createAnonymousTypeNode(type: ObjectType): TypeNode { - const typeId = '' + type.id; - const symbol = type.symbol; - if (symbol) { - if (isJSConstructor(symbol.valueDeclaration)) { - // Instance and static types share the same symbol; only add 'typeof' for the static side. - const isInstanceType = - type - === getDeclaredTypeOfClassOrInterface(symbol) - ? SymbolFlags.Type - : SymbolFlags.Value; - return symbolToTypeNode( - symbol, - context, - isInstanceType - ); - } // Always use 'typeof T' for type of class, enum, and module objects - else if (symbol.flags & SymbolFlags.Class - && !getBaseTypeVariableOfClass(symbol) - && !(symbol.valueDeclaration.kind - === SyntaxKind.ClassExpression - && context.flags - & NodeBuilderFlags - .WriteClassExpressionAsTypeLiteral) - || symbol.flags - & (SymbolFlags.Enum | SymbolFlags.ValueModule) - || shouldWriteTypeOfFunctionSymbol()) - { - return symbolToTypeNode( - symbol, - context, - SymbolFlags.Value - ); - } else if (context.visitedTypes - && context.visitedTypes.has(typeId)) - { - // If type is an anonymous type literal in a type alias declaration, use type alias name - const typeAlias = getTypeAliasForTypeLiteral(type); - if (typeAlias) { - // The specified symbol flags need to be reinterpreted as type flags - return symbolToTypeNode( - typeAlias, - context, - SymbolFlags.Type - ); - } else { - return createElidedInformationPlaceholder(context); - } - } else { - return visitAndTransformType( - type, - createTypeNodeFromObjectType - ); - } - } else { - // Anonymous types without a symbol are never circular. - return createTypeNodeFromObjectType(type); - } - function shouldWriteTypeOfFunctionSymbol() { - const isStaticMethodSymbol = - !!(symbol.flags - & SymbolFlags.Method) // typeof static method - && some( - symbol.declarations, - declaration => hasModifier( - declaration, - ModifierFlags.Static - ) - ); - const isNonLocalFunctionSymbol = - !!(symbol.flags & SymbolFlags.Function) - && (symbol - .parent // is exported function symbol - || forEach( - symbol.declarations, - declaration => declaration.parent.kind - === SyntaxKind.SourceFile - || declaration.parent.kind - === SyntaxKind.ModuleBlock - )); - if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { - // typeof is allowed only for static/non local functions - return (!!(context.flags - & NodeBuilderFlags.UseTypeOfFunction) - || (context.visitedTypes - && context.visitedTypes - .has(typeId))) // it is type of the symbol uses itself recursively - && (!(context.flags - & NodeBuilderFlags.UseStructuralFallback) - || isValueSymbolAccessible( - symbol, - context.enclosingDeclaration - )); // And the build is going to succeed without visibility error or there is no structural fallback allowed - } - } - } - - function visitAndTransformType( - type: Type, - transform: (type: Type) => T - ) { - const typeId = '' + type.id; - const isConstructorObject = - getObjectFlags(type) & ObjectFlags.Anonymous - && type.symbol - && type.symbol.flags & SymbolFlags.Class; - const id = - getObjectFlags(type) & ObjectFlags.Reference - && ( type).node - ? 'N' + getNodeId(( type).node!) - : type.symbol - ? (isConstructorObject ? '+' : '') - + getSymbolId(type.symbol) - : undefined; - // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead - // of types allows us to catch circular references to instantiations of the same anonymous type - if (!context.visitedTypes) { - context.visitedTypes = createMap(); - } - if (id && !context.symbolDepth) { - context.symbolDepth = createMap(); - } - - let depth: number | undefined; - if (id) { - depth = context.symbolDepth!.get(id) || 0; - if (depth > 10) { - return createElidedInformationPlaceholder(context); - } - context.symbolDepth!.set(id, depth + 1); - } - context.visitedTypes.set(typeId, true); - const result = transform(type); - context.visitedTypes.delete(typeId); - if (id) { - context.symbolDepth!.set(id, depth!); - } - return result; - } - - function createTypeNodeFromObjectType(type: - ObjectType): TypeNode - { - if (isGenericMappedType(type)) { - return createMappedTypeNodeFromType(type); - } - - const resolved = resolveStructuredTypeMembers(type); - if (!resolved.properties.length - && !resolved.stringIndexInfo - && !resolved.numberIndexInfo) - { - if (!resolved.callSignatures.length - && !resolved.constructSignatures.length) - { - context.approximateLength += 2; - return setEmitFlags( - createTypeLiteralNode(/*members*/ undefined), - EmitFlags.SingleLine - ); - } - - if (resolved.callSignatures.length === 1 - && !resolved.constructSignatures.length) - { - const signature = resolved.callSignatures[0]; - const signatureNode = - signatureToSignatureDeclarationHelper( - signature, - SyntaxKind.FunctionType, - context - ); - return signatureNode; - } - - if (resolved.constructSignatures.length === 1 - && !resolved.callSignatures.length) - { - const signature = resolved.constructSignatures[0]; - const signatureNode = - signatureToSignatureDeclarationHelper( - signature, - SyntaxKind.ConstructorType, - context - ); - return signatureNode; - } - } - - const savedFlags = context.flags; - context.flags |= NodeBuilderFlags.InObjectTypeLiteral; - const members = createTypeNodesFromResolvedType(resolved); - context.flags = savedFlags; - const typeLiteralNode = createTypeLiteralNode(members); - context.approximateLength += 2; - return setEmitFlags( - typeLiteralNode, - (context.flags - & NodeBuilderFlags.MultilineObjectLiterals) - ? 0 - : EmitFlags.SingleLine - ); - } - - function typeReferenceToTypeNode(type: TypeReference) { - const typeArguments: readonly Type[] = - getTypeArguments(type); - if (type.target === globalArrayType - || type.target === globalReadonlyArrayType) - { - if (context.flags - & NodeBuilderFlags.WriteArrayAsGenericType) - { - const typeArgumentNode = typeToTypeNodeHelper( - typeArguments[0], - context - ); - return createTypeReferenceNode( - type.target === globalArrayType - ? 'Array' - : 'ReadonlyArray', - [typeArgumentNode] - ); - } - const elementType = typeToTypeNodeHelper( - typeArguments[0], - context - ); - const arrayType = createArrayTypeNode(elementType); - return type.target === globalArrayType - ? arrayType - : createTypeOperatorNode( - SyntaxKind.ReadonlyKeyword, - arrayType - ); - } else if (type.target.objectFlags & ObjectFlags.Tuple) { - if (typeArguments.length > 0) { - const arity = getTypeReferenceArity(type); - const tupleConstituentNodes = mapToTypeNodes( - typeArguments.slice(0, arity), - context - ); - const hasRestElement = ( type.target) - .hasRestElement; - if (tupleConstituentNodes) { - for (let i = ( type.target) - .minLength; - i - < Math.min( - arity, - tupleConstituentNodes.length - ); i++) - { - tupleConstituentNodes - [i] = hasRestElement && i === arity - 1 - ? createRestTypeNode(createArrayTypeNode(tupleConstituentNodes - [i])) - : createOptionalTypeNode(tupleConstituentNodes - [i]); - } - const tupleTypeNode = - createTupleTypeNode(tupleConstituentNodes); - return ( type.target).readonly - ? createTypeOperatorNode( - SyntaxKind.ReadonlyKeyword, - tupleTypeNode - ) - : tupleTypeNode; - } - } - if (context.encounteredError - || (context.flags - & NodeBuilderFlags.AllowEmptyTuple)) - { - const tupleTypeNode = createTupleTypeNode([]); - return ( type.target).readonly - ? createTypeOperatorNode( - SyntaxKind.ReadonlyKeyword, - tupleTypeNode - ) - : tupleTypeNode; - } - context.encounteredError = true; - return undefined!; // TODO: GH#18217 - } else if (context.flags - & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral - && type.symbol.valueDeclaration - && isClassLike(type.symbol.valueDeclaration) - && !isValueSymbolAccessible( - type.symbol, - context.enclosingDeclaration - )) - { - return createAnonymousTypeNode(type); - } else { - const outerTypeParameters = type.target - .outerTypeParameters; - let i = 0; - let resultType: TypeReferenceNode | ImportTypeNode - | undefined; - if (outerTypeParameters) { - const length = outerTypeParameters.length; - while (i < length) { - // Find group of type arguments for type parameters with the same declaring container. - const start = i; - const parent = - getParentSymbolOfTypeParameter(outerTypeParameters - [i])!; - do { - i++; - } while (i < length - && getParentSymbolOfTypeParameter(outerTypeParameters - [i]) === parent); - // When type parameters are their own type arguments for the whole group (i.e. we have - // the default outer type arguments), we don't show the group. - if (!rangeEquals( - outerTypeParameters, - typeArguments, - start, - i - )) { - const typeArgumentSlice = mapToTypeNodes( - typeArguments.slice(start, i), - context - ); - const flags = context.flags; - context.flags |= NodeBuilderFlags - .ForbidIndexedAccessSymbolReferences; - const ref = symbolToTypeNode( - parent, - context, - SymbolFlags.Type, - typeArgumentSlice - ) as TypeReferenceNode | ImportTypeNode; - context.flags = flags; - resultType = !resultType - ? ref - : appendReferenceToType( - resultType, - ref as TypeReferenceNode - ); - } - } - } - let typeArgumentNodes: readonly TypeNode[] | undefined; - if (typeArguments.length > 0) { - const typeParameterCount = - (type.target.typeParameters || emptyArray) - .length; - typeArgumentNodes = mapToTypeNodes( - typeArguments.slice(i, typeParameterCount), - context - ); - } - const flags = context.flags; - context.flags |= NodeBuilderFlags - .ForbidIndexedAccessSymbolReferences; - const finalRef = symbolToTypeNode( - type.symbol, - context, - SymbolFlags.Type, - typeArgumentNodes - ); - context.flags = flags; - return !resultType - ? finalRef - : appendReferenceToType( - resultType, - finalRef as TypeReferenceNode - ); - } - } - - function appendReferenceToType( - root: TypeReferenceNode | ImportTypeNode, - ref: TypeReferenceNode - ): TypeReferenceNode | ImportTypeNode { - if (isImportTypeNode(root)) { - // first shift type arguments - const innerParams = root.typeArguments; - if (root.qualifier) { - (isIdentifier(root.qualifier) - ? root.qualifier - : root.qualifier.right) - .typeArguments = innerParams; - } - root.typeArguments = ref.typeArguments; - // then move qualifiers - const ids = getAccessStack(ref); - for (const id of ids) { - root.qualifier = root.qualifier - ? createQualifiedName(root.qualifier, id) - : id; - } - return root; - } else { - // first shift type arguments - const innerParams = root.typeArguments; - (isIdentifier(root.typeName) - ? root.typeName - : root.typeName.right).typeArguments = innerParams; - root.typeArguments = ref.typeArguments; - // then move qualifiers - const ids = getAccessStack(ref); - for (const id of ids) { - root.typeName = createQualifiedName( - root.typeName, - id - ); - } - return root; - } - } - - function getAccessStack(ref: TypeReferenceNode): Identifier[] { - let state = ref.typeName; - const ids = []; - while (!isIdentifier(state)) { - ids.unshift(state.right); - state = state.left; - } - ids.unshift(state); - return ids; - } - - function createTypeNodesFromResolvedType(resolvedType: - ResolvedType): TypeElement[] | undefined - { - if (checkTruncationLength(context)) { - return [createPropertySignature( - /*modifiers*/ undefined, - '...', /*questionToken*/ - undefined, /*type*/ - undefined, /*initializer*/ - undefined - )]; - } - const typeElements: TypeElement[] = []; - for (const signature of resolvedType.callSignatures) { - typeElements - .push( signatureToSignatureDeclarationHelper( - signature, - SyntaxKind.CallSignature, - context - )); - } - for (const signature of resolvedType.constructSignatures) { - typeElements - .push( signatureToSignatureDeclarationHelper( - signature, - SyntaxKind.ConstructSignature, - context - )); - } - if (resolvedType.stringIndexInfo) { - let indexSignature: IndexSignatureDeclaration; - if (resolvedType.objectFlags - & ObjectFlags.ReverseMapped) - { - indexSignature = indexInfoToIndexSignatureDeclarationHelper( - createIndexInfo( - anyType, - resolvedType.stringIndexInfo.isReadonly, - resolvedType.stringIndexInfo.declaration - ), - IndexKind.String, - context - ); - indexSignature - .type = createElidedInformationPlaceholder(context); - } else { - indexSignature = indexInfoToIndexSignatureDeclarationHelper( - resolvedType.stringIndexInfo, - IndexKind.String, - context - ); - } - typeElements.push(indexSignature); - } - if (resolvedType.numberIndexInfo) { - typeElements - .push(indexInfoToIndexSignatureDeclarationHelper( - resolvedType.numberIndexInfo, - IndexKind.Number, - context - )); - } - - const properties = resolvedType.properties; - if (!properties) { - return typeElements; - } - - let i = 0; - for (const propertySymbol of properties) { - i++; - if (context.flags - & NodeBuilderFlags - .WriteClassExpressionAsTypeLiteral) - { - if (propertySymbol.flags & SymbolFlags.Prototype) { - continue; - } - if (getDeclarationModifierFlagsFromSymbol(propertySymbol) - & (ModifierFlags.Private - | ModifierFlags.Protected) - && context.tracker - .reportPrivateInBaseOfClassExpression) - { - context.tracker - .reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(propertySymbol - .escapedName)); - } - } - if (checkTruncationLength(context) - && (i + 2 < properties.length - 1)) - { - typeElements - .push(createPropertySignature( - /*modifiers*/ undefined, - `... ${properties.length - i - } more ...`, /*questionToken*/ - undefined, /*type*/ - undefined, /*initializer*/ - undefined - )); - addPropertyToElementList( - properties[properties.length - 1], - context, - typeElements - ); - break; - } - addPropertyToElementList( - propertySymbol, - context, - typeElements - ); - } - return typeElements.length ? typeElements : undefined; - } - } - - function createElidedInformationPlaceholder(context: - NodeBuilderContext) - { - context.approximateLength += 3; - if (!(context.flags & NodeBuilderFlags.NoTruncation)) { - return createTypeReferenceNode( - createIdentifier('...'), /*typeArguments*/ - undefined - ); - } - return createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - - function addPropertyToElementList( - propertySymbol: Symbol, - context: NodeBuilderContext, - typeElements: TypeElement[] - ) { - const propertyIsReverseMapped = - !!(getCheckFlags(propertySymbol) - & CheckFlags.ReverseMapped); - const propertyType = - propertyIsReverseMapped - && context.flags & NodeBuilderFlags.InReverseMappedType - ? anyType - : getTypeOfSymbol(propertySymbol); - const saveEnclosingDeclaration = context.enclosingDeclaration; - context.enclosingDeclaration = undefined; - if (context.tracker.trackSymbol - && getCheckFlags(propertySymbol) & CheckFlags.Late) - { - const decl = first(propertySymbol.declarations); - if (hasLateBindableName(decl)) { - if (isBinaryExpression(decl)) { - const name = getNameOfDeclaration(decl); - if (name && isElementAccessExpression(name) - && isPropertyAccessEntityNameExpression(name - .argumentExpression)) - { - trackComputedName( - name.argumentExpression, - saveEnclosingDeclaration, - context - ); - } - } else { - trackComputedName( - decl.name.expression, - saveEnclosingDeclaration, - context - ); - } - } - } - context.enclosingDeclaration = saveEnclosingDeclaration; - const propertyName = getPropertyNameNodeForSymbol( - propertySymbol, - context - ); - context - .approximateLength += (symbolName(propertySymbol).length - + 1); - const optionalToken = - propertySymbol.flags & SymbolFlags.Optional - ? createToken(SyntaxKind.QuestionToken) - : undefined; - if (propertySymbol.flags - & (SymbolFlags.Function | SymbolFlags.Method) - && !getPropertiesOfObjectType(propertyType).length - && !isReadonlySymbol(propertySymbol)) - { - const signatures = getSignaturesOfType( - filterType( - propertyType, - t => !(t.flags & TypeFlags.Undefined) - ), - SignatureKind.Call - ); - for (const signature of signatures) { - const methodDeclaration = - signatureToSignatureDeclarationHelper( - signature, - SyntaxKind.MethodSignature, - context - ); - methodDeclaration.name = propertyName; - methodDeclaration.questionToken = optionalToken; - typeElements - .push(preserveCommentsOn(methodDeclaration)); - } - } else { - const savedFlags = context.flags; - context.flags |= propertyIsReverseMapped - ? NodeBuilderFlags.InReverseMappedType - : 0; - let propertyTypeNode: TypeNode; - if (propertyIsReverseMapped - && !!(savedFlags - & NodeBuilderFlags.InReverseMappedType)) - { - propertyTypeNode = createElidedInformationPlaceholder(context); - } else { - propertyTypeNode = propertyType - ? typeToTypeNodeHelper(propertyType, context) - : createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - context.flags = savedFlags; - - const modifiers = isReadonlySymbol(propertySymbol) - ? [createToken(SyntaxKind.ReadonlyKeyword)] - : undefined; - if (modifiers) { - context.approximateLength += 9; - } - const propertySignature = createPropertySignature( - modifiers, - propertyName, - optionalToken, - propertyTypeNode, - /*initializer*/ undefined - ); - - typeElements.push(preserveCommentsOn(propertySignature)); - } - - function preserveCommentsOn(node: T) { - if (some( - propertySymbol.declarations, - d => d.kind === SyntaxKind.JSDocPropertyTag - )) { - const d = find( - propertySymbol.declarations, - d => d.kind === SyntaxKind.JSDocPropertyTag - )! as JSDocPropertyTag; - const commentText = d.comment; - if (commentText) { - setSyntheticLeadingComments( - node, - [{ kind: SyntaxKind.MultiLineCommentTrivia, - text: '*\n * ' - + commentText.replace(/\n/g, '\n * ') - + '\n ', pos: -1, end: -1, - hasTrailingNewLine: true }] - ); - } - } else if (propertySymbol.valueDeclaration) { - // Copy comments to node for declaration emit - setCommentRange(node, propertySymbol.valueDeclaration); - } - return node; - } - } - - function mapToTypeNodes( - types: readonly Type[] | undefined, - context: NodeBuilderContext, - isBareList?: boolean - ): TypeNode[] | undefined { - if (some(types)) { - if (checkTruncationLength(context)) { - if (!isBareList) { - return [createTypeReferenceNode( - '...', /*typeArguments*/ - undefined - )]; - } else if (types.length > 2) { - return [ - typeToTypeNodeHelper(types[0], context), - createTypeReferenceNode( - `... ${types.length - 2 - } more ...`, /*typeArguments*/ - undefined - ), - typeToTypeNodeHelper( - types[types.length - 1], - context - ) - ]; - } - } - const result = []; - let i = 0; - for (const type of types) { - i++; - if (checkTruncationLength(context) - && (i + 2 < types.length - 1)) - { - result - .push(createTypeReferenceNode( - `... ${types.length - i - } more ...`, /*typeArguments*/ - undefined - )); - const typeNode = typeToTypeNodeHelper( - types[types.length - 1], - context - ); - if (typeNode) { - result.push(typeNode); - } - break; - } - context - .approximateLength += 2; // Account for whitespace + separator - const typeNode = typeToTypeNodeHelper(type, context); - if (typeNode) { - result.push(typeNode); - } - } - - return result; - } - } - - function indexInfoToIndexSignatureDeclarationHelper( - indexInfo: IndexInfo, - kind: IndexKind, - context: NodeBuilderContext - ): IndexSignatureDeclaration { - const name = getNameFromIndexInfo(indexInfo) || 'x'; - const indexerTypeNode = - createKeywordTypeNode(kind === IndexKind.String - ? SyntaxKind.StringKeyword - : SyntaxKind.NumberKeyword); - - const indexingParameter = createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - name, - /*questionToken*/ undefined, - indexerTypeNode, - /*initializer*/ undefined - ); - const typeNode = typeToTypeNodeHelper( - indexInfo.type || anyType, - context - ); - if (!indexInfo.type - && !(context.flags - & NodeBuilderFlags.AllowEmptyIndexInfoType)) - { - context.encounteredError = true; - } - context.approximateLength += (name.length + 4); - return createIndexSignature( - /*decorators*/ undefined, - indexInfo.isReadonly - ? [createToken(SyntaxKind.ReadonlyKeyword)] - : undefined, - [indexingParameter], - typeNode - ); - } - - function signatureToSignatureDeclarationHelper( - signature: Signature, - kind: SyntaxKind, - context: NodeBuilderContext - ): SignatureDeclaration { - let typeParameters: TypeParameterDeclaration[] | undefined; - let typeArguments: TypeNode[] | undefined; - if (context.flags - & NodeBuilderFlags.WriteTypeArgumentsOfSignature - && signature.target && signature.mapper - && signature.target.typeParameters) - { - typeArguments = signature.target.typeParameters - .map(parameter => typeToTypeNodeHelper( - instantiateType(parameter, signature.mapper), - context - )); - } else { - typeParameters = signature.typeParameters - && signature.typeParameters - .map(parameter => typeParameterToDeclaration( - parameter, - context - )); - } - - const parameters = getExpandedParameters(signature) - .map(parameter => symbolToParameterDeclaration( - parameter, - context, - kind === SyntaxKind.Constructor - )); - if (signature.thisParameter) { - const thisParameter = symbolToParameterDeclaration( - signature.thisParameter, - context - ); - parameters.unshift(thisParameter); - } - - let returnTypeNode: TypeNode | undefined; - const typePredicate = getTypePredicateOfSignature(signature); - if (typePredicate) { - const assertsModifier = - typePredicate.kind === TypePredicateKind.AssertsThis - || typePredicate.kind - === TypePredicateKind.AssertsIdentifier - ? createToken(SyntaxKind.AssertsKeyword) - : undefined; - const parameterName = - typePredicate.kind === TypePredicateKind.Identifier - || typePredicate.kind - === TypePredicateKind.AssertsIdentifier - ? setEmitFlags( - createIdentifier(typePredicate.parameterName), - EmitFlags.NoAsciiEscaping - ) - : createThisTypeNode(); - const typeNode = typePredicate.type - && typeToTypeNodeHelper(typePredicate.type, context); - returnTypeNode = createTypePredicateNodeWithModifier( - assertsModifier, - parameterName, - typeNode - ); - } else { - const returnType = getReturnTypeOfSignature(signature); - returnTypeNode = returnType - && typeToTypeNodeHelper(returnType, context); - } - if (context.flags & NodeBuilderFlags.SuppressAnyReturnType) { - if (returnTypeNode - && returnTypeNode.kind === SyntaxKind.AnyKeyword) - { - returnTypeNode = undefined; - } - } else if (!returnTypeNode) { - returnTypeNode = createKeywordTypeNode(SyntaxKind - .AnyKeyword); - } - context - .approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum - return createSignatureDeclaration( - kind, - typeParameters, - parameters, - returnTypeNode, - typeArguments - ); - } - - function typeParameterToDeclarationWithConstraint( - type: TypeParameter, - context: NodeBuilderContext, - constraintNode: TypeNode | undefined - ): TypeParameterDeclaration { - const savedContextFlags = context.flags; - context.flags &= ~NodeBuilderFlags - .WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic - const name = typeParameterToName(type, context); - const defaultParameter = getDefaultFromTypeParameter(type); - const defaultParameterNode = defaultParameter - && typeToTypeNodeHelper(defaultParameter, context); - context.flags = savedContextFlags; - return createTypeParameterDeclaration( - name, - constraintNode, - defaultParameterNode - ); - } - - function typeParameterToDeclaration( - type: TypeParameter, - context: NodeBuilderContext, - constraint = getConstraintOfTypeParameter(type) - ): TypeParameterDeclaration { - const constraintNode = constraint - && typeToTypeNodeHelper(constraint, context); - return typeParameterToDeclarationWithConstraint( - type, - context, - constraintNode - ); - } - - function symbolToParameterDeclaration( - parameterSymbol: Symbol, - context: NodeBuilderContext, - preserveModifierFlags?: boolean - ): ParameterDeclaration { - let parameterDeclaration: ParameterDeclaration - | JSDocParameterTag | undefined = - getDeclarationOfKind( - parameterSymbol, - SyntaxKind.Parameter - ); - if (!parameterDeclaration - && !isTransientSymbol(parameterSymbol)) - { - parameterDeclaration = getDeclarationOfKind( - parameterSymbol, - SyntaxKind.JSDocParameterTag - ); - } - - let parameterType = getTypeOfSymbol(parameterSymbol); - if (parameterDeclaration - && isRequiredInitializedParameter(parameterDeclaration)) - { - parameterType = getOptionalType(parameterType); - } - const parameterTypeNode = typeToTypeNodeHelper( - parameterType, - context - ); - - const modifiers = - !(context.flags & NodeBuilderFlags.OmitParameterModifiers) - && preserveModifierFlags && parameterDeclaration - && parameterDeclaration.modifiers - ? parameterDeclaration.modifiers - .map(getSynthesizedClone) - : undefined; - const isRest = parameterDeclaration - && isRestParameter(parameterDeclaration) - || getCheckFlags(parameterSymbol) - & CheckFlags.RestParameter; - const dotDotDotToken = isRest - ? createToken(SyntaxKind.DotDotDotToken) - : undefined; - const name = parameterDeclaration - ? parameterDeclaration.name - ? parameterDeclaration.name.kind - === SyntaxKind.Identifier - ? setEmitFlags( - getSynthesizedClone(parameterDeclaration.name), - EmitFlags.NoAsciiEscaping - ) - : parameterDeclaration.name.kind - === SyntaxKind.QualifiedName - ? setEmitFlags( - getSynthesizedClone(parameterDeclaration - .name.right), - EmitFlags.NoAsciiEscaping - ) - : cloneBindingName(parameterDeclaration.name) - : symbolName(parameterSymbol) - : symbolName(parameterSymbol); - const isOptional = parameterDeclaration - && isOptionalParameter(parameterDeclaration) - || getCheckFlags(parameterSymbol) - & CheckFlags.OptionalParameter; - const questionToken = isOptional - ? createToken(SyntaxKind.QuestionToken) - : undefined; - const parameterNode = createParameter( - /*decorators*/ undefined, - modifiers, - dotDotDotToken, - name, - questionToken, - parameterTypeNode, - /*initializer*/ undefined - ); - context.approximateLength += symbolName(parameterSymbol).length - + 3; - return parameterNode; - - function cloneBindingName(node: BindingName): BindingName { - return elideInitializerAndSetEmitFlags(node); - function elideInitializerAndSetEmitFlags(node: - Node): Node - { - if (context.tracker.trackSymbol - && isComputedPropertyName(node) - && isLateBindableName(node)) - { - trackComputedName( - node.expression, - context.enclosingDeclaration, - context - ); - } - const visited = visitEachChild( - node, - elideInitializerAndSetEmitFlags, - nullTransformationContext, /*nodesVisitor*/ - undefined, - elideInitializerAndSetEmitFlags - )!; - const clone = nodeIsSynthesized(visited) - ? visited - : getSynthesizedClone(visited); - if (clone.kind === SyntaxKind.BindingElement) { - ( clone).initializer = undefined; - } - return setEmitFlags( - clone, - EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping - ); - } - } - } - - function trackComputedName( - accessExpression: EntityNameOrEntityNameExpression, - enclosingDeclaration: Node | undefined, - context: NodeBuilderContext - ) { - if (!context.tracker.trackSymbol) return; - // get symbol of the first identifier of the entityName - const firstIdentifier = getFirstIdentifier(accessExpression); - const name = resolveName( - firstIdentifier, - firstIdentifier.escapedText, - SymbolFlags.Value - | SymbolFlags.ExportValue, /*nodeNotFoundErrorMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - true - ); - if (name) { - context.tracker.trackSymbol( - name, - enclosingDeclaration, - SymbolFlags.Value - ); - } - } - - function lookupSymbolChain( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags, - yieldModuleSymbol?: boolean - ) { - context.tracker.trackSymbol!( - symbol, - context.enclosingDeclaration, - meaning - ); // TODO: GH#18217 - return lookupSymbolChainWorker( - symbol, - context, - meaning, - yieldModuleSymbol - ); - } - - function lookupSymbolChainWorker( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags, - yieldModuleSymbol?: boolean - ) { - // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. - let chain: Symbol[]; - const isTypeParameter = - symbol.flags & SymbolFlags.TypeParameter; - if (!isTypeParameter - && (context.enclosingDeclaration - || context.flags - & NodeBuilderFlags.UseFullyQualifiedType) - && !(context.flags - & NodeBuilderFlags.DoNotIncludeSymbolChain)) - { - chain = Debug - .assertDefined(getSymbolChain( - symbol, - meaning, /*endOfChain*/ - true - )); - Debug.assert(chain && chain.length > 0); - } else { - chain = [symbol]; - } - return chain; - - /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ - function getSymbolChain( - symbol: Symbol, - meaning: SymbolFlags, - endOfChain: boolean - ): Symbol[] | undefined { - let accessibleSymbolChain = getAccessibleSymbolChain( - symbol, - context.enclosingDeclaration, - meaning, - !!(context.flags - & NodeBuilderFlags.UseOnlyExternalAliasing) - ); - let parentSpecifiers: (string | undefined)[]; - if (!accessibleSymbolChain - || needsQualification( - accessibleSymbolChain[0], - context.enclosingDeclaration, - accessibleSymbolChain.length === 1 - ? meaning - : getQualifiedLeftMeaning(meaning) - )) - { - // Go up and add our parent. - const parents = getContainersOfSymbol( - accessibleSymbolChain - ? accessibleSymbolChain[0] - : symbol, - context.enclosingDeclaration - ); - if (length(parents)) { - parentSpecifiers = parents!.map(symbol => some( - symbol.declarations, - hasNonGlobalAugmentationExternalModuleSymbol - ) - ? getSpecifierForModuleSymbol( - symbol, - context - ) - : undefined); - const indices = parents!.map((_, i) => i); - indices.sort(sortByBestName); - const sortedParents = indices - .map(i => parents![i]); - for (const parent of sortedParents) { - const parentChain = getSymbolChain( - parent, - getQualifiedLeftMeaning(meaning), /*endOfChain*/ - false - ); - if (parentChain) { - if (parent.exports - && parent.exports - .get(InternalSymbolName - .ExportEquals) - && getSymbolIfSameReference( - parent.exports - .get(InternalSymbolName - .ExportEquals)!, - symbol - )) - { - // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent - // No need to lookup an alias for the symbol in itself - accessibleSymbolChain = parentChain; - break; - } - accessibleSymbolChain = parentChain - .concat(accessibleSymbolChain - || [getAliasForSymbolInContainer( - parent, - symbol - ) || symbol]); - break; - } - } - } - } - - if (accessibleSymbolChain) { - return accessibleSymbolChain; - } - if ( - // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. - endOfChain - // If a parent symbol is an anonymous type, don't write it. - || !(symbol.flags - & (SymbolFlags.TypeLiteral - | SymbolFlags.ObjectLiteral)) - ) { - // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) - if (!endOfChain && !yieldModuleSymbol - && !!forEach( - symbol.declarations, - hasNonGlobalAugmentationExternalModuleSymbol - )) - { - return; - } - return [symbol]; - } - - function sortByBestName(a: number, b: number) { - const specifierA = parentSpecifiers[a]; - const specifierB = parentSpecifiers[b]; - if (specifierA && specifierB) { - const isBRelative = pathIsRelative(specifierB); - if (pathIsRelative(specifierA) === isBRelative) { - // Both relative or both non-relative, sort by number of parts - return moduleSpecifiers - .countPathComponents(specifierA) - - moduleSpecifiers - .countPathComponents(specifierB); - } - if (isBRelative) { - // A is non-relative, B is relative: prefer A - return -1; - } - // A is relative, B is non-relative: prefer B - return 1; - } - return 0; - } - } - } - - function typeParametersToTypeParameterDeclarations( - symbol: Symbol, - context: NodeBuilderContext - ) { - let typeParameterNodes: NodeArray - | undefined; - const targetSymbol = getTargetSymbol(symbol); - if (targetSymbol.flags - & (SymbolFlags.Class | SymbolFlags.Interface - | SymbolFlags.TypeAlias)) - { - typeParameterNodes = createNodeArray(map( - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), - tp => typeParameterToDeclaration(tp, context) - )); - } - return typeParameterNodes; - } - - function lookupTypeParameterNodes( - chain: Symbol[], - index: number, - context: NodeBuilderContext - ) { - Debug.assert(chain && 0 <= index && index < chain.length); - const symbol = chain[index]; - const symbolId = '' + getSymbolId(symbol); - if (context.typeParameterSymbolList - && context.typeParameterSymbolList.get(symbolId)) - { - return undefined; - } - (context.typeParameterSymbolList - || (context.typeParameterSymbolList = createMap())).set( - symbolId, - true - ); - let typeParameterNodes: readonly TypeNode[] - | readonly TypeParameterDeclaration[] | undefined; - if (context.flags - & NodeBuilderFlags.WriteTypeParametersInQualifiedName - && index < (chain.length - 1)) - { - const parentSymbol = symbol; - const nextSymbol = chain[index + 1]; - if (getCheckFlags(nextSymbol) & CheckFlags.Instantiated) { - const params = getTypeParametersOfClassOrInterface( - parentSymbol.flags & SymbolFlags.Alias - ? resolveAlias(parentSymbol) - : parentSymbol - ); - typeParameterNodes = mapToTypeNodes( - map( - params, - (nextSymbol as TransientSymbol).mapper! - ), - context - ); - } else { - typeParameterNodes = typeParametersToTypeParameterDeclarations( - symbol, - context - ); - } - } - return typeParameterNodes; - } - - /** - * Given A[B][C][D], finds A[B] - */ - function getTopmostIndexedAccessType(top: - IndexedAccessTypeNode): IndexedAccessTypeNode - { - if (isIndexedAccessTypeNode(top.objectType)) { - return getTopmostIndexedAccessType(top.objectType); - } - return top; - } - - function getSpecifierForModuleSymbol( - symbol: Symbol, - context: NodeBuilderContext - ) { - const file = getDeclarationOfKind( - symbol, - SyntaxKind.SourceFile - ); - if (file && file.moduleName !== undefined) { - // Use the amd name if it is available - return file.moduleName; - } - if (!file) { - if (context.tracker.trackReferencedAmbientModule) { - const ambientDecls = filter( - symbol.declarations, - isAmbientModule - ); - if (length(ambientDecls)) { - for (const decl of ambientDecls) { - context.tracker.trackReferencedAmbientModule( - decl, - symbol - ); - } - } - } - if (ambientModuleSymbolRegex - .test(symbol.escapedName as string)) - { - return (symbol.escapedName as string).substring( - 1, - (symbol.escapedName as string).length - 1 - ); - } - } - if (!context.enclosingDeclaration - || !context.tracker.moduleResolverHost) - { - // If there's no context declaration, we can't lookup a non-ambient specifier, so we just use the symbol name - if (ambientModuleSymbolRegex - .test(symbol.escapedName as string)) - { - return (symbol.escapedName as string).substring( - 1, - (symbol.escapedName as string).length - 1 - ); - } - return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!) - .fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full - } - const contextFile = - getSourceFileOfNode(getOriginalNode(context - .enclosingDeclaration)); - const links = getSymbolLinks(symbol); - let specifier = links.specifierCache - && links.specifierCache.get(contextFile.path); - if (!specifier) { - const isBundle = - (compilerOptions.out || compilerOptions.outFile); - // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports, - // just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this - // using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative - // specifier preference - const { moduleResolverHost } = context.tracker; - const specifierCompilerOptions = isBundle - ? { ...compilerOptions, - baseUrl: moduleResolverHost - .getCommonSourceDirectory() } - : compilerOptions; - specifier = first(moduleSpecifiers.getModuleSpecifiers( - symbol, - specifierCompilerOptions, - contextFile, - moduleResolverHost, - host.getSourceFiles(), - { importModuleSpecifierPreference: isBundle - ? 'non-relative' - : 'relative' }, - host.redirectTargetsMap - )); - links.specifierCache = links.specifierCache || createMap(); - links.specifierCache.set(contextFile.path, specifier); - } - return specifier; - } - - function symbolToTypeNode( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags, - overrideTypeArguments?: readonly TypeNode[] - ): TypeNode { - const chain = lookupSymbolChain( - symbol, - context, - meaning, - !(context.flags - & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope) - ); // If we're using aliases outside the current scope, dont bother with the module - - const isTypeOf = meaning === SymbolFlags.Value; - if (some( - chain[0].declarations, - hasNonGlobalAugmentationExternalModuleSymbol - )) { - // module is root, must use `ImportTypeNode` - const nonRootParts = chain.length > 1 - ? createAccessFromSymbolChain( - chain, - chain.length - 1, - 1 - ) - : undefined; - const typeParameterNodes = overrideTypeArguments - || lookupTypeParameterNodes(chain, 0, context); - const specifier = getSpecifierForModuleSymbol( - chain[0], - context - ); - if (!(context.flags - & NodeBuilderFlags.AllowNodeModulesRelativePaths) - && getEmitModuleResolutionKind(compilerOptions) - === ModuleResolutionKind.NodeJs - && specifier.indexOf('/node_modules/') >= 0) - { - // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error - // since declaration files with these kinds of references are liable to fail when published :( - context.encounteredError = true; - if (context.tracker - .reportLikelyUnsafeImportRequiredError) - { - context.tracker - .reportLikelyUnsafeImportRequiredError(specifier); - } - } - const lit = - createLiteralTypeNode(createLiteral(specifier)); - if (context.tracker - .trackExternalModuleSymbolOfImportTypeNode) - { - context.tracker - .trackExternalModuleSymbolOfImportTypeNode(chain - [0]); - } - context.approximateLength += specifier.length - + 10; // specifier + import("") - if (!nonRootParts || isEntityName(nonRootParts)) { - if (nonRootParts) { - const lastId = isIdentifier(nonRootParts) - ? nonRootParts - : nonRootParts.right; - lastId.typeArguments = undefined; - } - return createImportTypeNode( - lit, - nonRootParts as EntityName, - typeParameterNodes as readonly TypeNode[], - isTypeOf - ); - } else { - const splitNode = - getTopmostIndexedAccessType(nonRootParts); - const qualifier = - (splitNode.objectType as TypeReferenceNode) - .typeName; - return createIndexedAccessTypeNode( - createImportTypeNode( - lit, - qualifier, - typeParameterNodes as readonly TypeNode[], - isTypeOf - ), - splitNode.indexType - ); - } - } - - const entityName = createAccessFromSymbolChain( - chain, - chain.length - 1, - 0 - ); - if (isIndexedAccessTypeNode(entityName)) { - return entityName; // Indexed accesses can never be `typeof` - } - if (isTypeOf) { - return createTypeQueryNode(entityName); - } else { - const lastId = isIdentifier(entityName) - ? entityName - : entityName.right; - const lastTypeArgs = lastId.typeArguments; - lastId.typeArguments = undefined; - return createTypeReferenceNode( - entityName, - lastTypeArgs as NodeArray - ); - } - - function createAccessFromSymbolChain( - chain: Symbol[], - index: number, - stopper: number - ): EntityName | IndexedAccessTypeNode { - const typeParameterNodes = index === (chain.length - 1) - ? overrideTypeArguments - : lookupTypeParameterNodes(chain, index, context); - const symbol = chain[index]; - - const parent = chain[index - 1]; - let symbolName: string | undefined; - if (index === 0) { - context.flags |= NodeBuilderFlags.InInitialEntityName; - symbolName = getNameOfSymbolAsWritten(symbol, context); - context - .approximateLength += (symbolName - ? symbolName.length - : 0) + 1; - context.flags ^= NodeBuilderFlags.InInitialEntityName; - } else { - if (parent && getExportsOfSymbol(parent)) { - const exports = getExportsOfSymbol(parent); - forEachEntry(exports, (ex, name) => { - if (getSymbolIfSameReference(ex, symbol) - && !isLateBoundName(name) - && name - !== InternalSymbolName.ExportEquals) - { - symbolName = unescapeLeadingUnderscores(name); - return true; - } - }); - } - } - if (!symbolName) { - symbolName = getNameOfSymbolAsWritten(symbol, context); - } - context.approximateLength += symbolName.length + 1; - - if (!(context.flags - & NodeBuilderFlags.ForbidIndexedAccessSymbolReferences) - && parent - && getMembersOfSymbol(parent) - && getMembersOfSymbol(parent).get(symbol.escapedName) - && getSymbolIfSameReference( - getMembersOfSymbol(parent) - .get(symbol.escapedName)!, - symbol - )) - { - // Should use an indexed access - const LHS = createAccessFromSymbolChain( - chain, - index - 1, - stopper - ); - if (isIndexedAccessTypeNode(LHS)) { - return createIndexedAccessTypeNode( - LHS, - createLiteralTypeNode(createLiteral(symbolName)) - ); - } else { - return createIndexedAccessTypeNode( - createTypeReferenceNode( - LHS, - typeParameterNodes as readonly TypeNode[] - ), - createLiteralTypeNode(createLiteral(symbolName)) - ); - } - } - - const identifier = setEmitFlags( - createIdentifier(symbolName, typeParameterNodes), - EmitFlags.NoAsciiEscaping - ); - identifier.symbol = symbol; - - if (index > stopper) { - const LHS = createAccessFromSymbolChain( - chain, - index - 1, - stopper - ); - if (!isEntityName(LHS)) { - return Debug - .fail('Impossible construct - an export of an indexed access cannot be reachable'); - } - return createQualifiedName(LHS, identifier); - } - return identifier; - } - } - - function typeParameterShadowsNameInScope( - escapedName: __String, - context: NodeBuilderContext - ) { - return !!resolveName( - context.enclosingDeclaration, - escapedName, - SymbolFlags.Type, /*nameNotFoundArg*/ - undefined, - escapedName, /*isUse*/ - false - ); - } - - function typeParameterToName( - type: TypeParameter, - context: NodeBuilderContext - ) { - if (context.flags - & NodeBuilderFlags.GenerateNamesForShadowedTypeParams - && context.typeParameterNames) - { - const cached = context.typeParameterNames - .get('' + getTypeId(type)); - if (cached) { - return cached; - } - } - let result = symbolToName( - type.symbol, - context, - SymbolFlags.Type, /*expectsIdentifier*/ - true - ); - if (!(result.kind & SyntaxKind.Identifier)) { - return createIdentifier('(Missing type parameter)'); - } - if (context.flags - & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) - { - const rawtext = result.escapedText as string; - let i = 0; - let text = rawtext; - while ((context.typeParameterNamesByText - && context.typeParameterNamesByText.get(text)) - || typeParameterShadowsNameInScope( - text as __String, - context - )) - { - i++; - text = `${rawtext}_${i}`; - } - if (text !== rawtext) { - result = createIdentifier(text, result.typeArguments); - } - (context.typeParameterNames - || (context.typeParameterNames = createMap())).set( - '' + getTypeId(type), - result - ); - (context.typeParameterNamesByText - || (context.typeParameterNamesByText = createMap())) - .set(result.escapedText as string, true); - } - return result; - } - - function symbolToName( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags, - expectsIdentifier: true - ): Identifier; - function symbolToName( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags, - expectsIdentifier: false - ): EntityName; - function symbolToName( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags, - expectsIdentifier: boolean - ): EntityName { - const chain = lookupSymbolChain(symbol, context, meaning); - - if (expectsIdentifier && chain.length !== 1 - && !context.encounteredError - && !(context.flags - & NodeBuilderFlags - .AllowQualifedNameInPlaceOfIdentifier)) - { - context.encounteredError = true; - } - return createEntityNameFromSymbolChain( - chain, - chain.length - 1 - ); - - function createEntityNameFromSymbolChain( - chain: Symbol[], - index: number - ): EntityName { - const typeParameterNodes = lookupTypeParameterNodes( - chain, - index, - context - ); - const symbol = chain[index]; - - if (index === 0) { - context.flags |= NodeBuilderFlags.InInitialEntityName; - } - const symbolName = getNameOfSymbolAsWritten( - symbol, - context - ); - if (index === 0) { - context.flags ^= NodeBuilderFlags.InInitialEntityName; - } - - const identifier = setEmitFlags( - createIdentifier(symbolName, typeParameterNodes), - EmitFlags.NoAsciiEscaping - ); - identifier.symbol = symbol; - - return index > 0 - ? createQualifiedName( - createEntityNameFromSymbolChain(chain, index - 1), - identifier - ) - : identifier; - } - } - - function symbolToExpression( - symbol: Symbol, - context: NodeBuilderContext, - meaning: SymbolFlags - ) { - const chain = lookupSymbolChain(symbol, context, meaning); - - return createExpressionFromSymbolChain( - chain, - chain.length - 1 - ); - - function createExpressionFromSymbolChain( - chain: Symbol[], - index: number - ): Expression { - const typeParameterNodes = lookupTypeParameterNodes( - chain, - index, - context - ); - const symbol = chain[index]; - - if (index === 0) { - context.flags |= NodeBuilderFlags.InInitialEntityName; - } - let symbolName = getNameOfSymbolAsWritten(symbol, context); - if (index === 0) { - context.flags ^= NodeBuilderFlags.InInitialEntityName; - } - let firstChar = symbolName.charCodeAt(0); - - if (isSingleOrDoubleQuote(firstChar) - && some( - symbol.declarations, - hasNonGlobalAugmentationExternalModuleSymbol - )) - { - return createLiteral(getSpecifierForModuleSymbol( - symbol, - context - )); - } - const canUsePropertyAccess = - firstChar === CharacterCodes.hash - ? symbolName.length > 1 && isIdentifierStart( - symbolName.charCodeAt(1), - languageVersion - ) - : isIdentifierStart(firstChar, languageVersion); - if (index === 0 || canUsePropertyAccess) { - const identifier = setEmitFlags( - createIdentifier(symbolName, typeParameterNodes), - EmitFlags.NoAsciiEscaping - ); - identifier.symbol = symbol; - - return index > 0 - ? createPropertyAccess( - createExpressionFromSymbolChain( - chain, - index - 1 - ), - identifier - ) - : identifier; - } else { - if (firstChar === CharacterCodes.openBracket) { - symbolName = symbolName.substring( - 1, - symbolName.length - 1 - ); - firstChar = symbolName.charCodeAt(0); - } - let expression: Expression | undefined; - if (isSingleOrDoubleQuote(firstChar)) { - expression = createLiteral(symbolName.substring( - 1, - symbolName.length - 1 - ).replace(/\\./g, s => s.substring(1))); - (expression as StringLiteral) - .singleQuote = firstChar - === CharacterCodes.singleQuote; - } else if (('' + +symbolName) === symbolName) { - expression = createLiteral(+symbolName); - } - if (!expression) { - expression = setEmitFlags( - createIdentifier( - symbolName, - typeParameterNodes - ), - EmitFlags.NoAsciiEscaping - ); - expression.symbol = symbol; - } - return createElementAccess( - createExpressionFromSymbolChain(chain, index - 1), - expression - ); - } - } - } - - function isSingleQuotedStringNamed(d: Declaration) { - const name = getNameOfDeclaration(d); - if (name && isStringLiteral(name) && ( - name.singleQuote - || (!nodeIsSynthesized(name) - && startsWith( - getTextOfNode(name, /*includeTrivia*/ false), - '\'' - )) - )) { - return true; - } - return false; - } - - function getPropertyNameNodeForSymbol( - symbol: Symbol, - context: NodeBuilderContext - ) { - const singleQuote = !!length(symbol.declarations) - && every(symbol.declarations, isSingleQuotedStringNamed); - const fromNameType = getPropertyNameNodeForSymbolFromNameType( - symbol, - context, - singleQuote - ); - if (fromNameType) { - return fromNameType; - } - if (isKnownSymbol(symbol)) { - return createComputedPropertyName(createPropertyAccess( - createIdentifier('Symbol'), - (symbol.escapedName as string).substr(3) - )); - } - const rawName = unescapeLeadingUnderscores(symbol.escapedName); - return createPropertyNameNodeForIdentifierOrLiteral( - rawName, - singleQuote - ); - } - - // See getNameForSymbolFromNameType for a stringy equivalent - function getPropertyNameNodeForSymbolFromNameType( - symbol: Symbol, - context: NodeBuilderContext, - singleQuote?: boolean - ) { - const nameType = symbol.nameType; - if (nameType) { - if (nameType.flags & TypeFlags.StringOrNumberLiteral) { - const name = '' - + ( nameType).value; - if (!isIdentifierText(name, compilerOptions.target) - && !isNumericLiteralName(name)) - { - return createLiteral(name, !!singleQuote); - } - if (isNumericLiteralName(name) - && startsWith(name, '-')) - { - return createComputedPropertyName(createLiteral(+name)); - } - return createPropertyNameNodeForIdentifierOrLiteral(name); - } - if (nameType.flags & TypeFlags.UniqueESSymbol) { - return createComputedPropertyName(symbolToExpression( - ( nameType).symbol, - context, - SymbolFlags.Value - )); - } - } - } - - function createPropertyNameNodeForIdentifierOrLiteral( - name: string, - singleQuote?: boolean - ) { - return isIdentifierText(name, compilerOptions.target) - ? createIdentifier(name) - : createLiteral( - isNumericLiteralName(name) ? +name : name, - !!singleQuote - ); - } - - function cloneNodeBuilderContext(context: - NodeBuilderContext): NodeBuilderContext - { - const initial: NodeBuilderContext = { ...context }; - // Make type parameters created within this context not consume the name outside this context - // The symbol serializer ends up creating many sibling scopes that all need "separate" contexts when - // it comes to naming things - within a normal `typeToTypeNode` call, the node builder only ever descends - // through the type tree, so the only cases where we could have used distinct sibling scopes was when there - // were multiple generic overloads with similar generated type parameter names - // The effect: - // When we write out - // export const x: (x: T) => T - // export const y: (x: T) => T - // we write it out like that, rather than as - // export const x: (x: T) => T - // export const y: (x: T_1) => T_1 - if (initial.typeParameterNames) { - initial - .typeParameterNames = cloneMap(initial - .typeParameterNames); - } - if (initial.typeParameterNamesByText) { - initial - .typeParameterNamesByText = cloneMap(initial - .typeParameterNamesByText); - } - if (initial.typeParameterSymbolList) { - initial - .typeParameterSymbolList = cloneMap(initial - .typeParameterSymbolList); - } - return initial; - } - - function symbolTableToDeclarationStatements( - symbolTable: SymbolTable, - context: NodeBuilderContext, - bundled?: boolean - ): Statement[] { - const serializePropertySymbolForClass = - makeSerializePropertySymbol( - createProperty, - SyntaxKind.MethodDeclaration, /*useAcessors*/ - true - ); - const serializePropertySymbolForInterfaceWorker = - makeSerializePropertySymbol( - ( - _decorators, - mods, - name, - question, - type, - initializer - ) => createPropertySignature( - mods, - name, - question, - type, - initializer - ), - SyntaxKind.MethodSignature, /*useAcessors*/ - false - ); - - // TODO: Use `setOriginalNode` on original declaration names where possible so these declarations see some kind of - // declaration mapping - - // We save the enclosing declaration off here so it's not adjusted by well-meaning declaration - // emit codepaths which want to apply more specific contexts (so we can still refer to the root real declaration - // we're trying to emit from later on) - const enclosingDeclaration = context.enclosingDeclaration!; - let results: Statement[] = []; - const visitedSymbols: Map = createMap(); - let deferredPrivates: Map | undefined; - const oldcontext = context; - context = { - ...oldcontext, - usedSymbolNames: createMap(), - remappedSymbolNames: createMap(), - tracker: { - ...oldcontext.tracker, - trackSymbol: (sym, decl, meaning) => { - const accessibleResult = isSymbolAccessible( - sym, - decl, - meaning, /*computeALiases*/ - false - ); - if (accessibleResult.accessibility - === SymbolAccessibility.Accessible) - { - // Lookup the root symbol of the chain of refs we'll use to access it and serialize it - const chain = lookupSymbolChainWorker( - sym, - context, - meaning - ); - if (!(sym.flags & SymbolFlags.Property)) { - includePrivateSymbol(chain[0]); - } - } else if (oldcontext.tracker - && oldcontext.tracker.trackSymbol) - { - oldcontext.tracker.trackSymbol( - sym, - decl, - meaning - ); - } - } - } - }; - if (oldcontext.usedSymbolNames) { - oldcontext.usedSymbolNames.forEach((_, name) => { - context.usedSymbolNames!.set(name, true); - }); - } - forEachEntry(symbolTable, (symbol, name) => { - const baseName = unescapeLeadingUnderscores(name); - void getInternalSymbolName( - symbol, - baseName - ); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames` - }); - let addingDeclare = !bundled; - const exportEquals = symbolTable - .get(InternalSymbolName.ExportEquals); - if (exportEquals && symbolTable.size > 1 - && exportEquals.flags & SymbolFlags.Alias) - { - symbolTable = createSymbolTable(); - // Remove extraneous elements from root symbol table (they'll be mixed back in when the target of the `export=` is looked up) - symbolTable.set( - InternalSymbolName.ExportEquals, - exportEquals - ); - } - - visitSymbolTable(symbolTable); - return mergeRedundantStatements(results); - - function isIdentifierAndNotUndefined(node: Node - | undefined): node is Identifier - { - return !!node && node.kind === SyntaxKind.Identifier; - } - - function getNamesOfDeclaration(statement: - Statement): Identifier[] - { - if (isVariableStatement(statement)) { - return filter( - map( - statement.declarationList.declarations, - getNameOfDeclaration - ), - isIdentifierAndNotUndefined - ); - } - return filter( - [getNameOfDeclaration(statement as DeclarationStatement)], - isIdentifierAndNotUndefined - ); - } - - function flattenExportAssignedNamespace(statements: - Statement[]) - { - const exportAssignment = find( - statements, - isExportAssignment - ); - const ns = find(statements, isModuleDeclaration); - if (ns && exportAssignment - && exportAssignment.isExportEquals - && isIdentifier(exportAssignment.expression) - && isIdentifier(ns.name) - && idText(ns.name) - === idText(exportAssignment.expression) - && ns.body && isModuleBlock(ns.body)) - { - // Pass 0: Correct situations where a module has both an `export = ns` and multiple top-level exports by stripping the export modifiers from - // the top-level exports and exporting them in the targeted ns, as can occur when a js file has both typedefs and `module.export` assignments - const excessExports = filter( - statements, - s => !!(getModifierFlags(s) & ModifierFlags.Export) - ); - if (length(excessExports)) { - ns.body - .statements = createNodeArray([...ns.body - .statements, createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(map( - flatMap( - excessExports, - e => getNamesOfDeclaration(e) - ), - id => createExportSpecifier( - /*alias*/ undefined, - id - ) - )), - /*moduleSpecifier*/ undefined - )]); - } - - // Pass 1: Flatten `export namespace _exports {} export = _exports;` so long as the `export=` only points at a single namespace declaration - if (!find( - statements, - s => s !== ns - && nodeHasName(s, ns.name as Identifier) - )) { - results = []; - forEach(ns.body.statements, s => { - addResult( - s, - ModifierFlags.None - ); // Recalculates the ambient (and export, if applicable from above) flag - }); - statements = [...filter( - statements, - s => s !== ns && s !== exportAssignment - ), ...results]; - } - } - return statements; - } - - function mergeExportDeclarations(statements: Statement[]) { - // Pass 2: Combine all `export {}` declarations - const exports = filter( - statements, - d => isExportDeclaration(d) && !d.moduleSpecifier - && !!d.exportClause - && isNamedExports(d.exportClause) - ) as ExportDeclaration[]; - if (length(exports) > 1) { - const nonExports = filter( - statements, - d => !isExportDeclaration(d) || !!d.moduleSpecifier - || !d.exportClause - ); - statements = [...nonExports, createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(flatMap( - exports, - e => cast(e.exportClause, isNamedExports) - .elements - )), - /*moduleSpecifier*/ undefined - )]; - } - // Pass 2b: Also combine all `export {} from "..."` declarations as needed - const reexports = filter( - statements, - d => isExportDeclaration(d) && !!d.moduleSpecifier - && !!d.exportClause - && isNamedExports(d.exportClause) - ) as ExportDeclaration[]; - if (length(reexports) > 1) { - const groups = group( - reexports, - decl => isStringLiteral(decl.moduleSpecifier!) - ? '>' + decl.moduleSpecifier.text - : '>' - ); - if (groups.length !== reexports.length) { - for (const group of groups) { - if (group.length > 1) { - // remove group members from statements and then merge group members and add back to statements - statements = [ - ...filter( - statements, - s => group - .indexOf(s as ExportDeclaration) - === -1 - ), - createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(flatMap( - group, - e => cast( - e.exportClause, - isNamedExports - ).elements - )), - group[0].moduleSpecifier - ) - ]; - } - } - } - } - return statements; - } - - function inlineExportModifiers(statements: Statement[]) { - // Pass 3: Move all `export {}`'s to `export` modifiers where possible - const exportDecl = find( - statements, - d => isExportDeclaration(d) && !d.moduleSpecifier - && !!d.exportClause - ) as ExportDeclaration | undefined; - if (exportDecl && exportDecl.exportClause - && isNamedExports(exportDecl.exportClause)) - { - const replacements = mapDefined( - exportDecl.exportClause.elements, - e => { - if (!e.propertyName) { - // export {name} - look thru `statements` for `name`, and if all results can take an `export` modifier, do so and filter it - const associated = filter( - statements, - s => nodeHasName(s, e.name) - ); - if (length(associated) - && every( - associated, - canHaveExportModifier - )) - { - forEach(associated, addExportModifier); - return undefined; - } - } - return e; - } - ); - if (!length(replacements)) { - // all clauses removed, filter the export declaration - statements = filter( - statements, - s => s !== exportDecl - ); - } else { - // some items filtered, others not - update the export declaration - // (mutating because why not, we're building a whole new tree here anyway) - exportDecl.exportClause - .elements = createNodeArray(replacements); - } - } - return statements; - } - - function mergeRedundantStatements(statements: Statement[]) { - statements = flattenExportAssignedNamespace(statements); - statements = mergeExportDeclarations(statements); - statements = inlineExportModifiers(statements); - - // Not a cleanup, but as a final step: If there is a mix of `export` and non-`export` declarations, but no `export =` or `export {}` add a `export {};` so - // declaration privacy is respected. - if (enclosingDeclaration - && ((isSourceFile(enclosingDeclaration) - && isExternalOrCommonJsModule(enclosingDeclaration)) - || isModuleDeclaration(enclosingDeclaration)) - && (!some(statements, isExternalModuleIndicator) - || (!hasScopeMarker(statements) - && some(statements, needsScopeMarker)))) - { - statements.push(createEmptyExports()); - } - return statements; - } - - function canHaveExportModifier(node: Statement) { - return isEnumDeclaration(node) - || isVariableStatement(node) - || isFunctionDeclaration(node) - || isClassDeclaration(node) - || (isModuleDeclaration(node) - && !isExternalModuleAugmentation(node) - && !isGlobalScopeAugmentation(node)) - || isInterfaceDeclaration(node) - || isTypeDeclaration(node); - } - - function addExportModifier(statement: Statement) { - const flags = - (getModifierFlags(statement) | ModifierFlags.Export) - & ~ModifierFlags.Ambient; - statement - .modifiers = createNodeArray(createModifiersFromModifierFlags(flags)); - statement.modifierFlagsCache = 0; - } - - function visitSymbolTable( - symbolTable: SymbolTable, - suppressNewPrivateContext?: boolean, - propertyAsAlias?: boolean - ) { - const oldDeferredPrivates = deferredPrivates; - if (!suppressNewPrivateContext) { - deferredPrivates = createMap(); - } - symbolTable.forEach((symbol: Symbol) => { - serializeSymbol( - symbol, /*isPrivate*/ - false, - !!propertyAsAlias - ); - }); - if (!suppressNewPrivateContext) { - // deferredPrivates will be filled up by visiting the symbol table - // And will continue to iterate as elements are added while visited `deferredPrivates` - // (As that's how a map iterator is defined to work) - deferredPrivates!.forEach((symbol: Symbol) => { - serializeSymbol( - symbol, /*isPrivate*/ - true, - !!propertyAsAlias - ); - }); - } - deferredPrivates = oldDeferredPrivates; - } - - function serializeSymbol( - symbol: Symbol, - isPrivate: boolean, - propertyAsAlias: boolean - ) { - // cache visited list based on merged symbol, since we want to use the unmerged top-level symbol, but - // still skip reserializing it if we encounter the merged product later on - const visitedSym = getMergedSymbol(symbol); - if (visitedSymbols.has('' + getSymbolId(visitedSym))) { - return; // Already printed - } - visitedSymbols.set('' + getSymbolId(visitedSym), true); - // Only actually serialize symbols within the correct enclosing declaration, otherwise do nothing with the out-of-context symbol - const skipMembershipCheck = - !isPrivate; // We only call this on exported symbols when we know they're in the correct scope - if (skipMembershipCheck - || (!!length(symbol.declarations) - && some( - symbol.declarations, - d => !!findAncestor( - d, - n => n === enclosingDeclaration - ) - ))) - { - const oldContext = context; - context = cloneNodeBuilderContext(context); - const result = serializeSymbolWorker( - symbol, - isPrivate, - propertyAsAlias - ); - context = oldContext; - return result; - } - } - - // Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias - // or a merge of some number of those. - // An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping - // each symbol in only one of the representations - // Also, synthesizing a default export of some kind - // If it's an alias: emit `export default ref` - // If it's a property: emit `export default _default` with a `_default` prop - // If it's a class/interface/function: emit a class/interface/function with a `default` modifier - // These forms can merge, eg (`export default 12; export default interface A {}`) - function serializeSymbolWorker( - symbol: Symbol, - isPrivate: boolean, - propertyAsAlias: boolean - ) { - const symbolName = - unescapeLeadingUnderscores(symbol.escapedName); - const isDefault = - symbol.escapedName === InternalSymbolName.Default; - if (isStringANonContextualKeyword(symbolName) - && !isDefault) - { - // Oh no. We cannot use this symbol's name as it's name... It's likely some jsdoc had an invalid name like `export` or `default` :( - context.encounteredError = true; - // TODO: Issue error via symbol tracker? - return; // If we need to emit a private with a keyword name, we're done for, since something else will try to refer to it by that name - } - const needsPostExportDefault = isDefault && !!( - symbol.flags - & SymbolFlags.ExportDoesNotSupportDefaultModifier - || (symbol.flags & SymbolFlags.Function - && length(getPropertiesOfType(getTypeOfSymbol(symbol)))) - ) - && !(symbol.flags - & SymbolFlags - .Alias); // An alias symbol should preclude needing to make an alias ourselves - if (needsPostExportDefault) { - isPrivate = true; - } - const modifierFlags = - (!isPrivate ? ModifierFlags.Export : 0) - | (isDefault && !needsPostExportDefault - ? ModifierFlags.Default - : 0); - const isConstMergedWithNS = - symbol.flags & SymbolFlags.Module - && symbol.flags - & (SymbolFlags.BlockScopedVariable - | SymbolFlags.FunctionScopedVariable - | SymbolFlags.Property) - && symbol.escapedName - !== InternalSymbolName.ExportEquals; - const isConstMergedWithNSPrintableAsSignatureMerge = - isConstMergedWithNS - && isTypeRepresentableAsFunctionNamespaceMerge( - getTypeOfSymbol(symbol), - symbol - ); - if (symbol.flags & SymbolFlags.Function - || isConstMergedWithNSPrintableAsSignatureMerge) - { - serializeAsFunctionNamespaceMerge( - getTypeOfSymbol(symbol), - symbol, - getInternalSymbolName(symbol, symbolName), - modifierFlags - ); - } - if (symbol.flags & SymbolFlags.TypeAlias) { - serializeTypeAlias(symbol, symbolName, modifierFlags); - } - // Need to skip over export= symbols below - json source files get a single `Property` flagged - // symbol of name `export=` which needs to be handled like an alias. It's not great, but it is what it is. - if (symbol.flags - & (SymbolFlags.BlockScopedVariable - | SymbolFlags.FunctionScopedVariable - | SymbolFlags.Property) - && symbol.escapedName - !== InternalSymbolName.ExportEquals - && !(symbol.flags & SymbolFlags.Prototype) - && !(symbol.flags & SymbolFlags.Class) - && !isConstMergedWithNSPrintableAsSignatureMerge) - { - serializeVariableOrProperty( - symbol, - symbolName, - isPrivate, - needsPostExportDefault, - propertyAsAlias, - modifierFlags - ); - } - if (symbol.flags & SymbolFlags.Enum) { - serializeEnum(symbol, symbolName, modifierFlags); - } - if (symbol.flags & SymbolFlags.Class) { - if (symbol.flags & SymbolFlags.Property) { - // Looks like a `module.exports.Sub = class {}` - if we serialize `symbol` as a class, the result will have no members, - // since the classiness is actually from the target of the effective alias the symbol is. yes. A BlockScopedVariable|Class|Property - // _really_ acts like an Alias, and none of a BlockScopedVariable, Class, or Property. This is the travesty of JS binding today. - serializeAsAlias( - symbol, - getInternalSymbolName(symbol, symbolName), - modifierFlags - ); - } else { - serializeAsClass( - symbol, - getInternalSymbolName(symbol, symbolName), - modifierFlags - ); - } - } - if ((symbol.flags - & (SymbolFlags.ValueModule - | SymbolFlags.NamespaceModule) - && (!isConstMergedWithNS - || isTypeOnlyNamespace(symbol))) - || isConstMergedWithNSPrintableAsSignatureMerge) - { - serializeModule(symbol, symbolName, modifierFlags); - } - if (symbol.flags & SymbolFlags.Interface) { - serializeInterface(symbol, symbolName, modifierFlags); - } - if (symbol.flags & SymbolFlags.Alias) { - serializeAsAlias( - symbol, - getInternalSymbolName(symbol, symbolName), - modifierFlags - ); - } - if (symbol.flags & SymbolFlags.Property - && symbol.escapedName - === InternalSymbolName.ExportEquals) - { - serializeMaybeAliasAssignment(symbol); - } - if (symbol.flags & SymbolFlags.ExportStar) { - // synthesize export * from "moduleReference" - // Straightforward - only one thing to do - make an export declaration - for (const node of symbol.declarations) { - const resolvedModule = resolveExternalModuleName( - node, - (node as ExportDeclaration).moduleSpecifier! - ); - if (!resolvedModule) continue; - addResult( - createExportDeclaration( - /*decorators*/ undefined, /*modifiers*/ - undefined, /*exportClause*/ - undefined, - createLiteral(getSpecifierForModuleSymbol( - resolvedModule, - context - )) - ), - ModifierFlags.None - ); - } - } - if (needsPostExportDefault) { - addResult( - createExportAssignment( - /*decorators*/ undefined, /*modifiers*/ - undefined, /*isExportAssignment*/ - false, - createIdentifier(getInternalSymbolName( - symbol, - symbolName - )) - ), - ModifierFlags.None - ); - } - } - - function includePrivateSymbol(symbol: Symbol) { - if (some( - symbol.declarations, - isParameterDeclaration - )) - return; - Debug.assertDefined(deferredPrivates); - getUnusedName( - unescapeLeadingUnderscores(symbol.escapedName), - symbol - ); // Call to cache unique name for symbol - deferredPrivates!.set('' + getSymbolId(symbol), symbol); - } - - function isExportingScope(enclosingDeclaration: Node) { - return ((isSourceFile(enclosingDeclaration) - && (isExternalOrCommonJsModule(enclosingDeclaration) - || isJsonSourceFile(enclosingDeclaration))) - || (isAmbientModule(enclosingDeclaration) - && !isGlobalScopeAugmentation(enclosingDeclaration))); - } - - // Prepends a `declare` and/or `export` modifier if the context requires it, and then adds `node` to `result` and returns `node` - // Note: This _mutates_ `node` without using `updateNode` - the assumption being that all nodes should be manufactured fresh by the node builder - function addResult( - node: Statement, - additionalModifierFlags: ModifierFlags - ) { - let newModifierFlags: ModifierFlags = ModifierFlags.None; - if (additionalModifierFlags & ModifierFlags.Export - && enclosingDeclaration - && isExportingScope(enclosingDeclaration) - && canHaveExportModifier(node)) - { - // Classes, namespaces, variables, functions, interfaces, and types should all be `export`ed in a module context if not private - newModifierFlags |= ModifierFlags.Export; - } - if (addingDeclare - && !(newModifierFlags & ModifierFlags.Export) - && (!enclosingDeclaration - || !(enclosingDeclaration.flags - & NodeFlags.Ambient)) - && (isEnumDeclaration(node) - || isVariableStatement(node) - || isFunctionDeclaration(node) - || isClassDeclaration(node) - || isModuleDeclaration(node))) - { - // Classes, namespaces, variables, enums, and functions all need `declare` modifiers to be valid in a declaration file top-level scope - newModifierFlags |= ModifierFlags.Ambient; - } - if ((additionalModifierFlags & ModifierFlags.Default) - && (isClassDeclaration(node) - || isInterfaceDeclaration(node) - || isFunctionDeclaration(node))) - { - newModifierFlags |= ModifierFlags.Default; - } - if (newModifierFlags) { - node - .modifiers = createNodeArray(createModifiersFromModifierFlags(newModifierFlags - | getModifierFlags(node))); - node - .modifierFlagsCache = 0; // Reset computed flags cache - } - results.push(node); - } - - function serializeTypeAlias( - symbol: Symbol, - symbolName: string, - modifierFlags: ModifierFlags - ) { - const aliasType = getDeclaredTypeOfTypeAlias(symbol); - const typeParams = getSymbolLinks(symbol).typeParameters; - const typeParamDecls = map( - typeParams, - p => typeParameterToDeclaration(p, context) - ); - const jsdocAliasDecl = find( - symbol.declarations, - isJSDocTypeAlias - ); - const commentText = jsdocAliasDecl - ? jsdocAliasDecl.comment - || jsdocAliasDecl.parent.comment - : undefined; - const oldFlags = context.flags; - context.flags |= NodeBuilderFlags.InTypeAlias; - addResult(setSyntheticLeadingComments( - createTypeAliasDeclaration( - /*decorators*/ undefined, /*modifiers*/ - undefined, - getInternalSymbolName(symbol, symbolName), - typeParamDecls, - typeToTypeNodeHelper(aliasType, context) - ), - !commentText - ? [] - : [{ kind: SyntaxKind.MultiLineCommentTrivia, - text: '*\n * ' - + commentText.replace(/\n/g, '\n * ') - + '\n ', pos: -1, end: -1, - hasTrailingNewLine: true }] - ), modifierFlags); - context.flags = oldFlags; - } - - function serializeInterface( - symbol: Symbol, - symbolName: string, - modifierFlags: ModifierFlags - ) { - const interfaceType = - getDeclaredTypeOfClassOrInterface(symbol); - const localParams = - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - const typeParamDecls = map( - localParams, - p => typeParameterToDeclaration(p, context) - ); - const baseTypes = getBaseTypes(interfaceType); - const baseType = length(baseTypes) - ? getIntersectionType(baseTypes) - : undefined; - const members = flatMap( - getPropertiesOfType(interfaceType), - p => serializePropertySymbolForInterface(p, baseType) - ); - const callSignatures = serializeSignatures( - SignatureKind.Call, - interfaceType, - baseType, - SyntaxKind.CallSignature - ) as CallSignatureDeclaration[]; - const constructSignatures = serializeSignatures( - SignatureKind.Construct, - interfaceType, - baseType, - SyntaxKind.ConstructSignature - ) as ConstructSignatureDeclaration[]; - const indexSignatures = serializeIndexSignatures( - interfaceType, - baseType - ); - - const heritageClauses = !length(baseTypes) - ? undefined - : [createHeritageClause( - SyntaxKind.ExtendsKeyword, - mapDefined( - baseTypes, - b => trySerializeAsTypeReference(b) - ) - )]; - addResult(createInterfaceDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - getInternalSymbolName(symbol, symbolName), - typeParamDecls, - heritageClauses, - [...indexSignatures, ...constructSignatures, - ...callSignatures, ...members] - ), modifierFlags); - } - - function getNamespaceMembersForSerialization(symbol: Symbol) { - return !symbol.exports - ? [] - : filter( - arrayFrom((symbol.exports).values()), - p => !((p.flags & SymbolFlags.Prototype) - || (p.escapedName === 'prototype')) - ); - } - - function isTypeOnlyNamespace(symbol: Symbol) { - return every( - getNamespaceMembersForSerialization(symbol), - m => !(resolveSymbol(m).flags & SymbolFlags.Value) - ); - } - - function serializeModule( - symbol: Symbol, - symbolName: string, - modifierFlags: ModifierFlags - ) { - const members = - getNamespaceMembersForSerialization(symbol); - // Split NS members up by declaration - members whose parent symbol is the ns symbol vs those whose is not (but were added in later via merging) - const locationMap = arrayToMultiMap( - members, - m => m.parent && m.parent === symbol - ? 'real' - : 'merged' - ); - const realMembers = locationMap.get('real') || emptyArray; - const mergedMembers = locationMap.get('merged') - || emptyArray; - // TODO: `suppressNewPrivateContext` is questionable -we need to simply be emitting privates in whatever scope they were declared in, rather - // than whatever scope we traverse to them in. That's a bit of a complex rewrite, since we're not _actually_ tracking privates at all in advance, - // so we don't even have placeholders to fill in. - if (length(realMembers)) { - const localName = getInternalSymbolName( - symbol, - symbolName - ); - serializeAsNamespaceDeclaration( - realMembers, - localName, - modifierFlags, - !!(symbol.flags - & (SymbolFlags.Function - | SymbolFlags.Assignment)) - ); - } - if (length(mergedMembers)) { - const localName = getInternalSymbolName( - symbol, - symbolName - ); - const nsBody = - createModuleBlock([createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports(map( - filter( - mergedMembers, - n => n.escapedName - !== InternalSymbolName.ExportEquals - ), - s => { - const name = - unescapeLeadingUnderscores(s - .escapedName); - const localName = - getInternalSymbolName(s, name); - const aliasDecl = s.declarations - && getDeclarationOfAliasSymbol(s); - const target = aliasDecl - && getTargetOfAliasDeclaration( - aliasDecl, /*dontRecursivelyResolve*/ - true - ); - includePrivateSymbol(target || s); - const targetName = target - ? getInternalSymbolName( - target, - unescapeLeadingUnderscores(target - .escapedName) - ) - : localName; - return createExportSpecifier( - name === targetName - ? undefined - : targetName, - name - ); - } - )) - )]); - addResult(createModuleDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createIdentifier(localName), - nsBody, - NodeFlags.Namespace - ), ModifierFlags.None); - } - } - - function serializeEnum( - symbol: Symbol, - symbolName: string, - modifierFlags: ModifierFlags - ) { - addResult(createEnumDeclaration( - /*decorators*/ undefined, - createModifiersFromModifierFlags(isConstEnumSymbol(symbol) - ? ModifierFlags.Const - : 0), - getInternalSymbolName(symbol, symbolName), - map( - filter( - getPropertiesOfType(getTypeOfSymbol(symbol)), - p => !!(p.flags & SymbolFlags.EnumMember) - ), - p => { - // TODO: Handle computed names - // I hate that to get the initialized value we need to walk back to the declarations here; but there's no - // other way to get the possible const value of an enum member that I'm aware of, as the value is cached - // _on the declaration_, not on the declaration's symbol... - const initializedValue = p.declarations - && p.declarations[0] - && isEnumMember(p.declarations[0]) - && getConstantValue(p.declarations - [0] as EnumMember); - return createEnumMember( - unescapeLeadingUnderscores(p.escapedName), - initializedValue === undefined - ? undefined - : createLiteral(initializedValue) - ); - } - ) - ), modifierFlags); - } - - function serializeVariableOrProperty( - symbol: Symbol, - symbolName: string, - isPrivate: boolean, - needsPostExportDefault: boolean, - propertyAsAlias: boolean | undefined, - modifierFlags: ModifierFlags - ) { - if (propertyAsAlias) { - serializeMaybeAliasAssignment(symbol); - } else { - const type = getTypeOfSymbol(symbol); - const localName = getInternalSymbolName( - symbol, - symbolName - ); - if (!(symbol.flags & SymbolFlags.Function) - && isTypeRepresentableAsFunctionNamespaceMerge( - type, - symbol - )) - { - // If the type looks like a function declaration + ns could represent it, and it's type is sourced locally, rewrite it into a function declaration + ns - serializeAsFunctionNamespaceMerge( - type, - symbol, - localName, - modifierFlags - ); - } else { - // A Class + Property merge is made for a `module.exports.Member = class {}`, and it doesn't serialize well as either a class _or_ a property symbol - in fact, _it behaves like an alias!_ - // `var` is `FunctionScopedVariable`, `const` and `let` are `BlockScopedVariable`, and `module.exports.thing =` is `Property` - const flags = - !(symbol.flags - & SymbolFlags.BlockScopedVariable) - ? undefined - : isConstVariable(symbol) - ? NodeFlags.Const - : NodeFlags.Let; - const name = - (needsPostExportDefault - || !(symbol.flags & SymbolFlags.Property)) - ? localName - : getUnusedName(localName, symbol); - let textRange: Node | undefined = - symbol.declarations - && find( - symbol.declarations, - d => isVariableDeclaration(d) - ); - if (textRange - && isVariableDeclarationList(textRange.parent) - && textRange.parent.declarations.length === 1) - { - textRange = textRange.parent.parent; - } - const statement = setTextRange( - createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration( - name, - serializeTypeForDeclaration( - type, - symbol - ) - ) - ], flags) - ), - textRange - ); - addResult( - statement, - name !== localName - ? modifierFlags & ~ModifierFlags.Export - : modifierFlags - ); - if (name !== localName && !isPrivate) { - // We rename the variable declaration we generate for Property symbols since they may have a name which - // conflicts with a local declaration. For example, given input: - // ``` - // function g() {} - // module.exports.g = g - // ``` - // In such a situation, we have a local variable named `g`, and a seperate exported variable named `g`. - // Naively, we would emit - // ``` - // function g() {} - // export const g: typeof g; - // ``` - // That's obviously incorrect - the `g` in the type annotation needs to refer to the local `g`, but - // the export declaration shadows it. - // To work around that, we instead write - // ``` - // function g() {} - // const g_1: typeof g; - // export { g_1 as g }; - // ``` - // To create an export named `g` that does _not_ shadow the local `g` - addResult( - createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports([createExportSpecifier( - name, - localName - )]) - ), - ModifierFlags.None - ); - } - } - } - } - - function serializeAsFunctionNamespaceMerge( - type: Type, - symbol: Symbol, - localName: string, - modifierFlags: ModifierFlags - ) { - const signatures = getSignaturesOfType( - type, - SignatureKind.Call - ); - for (const sig of signatures) { - // Each overload becomes a separate function declaration, in order - const decl = signatureToSignatureDeclarationHelper( - sig, - SyntaxKind.FunctionDeclaration, - context - ) as FunctionDeclaration; - decl.name = createIdentifier(localName); - addResult( - setTextRange(decl, sig.declaration), - modifierFlags - ); - } - // Module symbol emit will take care of module-y members, provided it has exports - if (!(symbol.flags - & (SymbolFlags.ValueModule - | SymbolFlags.NamespaceModule) && !!symbol.exports - && !!symbol.exports.size)) - { - const props = filter( - getPropertiesOfType(type), - p => !((p.flags & SymbolFlags.Prototype) - || (p.escapedName === 'prototype')) - ); - serializeAsNamespaceDeclaration( - props, - localName, - modifierFlags, /*suppressNewPrivateContext*/ - true - ); - } - } - - function serializeAsNamespaceDeclaration( - props: readonly Symbol[], - localName: string, - modifierFlags: ModifierFlags, - suppressNewPrivateContext: boolean - ) { - if (length(props)) { - const localVsRemoteMap = arrayToMultiMap( - props, - p => !length(p.declarations) || some( - p.declarations, - d => getSourceFileOfNode(d) - === getSourceFileOfNode(context - .enclosingDeclaration!) - ) - ? 'local' - : 'remote' - ); - const localProps = localVsRemoteMap.get('local') - || emptyArray; - // handle remote props first - we need to make an `import` declaration that points at the module containing each remote - // prop in the outermost scope (TODO: a namespace within a namespace would need to be appropriately handled by this) - // Example: - // import Foo_1 = require("./exporter"); - // export namespace ns { - // import Foo = Foo_1.Foo; - // export { Foo }; - // export const c: number; - // } - // This is needed because in JS, statements like `const x = require("./f")` support both type and value lookup, even if they're - // normally just value lookup (so it functions kinda like an alias even when it's not an alias) - // _Usually_, we'll simply print the top-level as an alias instead of a `var` in such situations, however is is theoretically - // possible to encounter a situation where a type has members from both the current file and other files - in those situations, - // emit akin to the above would be needed. - - // Add a namespace - const fakespace = createModuleDeclaration( - /*decorators*/ undefined, /*modifiers*/ - undefined, - createIdentifier(localName), - createModuleBlock([]), - NodeFlags.Namespace - ); - fakespace.flags ^= NodeFlags - .Synthesized; // unset synthesized so it is usable as an enclosing declaration - fakespace.parent = enclosingDeclaration as SourceFile - | NamespaceDeclaration; - fakespace.locals = createSymbolTable(props); - fakespace.symbol = props[0].parent!; - const oldResults = results; - results = []; - const oldAddingDeclare = addingDeclare; - addingDeclare = false; - const subcontext = { ...context, - enclosingDeclaration: fakespace }; - const oldContext = context; - context = subcontext; - // TODO: implement handling for the localVsRemoteMap.get("remote") - should be difficult to trigger (see comment above), as only interesting cross-file js merges should make this possible - visitSymbolTable( - createSymbolTable(localProps), - suppressNewPrivateContext, /*propertyAsAlias*/ - true - ); - context = oldContext; - addingDeclare = oldAddingDeclare; - const declarations = results; - results = oldResults; - fakespace.flags ^= NodeFlags - .Synthesized; // reset synthesized - fakespace.parent = undefined!; - fakespace.locals = undefined!; - fakespace.symbol = undefined!; - fakespace.body = createModuleBlock(declarations); - addResult( - fakespace, - modifierFlags - ); // namespaces can never be default exported - } - } - - function serializeAsClass( - symbol: Symbol, - localName: string, - modifierFlags: ModifierFlags - ) { - const localParams = - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - const typeParamDecls = map( - localParams, - p => typeParameterToDeclaration(p, context) - ); - const classType = - getDeclaredTypeOfClassOrInterface(symbol); - const baseTypes = getBaseTypes(classType); - const staticType = getTypeOfSymbol(symbol); - const staticBaseType = - getBaseConstructorTypeOfClass(staticType as InterfaceType); - const heritageClauses = !length(baseTypes) - ? undefined - : [createHeritageClause( - SyntaxKind.ExtendsKeyword, - map( - baseTypes, - b => serializeBaseType( - b, - staticBaseType, - localName - ) - ) - )]; - const symbolProps = getPropertiesOfType(classType); - const publicSymbolProps = filter(symbolProps, s => { - const valueDecl = s.valueDeclaration; - Debug.assertDefined(valueDecl); - return !(isNamedDeclaration(valueDecl) - && isPrivateIdentifier(valueDecl.name)); - }); - const hasPrivateIdentifier = some(symbolProps, s => { - const valueDecl = s.valueDeclaration; - Debug.assertDefined(valueDecl); - return isNamedDeclaration(valueDecl) - && isPrivateIdentifier(valueDecl.name); - }); - // Boil down all private properties into a single one. - const privateProperties = hasPrivateIdentifier - ? [createProperty( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createPrivateIdentifier('#private'), - /*questionOrExclamationToken*/ undefined, - /*type*/ undefined, - /*initializer*/ undefined - )] - : emptyArray; - const publicProperties = flatMap( - publicSymbolProps, - p => serializePropertySymbolForClass( - p, /*isStatic*/ - false, - baseTypes[0] - ) - ); - // Consider static members empty if symbol also has function or module meaning - function namespacey emit will handle statics - const staticMembers = - symbol.flags - & (SymbolFlags.Function | SymbolFlags.ValueModule) - ? [] - : flatMap( - filter( - getPropertiesOfType(staticType), - p => !(p.flags & SymbolFlags.Prototype) - && p.escapedName !== 'prototype' - ), - p => serializePropertySymbolForClass( - p, /*isStatic*/ - true, - staticBaseType - ) - ); - const constructors = serializeSignatures( - SignatureKind.Construct, - staticType, - baseTypes[0], - SyntaxKind.Constructor - ) as ConstructorDeclaration[]; - for (const c of constructors) { - // A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration - // `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here - c.type = undefined; - c.typeParameters = undefined; - } - const indexSignatures = serializeIndexSignatures( - classType, - baseTypes[0] - ); - addResult(setTextRange( - createClassDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - localName, - typeParamDecls, - heritageClauses, - [...indexSignatures, ...staticMembers, - ...constructors, ...publicProperties, - ...privateProperties] - ), - symbol.declarations && filter( - symbol.declarations, - d => isClassDeclaration(d) || isClassExpression(d) - )[0] - ), modifierFlags); - } - - function serializeAsAlias( - symbol: Symbol, - localName: string, - modifierFlags: ModifierFlags - ) { - // synthesize an alias, eg `export { symbolName as Name }` - // need to mark the alias `symbol` points - // at as something we need to serialize as a private declaration as well - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - const target = - getMergedSymbol(getTargetOfAliasDeclaration( - node, /*dontRecursivelyResolve*/ - true - )); - if (!target) { - return; - } - let verbatimTargetName = - unescapeLeadingUnderscores(target.escapedName); - if (verbatimTargetName === InternalSymbolName.ExportEquals - && (compilerOptions.esModuleInterop - || compilerOptions.allowSyntheticDefaultImports)) - { - // target refers to an `export=` symbol that was hoisted into a synthetic default - rename here to match - verbatimTargetName = InternalSymbolName.Default; - } - const targetName = getInternalSymbolName( - target, - verbatimTargetName - ); - includePrivateSymbol(target); // the target may be within the same scope - attempt to serialize it first - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - // Could be a local `import localName = ns.member` or - // an external `import localName = require("whatever")` - const isLocalImport = - !(target.flags & SymbolFlags.ValueModule); - addResult( - createImportEqualsDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createIdentifier(localName), - isLocalImport - ? symbolToName( - target, - context, - SymbolFlags - .All, /*expectsIdentifier*/ - false - ) - : createExternalModuleReference(createLiteral(getSpecifierForModuleSymbol( - symbol, - context - ))) - ), - isLocalImport - ? modifierFlags - : ModifierFlags.None - ); - break; - case SyntaxKind.NamespaceExportDeclaration: - // export as namespace foo - // TODO: Not part of a file's local or export symbol tables - // Is bound into file.symbol.globalExports instead, which we don't currently traverse - addResult( - createNamespaceExportDeclaration(idText((node as NamespaceExportDeclaration) - .name)), - ModifierFlags.None - ); - break; - case SyntaxKind.ImportClause: - addResult(createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause( - createIdentifier(localName), /*namedBindings*/ - undefined - ), - // We use `target.parent || target` below as `target.parent` is unset when the target is a module which has been export assigned - // And then made into a default by the `esModuleInterop` or `allowSyntheticDefaultImports` flag - // In such cases, the `target` refers to the module itself already - createLiteral(getSpecifierForModuleSymbol( - target.parent || target, - context - )) - ), ModifierFlags.None); - break; - case SyntaxKind.NamespaceImport: - addResult(createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause( - /*importClause*/ undefined, - createNamespaceImport(createIdentifier(localName)) - ), - createLiteral(getSpecifierForModuleSymbol( - target, - context - )) - ), ModifierFlags.None); - break; - case SyntaxKind.NamespaceExport: - addResult(createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamespaceExport(createIdentifier(localName)), - createLiteral(getSpecifierForModuleSymbol( - target, - context - )) - ), ModifierFlags.None); - break; - case SyntaxKind.ImportSpecifier: - addResult(createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause( - /*importClause*/ undefined, - createNamedImports([ - createImportSpecifier( - localName !== verbatimTargetName - ? createIdentifier(verbatimTargetName) - : undefined, - createIdentifier(localName) - ) - ]) - ), - createLiteral(getSpecifierForModuleSymbol( - target.parent || target, - context - )) - ), ModifierFlags.None); - break; - case SyntaxKind.ExportSpecifier: - // does not use localName because the symbol name in this case refers to the name in the exports table, - // which we must exactly preserve - const specifier = - (node.parent.parent as ExportDeclaration) - .moduleSpecifier; - // targetName is only used when the target is local, as otherwise the target is an alias that points at - // another file - serializeExportSpecifier( - unescapeLeadingUnderscores(symbol.escapedName), - specifier ? verbatimTargetName : targetName, - specifier && isStringLiteralLike(specifier) - ? createLiteral(specifier.text) - : undefined - ); - break; - case SyntaxKind.ExportAssignment: - serializeMaybeAliasAssignment(symbol); - break; - case SyntaxKind.BinaryExpression: - case SyntaxKind.PropertyAccessExpression: - // Could be best encoded as though an export specifier or as though an export assignment - // If name is default or export=, do an export assignment - // Otherwise do an export specifier - if (symbol.escapedName - === InternalSymbolName.Default - || symbol.escapedName - === InternalSymbolName.ExportEquals) - { - serializeMaybeAliasAssignment(symbol); - } else { - serializeExportSpecifier( - localName, - targetName - ); - } - break; - default: - return Debug.failBadSyntaxKind( - node, - 'Unhandled alias declaration kind in symbol serializer!' - ); - } - } - - function serializeExportSpecifier( - localName: string, - targetName: string, - specifier?: Expression - ) { - addResult(createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports([createExportSpecifier( - localName !== targetName ? targetName : undefined, - localName - )]), - specifier - ), ModifierFlags.None); - } - - function serializeMaybeAliasAssignment(symbol: Symbol) { - if (symbol.flags & SymbolFlags.Prototype) { - return; - } - const name = - unescapeLeadingUnderscores(symbol.escapedName); - const isExportEquals = - name === InternalSymbolName.ExportEquals; - const isDefault = name === InternalSymbolName.Default; - const isExportAssignment = isExportEquals || isDefault; - // synthesize export = ref - // ref should refer to either be a locally scoped symbol which we need to emit, or - // a reference to another namespace/module which we may need to emit an `import` statement for - const aliasDecl = symbol.declarations - && getDeclarationOfAliasSymbol(symbol); - // serialize what the alias points to, preserve the declaration's initializer - const target = aliasDecl - && getTargetOfAliasDeclaration( - aliasDecl, /*dontRecursivelyResolve*/ - true - ); - // If the target resolves and resolves to a thing defined in this file, emit as an alias, otherwise emit as a const - if (target && length(target.declarations) - && some( - target.declarations, - d => getSourceFileOfNode(d) - === getSourceFileOfNode(enclosingDeclaration) - )) - { - // In case `target` refers to a namespace member, look at the declaration and serialize the leftmost symbol in it - // eg, `namespace A { export class B {} }; exports = A.B;` - // Technically, this is all that's required in the case where the assignment is an entity name expression - const expr = isExportAssignment - ? getExportAssignmentExpression(aliasDecl as ExportAssignment - | BinaryExpression) - : getPropertyAssignmentAliasLikeExpression(aliasDecl as ShorthandPropertyAssignment - | PropertyAssignment - | PropertyAccessExpression); - const first = isEntityNameExpression(expr) - ? getFirstNonModuleExportsIdentifier(expr) - : undefined; - const referenced = first - && resolveEntityName( - first, - SymbolFlags.All, /*ignoreErrors*/ - true, /*dontResolveAlias*/ - true, - enclosingDeclaration - ); - if (referenced || target) { - includePrivateSymbol(referenced || target); - } - - // We disable the context's symbol traker for the duration of this name serialization - // as, by virtue of being here, the name is required to print something, and we don't want to - // issue a visibility error on it. Only anonymous classes that an alias points at _would_ issue - // a visibility error here (as they're not visible within any scope), but we want to hoist them - // into the containing scope anyway, so we want to skip the visibility checks. - const oldTrack = context.tracker.trackSymbol; - context.tracker.trackSymbol = noop; - if (isExportAssignment) { - results.push(createExportAssignment( - /*decorators*/ undefined, - /*modifiers*/ undefined, - isExportEquals, - symbolToExpression( - target, - context, - SymbolFlags.All - ) - )); - } else { - if (first === expr) { - // serialize as `export {target as name}` - serializeExportSpecifier(name, idText(first)); - } else if (isClassExpression(expr)) { - serializeExportSpecifier( - name, - getInternalSymbolName( - target, - symbolName(target) - ) - ); - } else { - // serialize as `import _Ref = t.arg.et; export { _Ref as name }` - const varName = getUnusedName(name, symbol); - addResult(createImportEqualsDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createIdentifier(varName), - symbolToName( - target, - context, - SymbolFlags.All, /*expectsIdentifier*/ - false - ) - ), ModifierFlags.None); - serializeExportSpecifier(name, varName); - } - } - context.tracker.trackSymbol = oldTrack; - } else { - // serialize as an anonymous property declaration - const varName = getUnusedName(name, symbol); - // We have to use `getWidenedType` here since the object within a json file is unwidened within the file - // (Unwidened types can only exist in expression contexts and should never be serialized) - const typeToSerialize = - getWidenedType(getTypeOfSymbol(symbol)); - if (isTypeRepresentableAsFunctionNamespaceMerge( - typeToSerialize, - symbol - )) { - // If there are no index signatures and `typeToSerialize` is an object type, emit as a namespace instead of a const - serializeAsFunctionNamespaceMerge( - typeToSerialize, - symbol, - varName, - isExportAssignment - ? ModifierFlags.None - : ModifierFlags.Export - ); - } else { - const statement = createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration( - varName, - serializeTypeForDeclaration( - typeToSerialize, - symbol - ) - ) - ], NodeFlags.Const) - ); - addResult( - statement, - name === varName - ? ModifierFlags.Export - : ModifierFlags.None - ); - } - if (isExportAssignment) { - results.push(createExportAssignment( - /*decorators*/ undefined, - /*modifiers*/ undefined, - isExportEquals, - createIdentifier(varName) - )); - } else if (name !== varName) { - serializeExportSpecifier(name, varName); - } - } - } - - function isTypeRepresentableAsFunctionNamespaceMerge( - typeToSerialize: Type, - hostSymbol: Symbol - ) { - // Only object types which are not constructable, or indexable, whose members all come from the - // context source file, and whose property names are all valid identifiers and not late-bound, _and_ - // whose input is not type annotated (if the input symbol has an annotation we can reuse, we should prefer it) - const ctxSrc = - getSourceFileOfNode(context.enclosingDeclaration); - return getObjectFlags(typeToSerialize) - & (ObjectFlags.Anonymous | ObjectFlags.Mapped) - && !getIndexInfoOfType( - typeToSerialize, - IndexKind.String - ) - && !getIndexInfoOfType( - typeToSerialize, - IndexKind.Number - ) - && !!(length(getPropertiesOfType(typeToSerialize)) - || length(getSignaturesOfType( - typeToSerialize, - SignatureKind.Call - ))) - && !length(getSignaturesOfType( - typeToSerialize, - SignatureKind.Construct - )) // TODO: could probably serialize as function + ns + class, now that that's OK - && !getDeclarationWithTypeAnnotation(hostSymbol) - && !(typeToSerialize.symbol - && some( - typeToSerialize.symbol.declarations, - d => getSourceFileOfNode(d) !== ctxSrc - )) - && !some( - getPropertiesOfType(typeToSerialize), - p => isLateBoundName(p.escapedName) - ) - && !some( - getPropertiesOfType(typeToSerialize), - p => some( - p.declarations, - d => getSourceFileOfNode(d) !== ctxSrc - ) - ) - && every( - getPropertiesOfType(typeToSerialize), - p => isIdentifierText( - symbolName(p), - languageVersion - ) && !isStringAKeyword(symbolName(p)) - ); - } - - function makeSerializePropertySymbol( - createProperty: ( - decorators: readonly Decorator[] | undefined, - modifiers: readonly Modifier[] | undefined, - name: string | PropertyName, - questionOrExclamationToken: QuestionToken | undefined, - type: TypeNode | undefined, - initializer: Expression | undefined - ) => T, - methodKind: SyntaxKind, - useAccessors: true - ): (p: Symbol, isStatic: boolean, baseType: Type | undefined) - => (T | AccessorDeclaration | (T | AccessorDeclaration)[]); - function makeSerializePropertySymbol( - createProperty: ( - decorators: readonly Decorator[] | undefined, - modifiers: readonly Modifier[] | undefined, - name: string | PropertyName, - questionOrExclamationToken: QuestionToken | undefined, - type: TypeNode | undefined, - initializer: Expression | undefined - ) => T, - methodKind: SyntaxKind, - useAccessors: false - ): (p: Symbol, isStatic: boolean, baseType: Type | undefined) - => (T | T[]); - function makeSerializePropertySymbol( - createProperty: ( - decorators: readonly Decorator[] | undefined, - modifiers: readonly Modifier[] | undefined, - name: string | PropertyName, - questionOrExclamationToken: QuestionToken | undefined, - type: TypeNode | undefined, - initializer: Expression | undefined - ) => T, - methodKind: SyntaxKind, - useAccessors: boolean - ): (p: Symbol, isStatic: boolean, baseType: Type | undefined) - => (T | AccessorDeclaration | (T | AccessorDeclaration)[]) { - return function serializePropertySymbol( - p: Symbol, - isStatic: boolean, - baseType: Type | undefined - ) { - const modifierFlags = - getDeclarationModifierFlagsFromSymbol(p); - const isPrivate = - !!(modifierFlags & ModifierFlags.Private); - if (isStatic - && (p.flags - & (SymbolFlags.Type | SymbolFlags.Namespace - | SymbolFlags.Alias))) - { - // Only value-only-meaning symbols can be correctly encoded as class statics, type/namespace/alias meaning symbols - // need to be merged namespace members - return []; - } - if (p.flags & SymbolFlags.Prototype - || (baseType - && getPropertyOfType(baseType, p.escapedName) - && isReadonlySymbol(getPropertyOfType( - baseType, - p.escapedName - )!) === isReadonlySymbol(p) - && (p.flags & SymbolFlags.Optional) - === (getPropertyOfType( - baseType, - p.escapedName - )!.flags & SymbolFlags.Optional) - && isTypeIdenticalTo( - getTypeOfSymbol(p), - getTypeOfPropertyOfType( - baseType, - p.escapedName - )! - ))) - { - return []; - } - const flag = - modifierFlags - | (isStatic ? ModifierFlags.Static : 0); - const name = getPropertyNameNodeForSymbol(p, context); - const firstPropertyLikeDecl = find( - p.declarations, - or( - isPropertyDeclaration, - isAccessor, - isVariableDeclaration, - isPropertySignature, - isBinaryExpression, - isPropertyAccessExpression - ) - ); - if (p.flags & SymbolFlags.Accessor && useAccessors) { - const result: AccessorDeclaration[] = []; - if (p.flags & SymbolFlags.SetAccessor) { - result.push(setTextRange( - createSetAccessor( - /*decorators*/ undefined, - createModifiersFromModifierFlags(flag), - name, - [createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - 'arg', - /*questionToken*/ undefined, - isPrivate - ? undefined - : serializeTypeForDeclaration( - getTypeOfSymbol(p), - p - ) - )], - /*body*/ undefined - ), - find(p.declarations, isSetAccessor) - || firstPropertyLikeDecl - )); - } - if (p.flags & SymbolFlags.GetAccessor) { - const isPrivate = - modifierFlags & ModifierFlags.Private; - result.push(setTextRange( - createGetAccessor( - /*decorators*/ undefined, - createModifiersFromModifierFlags(flag), - name, - [], - isPrivate - ? undefined - : serializeTypeForDeclaration( - getTypeOfSymbol(p), - p - ), - /*body*/ undefined - ), - find(p.declarations, isGetAccessor) - || firstPropertyLikeDecl - )); - } - return result; - } // This is an else/if as accessors and properties can't merge in TS, but might in JS - // If this happens, we assume the accessor takes priority, as it imposes more constraints - else if (p.flags - & (SymbolFlags.Property | SymbolFlags.Variable)) - { - return setTextRange( - createProperty( - /*decorators*/ undefined, - createModifiersFromModifierFlags((isReadonlySymbol(p) - ? ModifierFlags.Readonly - : 0) | flag), - name, - p.flags & SymbolFlags.Optional - ? createToken(SyntaxKind.QuestionToken) - : undefined, - isPrivate - ? undefined - : serializeTypeForDeclaration( - getTypeOfSymbol(p), - p - ), - // TODO: https://github.com/microsoft/TypeScript/pull/32372#discussion_r328386357 - // interface members can't have initializers, however class members _can_ - /*initializer*/ undefined - ), - find( - p.declarations, - or( - isPropertyDeclaration, - isVariableDeclaration - ) - ) || firstPropertyLikeDecl - ); - } - if (p.flags - & (SymbolFlags.Method | SymbolFlags.Function)) - { - const type = getTypeOfSymbol(p); - const signatures = getSignaturesOfType( - type, - SignatureKind.Call - ); - if (flag & ModifierFlags.Private) { - return setTextRange( - createProperty( - /*decorators*/ undefined, - createModifiersFromModifierFlags((isReadonlySymbol(p) - ? ModifierFlags.Readonly - : 0) | flag), - name, - p.flags & SymbolFlags.Optional - ? createToken(SyntaxKind - .QuestionToken) - : undefined, - /*type*/ undefined, - /*initializer*/ undefined - ), - find( - p.declarations, - isFunctionLikeDeclaration - ) || signatures[0] - && signatures[0].declaration - || p.declarations[0] - ); - } - - const results = []; - for (const sig of signatures) { - // Each overload becomes a separate method declaration, in order - const decl = - signatureToSignatureDeclarationHelper( - sig, - methodKind, - context - ) as MethodDeclaration; - decl.name = name; // TODO: Clone - if (flag) { - decl - .modifiers = createNodeArray(createModifiersFromModifierFlags(flag)); - } - if (p.flags & SymbolFlags.Optional) { - decl - .questionToken = createToken(SyntaxKind - .QuestionToken); - } - results - .push(setTextRange(decl, sig.declaration)); - } - return results as unknown as T[]; - } - // The `Constructor`'s symbol isn't in the class's properties lists, obviously, since it's a signature on the static - return Debug - .fail(`Unhandled class member kind! ${ - (p as any).__debugFlags || p.flags}`); - }; - } - - function serializePropertySymbolForInterface( - p: Symbol, - baseType: Type | undefined - ) { - return serializePropertySymbolForInterfaceWorker( - p, /*isStatic*/ - false, - baseType - ); - } - - function getDeclarationWithTypeAnnotation(symbol: Symbol) { - return find( - symbol.declarations, - s => !!getEffectiveTypeAnnotationNode(s) - && !!findAncestor( - s, - n => n === enclosingDeclaration - ) - ); - } - - /** - * Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag - * so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym` - */ - function serializeTypeForDeclaration( - type: Type, - symbol: Symbol - ) { - const declWithExistingAnnotation = - getDeclarationWithTypeAnnotation(symbol); - if (declWithExistingAnnotation - && !isFunctionLikeDeclaration(declWithExistingAnnotation)) - { - // try to reuse the existing annotation - const existing = - getEffectiveTypeAnnotationNode(declWithExistingAnnotation)!; - const transformed = visitNode( - existing, - visitExistingNodeTreeSymbols - ); - return transformed === existing - ? getMutableClone(existing) - : transformed; - } - const oldFlags = context.flags; - if (type.flags & TypeFlags.UniqueESSymbol - && type.symbol === symbol) - { - context.flags |= NodeBuilderFlags - .AllowUniqueESSymbolType; - } - const result = typeToTypeNodeHelper(type, context); - context.flags = oldFlags; - return result; - - function visitExistingNodeTreeSymbols(node: - T): Node - { - if (isJSDocAllType(node)) { - return createKeywordTypeNode(SyntaxKind - .AnyKeyword); - } - if (isJSDocUnknownType(node)) { - return createKeywordTypeNode(SyntaxKind - .UnknownKeyword); - } - if (isJSDocNullableType(node)) { - return createUnionTypeNode([visitNode( - node.type, - visitExistingNodeTreeSymbols - ), createKeywordTypeNode(SyntaxKind.NullKeyword)]); - } - if (isJSDocOptionalType(node)) { - return createUnionTypeNode([visitNode( - node.type, - visitExistingNodeTreeSymbols - ), - createKeywordTypeNode(SyntaxKind - .UndefinedKeyword)]); - } - if (isJSDocNonNullableType(node)) { - return visitNode( - node.type, - visitExistingNodeTreeSymbols - ); - } - if ((isExpressionWithTypeArguments(node) - || isTypeReferenceNode(node)) - && isJSDocIndexSignature(node)) - { - return createTypeLiteralNode([createIndexSignature( - /*decorators*/ undefined, - /*modifiers*/ undefined, - [createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotdotdotToken*/ undefined, - 'x', - /*questionToken*/ undefined, - visitNode( - node.typeArguments![0], - visitExistingNodeTreeSymbols - ) - )], - visitNode( - node.typeArguments![1], - visitExistingNodeTreeSymbols - ) - )]); - } - if (isJSDocFunctionType(node)) { - if (isJSDocConstructSignature(node)) { - let newTypeNode: TypeNode | undefined; - return createConstructorTypeNode( - visitNodes( - node.typeParameters, - visitExistingNodeTreeSymbols - ), - mapDefined( - node.parameters, - ( - p, - i - ) => p.name && isIdentifier(p.name) - && p.name.escapedText === 'new' - ? (newTypeNode = p.type, undefined) - : createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - p.dotDotDotToken, - p.name || p.dotDotDotToken - ? `args` - : `arg${i}`, - p.questionToken, - visitNode( - p.type, - visitExistingNodeTreeSymbols - ), - /*initializer*/ undefined - ) - ), - visitNode( - newTypeNode || node.type, - visitExistingNodeTreeSymbols - ) - ); - } else { - return createFunctionTypeNode( - visitNodes( - node.typeParameters, - visitExistingNodeTreeSymbols - ), - map( - node.parameters, - (p, i) => createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - p.dotDotDotToken, - p.name || p.dotDotDotToken - ? `args` - : `arg${i}`, - p.questionToken, - visitNode( - p.type, - visitExistingNodeTreeSymbols - ), - /*initializer*/ undefined - ) - ), - visitNode( - node.type, - visitExistingNodeTreeSymbols - ) - ); - } - } - if (isLiteralImportTypeNode(node)) { - return updateImportTypeNode( - node, - updateLiteralTypeNode( - node.argument, - rewriteModuleSpecifier( - node, - node.argument.literal - ) - ), - node.qualifier, - visitNodes( - node.typeArguments, - visitExistingNodeTreeSymbols, - isTypeNode - ), - node.isTypeOf - ); - } - - if (isEntityName(node) - || isEntityNameExpression(node)) - { - const leftmost = getFirstIdentifier(node); - const sym = resolveEntityName( - leftmost, - SymbolFlags.All, /*ignoreErrors*/ - true, /*dontResolveALias*/ - true - ); - if (sym) { - includePrivateSymbol(sym); - if (isIdentifier(node) - && sym.flags & SymbolFlags.TypeParameter) - { - const name = typeParameterToName( - getDeclaredTypeOfSymbol(sym), - context - ); - if (idText(name) !== idText(node)) { - return name; - } - return node; - } - } - } - - return visitEachChild( - node, - visitExistingNodeTreeSymbols, - nullTransformationContext - ); - } - - function rewriteModuleSpecifier( - parent: ImportTypeNode, - lit: StringLiteral - ) { - if (bundled) { - if (context.tracker - && context.tracker.moduleResolverHost) - { - const targetFile = - getExternalModuleFileFromDeclaration(parent); - if (targetFile) { - const getCanonicalFileName = - createGetCanonicalFileName(!!host - .useCaseSensitiveFileNames); - const resolverHost = { - getCanonicalFileName, - getCurrentDirectory: - context.tracker.moduleResolverHost - .getCurrentDirectory - ? () => context.tracker - .moduleResolverHost! - .getCurrentDirectory!() - : () => '', - getCommonSourceDirectory: () => context - .tracker.moduleResolverHost! - .getCommonSourceDirectory() - }; - const newName = - getResolvedExternalModuleName( - resolverHost, - targetFile - ); - return createLiteral(newName); - } - } - } else { - if (context.tracker - && context.tracker - .trackExternalModuleSymbolOfImportTypeNode) - { - const moduleSym = - resolveExternalModuleNameWorker( - lit, - lit, /*moduleNotFoundError*/ - undefined - ); - if (moduleSym) { - context.tracker - .trackExternalModuleSymbolOfImportTypeNode(moduleSym); - } - } - } - return lit; - } - } - - function serializeSignatures( - kind: SignatureKind, - input: Type, - baseType: Type | undefined, - outputKind: SyntaxKind - ) { - const signatures = getSignaturesOfType(input, kind); - if (kind === SignatureKind.Construct) { - if (!baseType - && every( - signatures, - s => length(s.parameters) === 0 - )) - { - return []; // No base type, every constructor is empty - elide the extraneous `constructor()` - } - if (baseType) { - // If there is a base type, if every signature in the class is identical to a signature in the baseType, elide all the declarations - const baseSigs = getSignaturesOfType( - baseType, - SignatureKind.Construct - ); - if (!length(baseSigs) - && every( - signatures, - s => length(s.parameters) === 0 - )) - { - return []; // Base had no explicit signatures, if all our signatures are also implicit, return an empty list - } - if (baseSigs.length === signatures.length) { - let failed = false; - for (let i = 0; i < baseSigs.length; i++) { - if (!compareSignaturesIdentical( - signatures[i], - baseSigs[i], /*partialMatch*/ - false, /*ignoreThisTypes*/ - false, /*ignoreReturnTypes*/ - true, - compareTypesIdentical - )) { - failed = true; - break; - } - } - if (!failed) { - return []; // Every signature was identical - elide constructor list as it is inherited - } - } - } - let privateProtected: ModifierFlags = 0; - for (const s of signatures) { - if (s.declaration) { - privateProtected |= getSelectedModifierFlags( - s.declaration, - ModifierFlags.Private - | ModifierFlags.Protected - ); - } - } - if (privateProtected) { - return [setTextRange(createConstructor( - /*decorators*/ undefined, - createModifiersFromModifierFlags(privateProtected), - /*parameters*/ [], - /*body*/ undefined - ), signatures[0].declaration)]; - } - } - - const results = []; - for (const sig of signatures) { - // Each overload becomes a separate constructor declaration, in order - const decl = signatureToSignatureDeclarationHelper( - sig, - outputKind, - context - ); - results.push(setTextRange(decl, sig.declaration)); - } - return results; - } - - function serializeIndexSignatures( - input: Type, - baseType: Type | undefined - ) { - const results: IndexSignatureDeclaration[] = []; - for (const type of [IndexKind.String, IndexKind.Number]) { - const info = getIndexInfoOfType(input, type); - if (info) { - if (baseType) { - const baseInfo = getIndexInfoOfType( - baseType, - type - ); - if (baseInfo) { - if (isTypeIdenticalTo( - info.type, - baseInfo.type - )) { - continue; // elide identical index signatures - } - } - } - results - .push(indexInfoToIndexSignatureDeclarationHelper( - info, - type, - context - )); - } - } - return results; - } - - function serializeBaseType( - t: Type, - staticType: Type, - rootName: string - ) { - const ref = trySerializeAsTypeReference(t); - if (ref) { - return ref; - } - const tempName = getUnusedName(`${rootName}_base`); - const statement = createVariableStatement( - /*modifiers*/ undefined, - createVariableDeclarationList([ - createVariableDeclaration( - tempName, - typeToTypeNodeHelper(staticType, context) - ) - ], NodeFlags.Const) - ); - addResult(statement, ModifierFlags.None); - return createExpressionWithTypeArguments( - /*typeArgs*/ undefined, - createIdentifier(tempName) - ); - } - - function trySerializeAsTypeReference(t: Type) { - let typeArgs: TypeNode[] | undefined; - let reference: Expression | undefined; - // We don't use `isValueSymbolAccessible` below. since that considers alternative containers (like modules) - // which we can't write out in a syntactically valid way as an expression - if ((t as TypeReference).target - && getAccessibleSymbolChain( - (t as TypeReference).target.symbol, - enclosingDeclaration, - SymbolFlags.Value, /*useOnlyExternalAliasing*/ - false - )) - { - typeArgs = map( - getTypeArguments(t as TypeReference), - t => typeToTypeNodeHelper(t, context) - ); - reference = symbolToExpression( - (t as TypeReference).target.symbol, - context, - SymbolFlags.Type - ); - } else if (t.symbol - && getAccessibleSymbolChain( - t.symbol, - enclosingDeclaration, - SymbolFlags.Value, /*useOnlyExternalAliasing*/ - false - )) - { - reference = symbolToExpression( - t.symbol, - context, - SymbolFlags.Type - ); - } - if (reference) { - return createExpressionWithTypeArguments( - typeArgs, - reference - ); - } - } - - function getUnusedName( - input: string, - symbol?: Symbol - ): string { - if (symbol) { - if (context.remappedSymbolNames! - .has('' + getSymbolId(symbol))) - { - return context.remappedSymbolNames! - .get('' + getSymbolId(symbol))!; - } - } - if (symbol) { - input = getNameCandidateWorker(symbol, input); - } - let i = 0; - const original = input; - while (context.usedSymbolNames!.has(input)) { - i++; - input = `${original}_${i}`; - } - context.usedSymbolNames!.set(input, true); - if (symbol) { - context.remappedSymbolNames!.set( - '' + getSymbolId(symbol), - input - ); - } - return input; - } - - function getNameCandidateWorker( - symbol: Symbol, - localName: string - ) { - if (localName === InternalSymbolName.Default - || localName === InternalSymbolName.Class - || localName === InternalSymbolName.Function) - { - const flags = context.flags; - context.flags |= NodeBuilderFlags.InInitialEntityName; - const nameCandidate = getNameOfSymbolAsWritten( - symbol, - context - ); - context.flags = flags; - localName = nameCandidate.length > 0 - && isSingleOrDoubleQuote(nameCandidate - .charCodeAt(0)) - ? stripQuotes(nameCandidate) - : nameCandidate; - } - if (localName === InternalSymbolName.Default) { - localName = '_default'; - } else if (localName === InternalSymbolName.ExportEquals) { - localName = '_exports'; - } - localName = isIdentifierText(localName, languageVersion) - && !isStringANonContextualKeyword(localName) - ? localName - : '_' + localName.replace(/[^a-zA-Z0-9]/g, '_'); - return localName; - } - - function getInternalSymbolName( - symbol: Symbol, - localName: string - ) { - if (context.remappedSymbolNames! - .has('' + getSymbolId(symbol))) - { - return context.remappedSymbolNames! - .get('' + getSymbolId(symbol))!; - } - localName = getNameCandidateWorker(symbol, localName); - // The result of this is going to be used as the symbol's name - lock it in, so `getUnusedName` will also pick it up - context.remappedSymbolNames!.set( - '' + getSymbolId(symbol), - localName - ); - return localName; - } - } - } - - function typePredicateToString( - typePredicate: TypePredicate, - enclosingDeclaration?: Node, - flags: TypeFormatFlags = TypeFormatFlags - .UseAliasDefinedOutsideCurrentScope, - writer?: EmitTextWriter - ): string { - return writer - ? typePredicateToStringWorker(writer).getText() - : usingSingleLineStringWriter(typePredicateToStringWorker); - - function typePredicateToStringWorker(writer: EmitTextWriter) { - const predicate = createTypePredicateNodeWithModifier( - typePredicate.kind === TypePredicateKind.AssertsThis - || typePredicate.kind - === TypePredicateKind.AssertsIdentifier - ? createToken(SyntaxKind.AssertsKeyword) - : undefined, - typePredicate.kind === TypePredicateKind.Identifier - || typePredicate.kind - === TypePredicateKind.AssertsIdentifier - ? createIdentifier(typePredicate.parameterName) - : createThisTypeNode(), - typePredicate.type && nodeBuilder.typeToTypeNode( - typePredicate.type, - enclosingDeclaration, - toNodeBuilderFlags(flags) - | NodeBuilderFlags.IgnoreErrors - | NodeBuilderFlags - .WriteTypeParametersInQualifiedName - )! // TODO: GH#18217 - ); - const printer = createPrinter({ removeComments: true }); - const sourceFile = enclosingDeclaration - && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode( - EmitHint.Unspecified, - predicate, /*sourceFile*/ - sourceFile, - writer - ); - return writer; - } - } - - function formatUnionTypes(types: readonly Type[]): Type[] { - const result: Type[] = []; - let flags: TypeFlags = 0; - for (let i = 0; i < types.length; i++) { - const t = types[i]; - flags |= t.flags; - if (!(t.flags & TypeFlags.Nullable)) { - if (t.flags - & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) - { - const baseType = t.flags & TypeFlags.BooleanLiteral - ? booleanType - : getBaseTypeOfEnumLiteralType( t); - if (baseType.flags & TypeFlags.Union) { - const count = ( baseType).types.length; - if (i + count <= types.length - && getRegularTypeOfLiteralType(types[i + count - - 1]) - === getRegularTypeOfLiteralType(( baseType) - .types[count - 1])) - { - result.push(baseType); - i += count - 1; - continue; - } - } - } - result.push(t); - } - } - if (flags & TypeFlags.Null) result.push(nullType); - if (flags & TypeFlags.Undefined) result.push(undefinedType); - return result || types; - } - - function visibilityToString(flags: ModifierFlags): string | undefined { - if (flags === ModifierFlags.Private) { - return 'private'; - } - if (flags === ModifierFlags.Protected) { - return 'protected'; - } - return 'public'; - } - - function getTypeAliasForTypeLiteral(type: Type): Symbol | undefined { - if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) { - const node = findAncestor( - type.symbol.declarations[0].parent, - n => n.kind !== SyntaxKind.ParenthesizedType - )!; - if (node.kind === SyntaxKind.TypeAliasDeclaration) { - return getSymbolOfNode(node); - } - } - return undefined; - } - - function isTopLevelInExternalModuleAugmentation(node: Node): boolean { - return node && node.parent - && node.parent.kind === SyntaxKind.ModuleBlock - && isExternalModuleAugmentation(node.parent.parent); - } - - interface NodeBuilderContext { - enclosingDeclaration: Node | undefined; - flags: NodeBuilderFlags; - tracker: SymbolTracker; - - // State - encounteredError: boolean; - visitedTypes: Map | undefined; - symbolDepth: Map | undefined; - inferTypeParameters: TypeParameter[] | undefined; - approximateLength: number; - truncating?: boolean; - typeParameterSymbolList?: Map; - typeParameterNames?: Map; - typeParameterNamesByText?: Map; - usedSymbolNames?: Map; - remappedSymbolNames?: Map; - } - - function isDefaultBindingContext(location: Node) { - return location.kind === SyntaxKind.SourceFile - || isAmbientModule(location); - } - - function getNameOfSymbolFromNameType( - symbol: Symbol, - context?: NodeBuilderContext - ) { - const nameType = symbol.nameType; - if (nameType) { - if (nameType.flags & TypeFlags.StringOrNumberLiteral) { - const name = '' - + ( nameType) - .value; - if (!isIdentifierText(name, compilerOptions.target) - && !isNumericLiteralName(name)) - { - return `"${escapeString( - name, - CharacterCodes.doubleQuote - )}"`; - } - if (isNumericLiteralName(name) && startsWith(name, '-')) { - return `[${name}]`; - } - return name; - } - if (nameType.flags & TypeFlags.UniqueESSymbol) { - return `[${getNameOfSymbolAsWritten( - ( nameType).symbol, - context - )}]`; - } - } - } - - /** - * Gets a human-readable name for a symbol. - * Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. - * - * Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. - * It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. - */ - function getNameOfSymbolAsWritten( - symbol: Symbol, - context?: NodeBuilderContext - ): string { - if (context && symbol.escapedName === InternalSymbolName.Default - && !(context.flags - & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope) - // If it's not the first part of an entity name, it must print as `default` - && (!(context.flags & NodeBuilderFlags.InInitialEntityName) - // if the symbol is synthesized, it will only be referenced externally it must print as `default` - || !symbol.declarations - // if not in the same binding context (source file, module declaration), it must print as `default` - || (context.enclosingDeclaration - && findAncestor( - symbol.declarations[0], - isDefaultBindingContext - ) - !== findAncestor( - context.enclosingDeclaration, - isDefaultBindingContext - )))) - { - return 'default'; - } - if (symbol.declarations && symbol.declarations.length) { - let declaration = firstDefined( - symbol.declarations, - d => getNameOfDeclaration(d) ? d : undefined - ); // Try using a declaration with a name, first - const name = declaration && getNameOfDeclaration(declaration); - if (declaration && name) { - if (isCallExpression(declaration) - && isBindableObjectDefinePropertyCall(declaration)) - { - return symbolName(symbol); - } - if (isComputedPropertyName(name) - && !(getCheckFlags(symbol) & CheckFlags.Late) - && symbol.nameType - && symbol.nameType.flags - & TypeFlags.StringOrNumberLiteral) - { - // Computed property name isn't late bound, but has a well-known name type - use name type to generate a symbol name - const result = getNameOfSymbolFromNameType( - symbol, - context - ); - if (result !== undefined) { - return result; - } - } - return declarationNameToString(name); - } - if (!declaration) { - declaration = symbol.declarations - [0]; // Declaration may be nameless, but we'll try anyway - } - if (declaration.parent - && declaration.parent.kind - === SyntaxKind.VariableDeclaration) - { - return declarationNameToString(( declaration - .parent).name); - } - switch (declaration.kind) { - case SyntaxKind.ClassExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - if (context && !context.encounteredError - && !(context.flags - & NodeBuilderFlags.AllowAnonymousIdentifier)) - { - context.encounteredError = true; - } - return declaration.kind === SyntaxKind.ClassExpression - ? '(Anonymous class)' - : '(Anonymous function)'; - } - } - const name = getNameOfSymbolFromNameType(symbol, context); - return name !== undefined ? name : symbolName(symbol); - } - - function isDeclarationVisible(node: Node): boolean { - if (node) { - const links = getNodeLinks(node); - if (links.isVisible === undefined) { - links.isVisible = !!determineIfDeclarationIsVisible(); - } - return links.isVisible; - } - - return false; - - function determineIfDeclarationIsVisible() { - switch (node.kind) { - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocEnumTag: - // Top-level jsdoc type aliases are considered exported - // First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file - return !!(node.parent && node.parent.parent - && node.parent.parent.parent - && isSourceFile(node.parent.parent.parent)); - case SyntaxKind.BindingElement: - return isDeclarationVisible(node.parent.parent); - case SyntaxKind.VariableDeclaration: - if (isBindingPattern((node as VariableDeclaration) - .name) - && !((node as VariableDeclaration) - .name as BindingPattern).elements.length) - { - // If the binding pattern is empty, this variable declaration is not visible - return false; - } - // falls through - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - // external module augmentation is always visible - if (isExternalModuleAugmentation(node)) { - return true; - } - const parent = getDeclarationContainer(node); - // If the node is not exported or it is not ambient module element (except import declaration) - if (!(getCombinedModifierFlags(node as Declaration) - & ModifierFlags.Export) - && !(node.kind - !== SyntaxKind.ImportEqualsDeclaration - && parent.kind !== SyntaxKind.SourceFile - && parent.flags & NodeFlags.Ambient)) - { - return isGlobalSourceFile(parent); - } - // Exported members/ambient module elements (exception import declaration) are visible if parent is visible - return isDeclarationVisible(parent); - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - if (hasModifier( - node, - ModifierFlags.Private | ModifierFlags.Protected - )) { - // Private/protected properties/methods are not visible - return false; - } - // Public properties/methods are visible if its parents are visible, so: - // falls through - case SyntaxKind.Constructor: - case SyntaxKind.ConstructSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.Parameter: - case SyntaxKind.ModuleBlock: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.TypeReference: - case SyntaxKind.ArrayType: - case SyntaxKind.TupleType: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - case SyntaxKind.ParenthesizedType: - return isDeclarationVisible(node.parent); - - // Default binding, import specifier and namespace import is visible - // only on demand so by default it is not visible - - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportSpecifier: - return false; - - // Type parameters are always visible - - case SyntaxKind.TypeParameter: - - // Source file and namespace export are always visible - // falls through - case SyntaxKind.SourceFile: - case SyntaxKind.NamespaceExportDeclaration: - return true; - - // Export assignments do not create name bindings outside the module - - case SyntaxKind.ExportAssignment: - return false; - - default: - return false; - } - } - } - - function collectLinkedAliases( - node: Identifier, - setVisibility?: boolean - ): Node[] | undefined { - let exportSymbol: Symbol | undefined; - if (node.parent - && node.parent.kind === SyntaxKind.ExportAssignment) - { - exportSymbol = resolveName( - node, - node.escapedText, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace - | SymbolFlags.Alias, /*nameNotFoundMessage*/ - undefined, - node, /*isUse*/ - false - ); - } else if (node.parent.kind === SyntaxKind.ExportSpecifier) { - exportSymbol = getTargetOfExportSpecifier( - node.parent, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace | SymbolFlags.Alias - ); - } - let result: Node[] | undefined; - let visited: Map | undefined; - if (exportSymbol) { - visited = createMap(); - visited.set('' + getSymbolId(exportSymbol), true); - buildVisibleNodeList(exportSymbol.declarations); - } - return result; - - function buildVisibleNodeList(declarations: Declaration[]) { - forEach(declarations, declaration => { - const resultNode = getAnyImportSyntax(declaration) - || declaration; - if (setVisibility) { - getNodeLinks(declaration).isVisible = true; - } else { - result = result || []; - pushIfUnique(result, resultNode); - } - - if (isInternalModuleImportEqualsDeclaration(declaration)) { - // Add the referenced top container visible - const internalModuleReference = declaration.moduleReference; - const firstIdentifier = - getFirstIdentifier(internalModuleReference); - const importSymbol = resolveName( - declaration, - firstIdentifier.escapedText, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace, - undefined, - undefined, /*isUse*/ - false - ); - const id = importSymbol && '' - + getSymbolId(importSymbol); - if (importSymbol && !visited!.has(id!)) { - visited!.set(id!, true); - buildVisibleNodeList(importSymbol.declarations); - } - } - }); - } - } - - /** - * Push an entry on the type resolution stack. If an entry with the given target and the given property name - * is already on the stack, and no entries in between already have a type, then a circularity has occurred. - * In this case, the result values of the existing entry and all entries pushed after it are changed to false, - * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. - * In order to see if the same query has already been done before, the target object and the propertyName both - * must match the one passed in. - * - * @param target The symbol, type, or signature whose type is being queried - * @param propertyName The property name that should be used to query the target for its type - */ - function pushTypeResolution( - target: TypeSystemEntity, - propertyName: TypeSystemPropertyName - ): boolean { - const resolutionCycleStartIndex = findResolutionCycleStartIndex( - target, - propertyName - ); - if (resolutionCycleStartIndex >= 0) { - // A cycle was found - const { length } = resolutionTargets; - for (let i = resolutionCycleStartIndex; i < length; i++) { - resolutionResults[i] = false; - } - return false; - } - resolutionTargets.push(target); - resolutionResults.push(/*items*/ true); - resolutionPropertyNames.push(propertyName); - return true; - } - - function findResolutionCycleStartIndex( - target: TypeSystemEntity, - propertyName: TypeSystemPropertyName - ): number { - for (let i = resolutionTargets.length - 1; i >= 0; i--) { - if (hasType( - resolutionTargets[i], - resolutionPropertyNames[i] - )) { - return -1; - } - if (resolutionTargets[i] === target - && resolutionPropertyNames[i] === propertyName) - { - return i; - } - } - return -1; - } - - function hasType( - target: TypeSystemEntity, - propertyName: TypeSystemPropertyName - ): boolean { - switch (propertyName) { - case TypeSystemPropertyName.Type: - return !!getSymbolLinks( target).type; - case TypeSystemPropertyName.EnumTagType: - return !!(getNodeLinks(target as JSDocEnumTag) - .resolvedEnumType); - case TypeSystemPropertyName.DeclaredType: - return !!getSymbolLinks( target).declaredType; - case TypeSystemPropertyName.ResolvedBaseConstructorType: - return !!( target) - .resolvedBaseConstructorType; - case TypeSystemPropertyName.ResolvedReturnType: - return !!( target).resolvedReturnType; - case TypeSystemPropertyName.ImmediateBaseConstraint: - return !!( target).immediateBaseConstraint; - case TypeSystemPropertyName.JSDocTypeReference: - return !!getSymbolLinks(target as Symbol) - .resolvedJSDocType; - case TypeSystemPropertyName.ResolvedTypeArguments: - return !!(target as TypeReference).resolvedTypeArguments; - } - return Debug.assertNever(propertyName); - } - - /** - * Pop an entry from the type resolution stack and return its associated result value. The result value will - * be true if no circularities were detected, or false if a circularity was found. - */ - function popTypeResolution(): boolean { - resolutionTargets.pop(); - resolutionPropertyNames.pop(); - return resolutionResults.pop()!; - } - - function getDeclarationContainer(node: Node): Node { - return findAncestor(getRootDeclaration(node), node => { - switch (node.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.VariableDeclarationList: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.NamedImports: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportClause: - return false; - default: - return true; - } - })!.parent; - } - - function getTypeOfPrototypeProperty(prototype: Symbol): Type { - // TypeScript 1.0 spec (April 2014): 8.4 - // Every class automatically contains a static property member named 'prototype', - // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. - // It is an error to explicitly declare a static property member with the name 'prototype'. - const classType = - getDeclaredTypeOfSymbol(getParentOfSymbol(prototype)!); - return classType.typeParameters - ? createTypeReference( - classType, - map(classType.typeParameters, _ => anyType) - ) - : classType; - } - - // Return the type of the given property in the given type, or undefined if no such property exists - function getTypeOfPropertyOfType(type: Type, name: __String): Type - | undefined - { - const prop = getPropertyOfType(type, name); - return prop ? getTypeOfSymbol(prop) : undefined; - } - - function getTypeOfPropertyOrIndexSignature( - type: Type, - name: __String - ): Type { - return getTypeOfPropertyOfType(type, name) - || isNumericLiteralName(name) - && getIndexTypeOfType(type, IndexKind.Number) - || getIndexTypeOfType(type, IndexKind.String) || unknownType; - } - - function isTypeAny(type: Type | undefined) { - return type && (type.flags & TypeFlags.Any) !== 0; - } - - // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been - // assigned by contextual typing. - function getTypeForBindingElementParent(node: - BindingElementGrandparent) - { - const symbol = getSymbolOfNode(node); - return symbol && getSymbolLinks(symbol).type - || getTypeForVariableLikeDeclaration( - node, /*includeOptionality*/ - false - ); - } - - function isComputedNonLiteralName(name: PropertyName): boolean { - return name.kind === SyntaxKind.ComputedPropertyName - && !isStringOrNumericLiteralLike(name.expression); - } - - function getRestType( - source: Type, - properties: PropertyName[], - symbol: Symbol | undefined - ): Type { - source = filterType(source, t => !(t.flags & TypeFlags.Nullable)); - if (source.flags & TypeFlags.Never) { - return emptyObjectType; - } - if (source.flags & TypeFlags.Union) { - return mapType( - source, - t => getRestType(t, properties, symbol) - ); - } - const omitKeyType = - getUnionType(map(properties, getLiteralTypeFromPropertyName)); - if (isGenericObjectType(source) - || isGenericIndexType(omitKeyType)) - { - if (omitKeyType.flags & TypeFlags.Never) { - return source; - } - - const omitTypeAlias = getGlobalOmitSymbol(); - if (!omitTypeAlias) { - return errorType; - } - return getTypeAliasInstantiation( - omitTypeAlias, - [source, omitKeyType] - ); - } - const members = createSymbolTable(); - for (const prop of getPropertiesOfType(source)) { - if (!isTypeAssignableTo( - getLiteralTypeFromProperty( - prop, - TypeFlags.StringOrNumberLiteralOrUnique - ), - omitKeyType - ) - && !(getDeclarationModifierFlagsFromSymbol(prop) - & (ModifierFlags.Private | ModifierFlags.Protected)) - && isSpreadableProperty(prop)) - { - members.set( - prop.escapedName, - getSpreadSymbol(prop, /*readonly*/ false) - ); - } - } - const stringIndexInfo = getIndexInfoOfType( - source, - IndexKind.String - ); - const numberIndexInfo = getIndexInfoOfType( - source, - IndexKind.Number - ); - const result = createAnonymousType( - symbol, - members, - emptyArray, - emptyArray, - stringIndexInfo, - numberIndexInfo - ); - result.objectFlags |= ObjectFlags.ObjectRestType; - return result; - } - - // Determine the control flow type associated with a destructuring declaration or assignment. The following - // forms of destructuring are possible: - // let { x } = obj; // BindingElement - // let [ x ] = obj; // BindingElement - // { x } = obj; // ShorthandPropertyAssignment - // { x: v } = obj; // PropertyAssignment - // [ x ] = obj; // Expression - // We construct a synthetic element access expression corresponding to 'obj.x' such that the control - // flow analyzer doesn't have to handle all the different syntactic forms. - function getFlowTypeOfDestructuring( - node: BindingElement | PropertyAssignment - | ShorthandPropertyAssignment | Expression, - declaredType: Type - ) { - const reference = getSyntheticElementAccess(node); - return reference - ? getFlowTypeOfReference(reference, declaredType) - : declaredType; - } - - function getSyntheticElementAccess(node: BindingElement - | PropertyAssignment | ShorthandPropertyAssignment - | Expression): ElementAccessExpression | undefined - { - const parentAccess = getParentElementAccess(node); - if (parentAccess && parentAccess.flowNode) { - const propName = getDestructuringPropertyName(node); - if (propName) { - const result = createNode( - SyntaxKind.ElementAccessExpression, - node.pos, - node.end - ); - result.parent = node; - result.expression = parentAccess; - const literal = createNode( - SyntaxKind.StringLiteral, - node.pos, - node.end - ); - literal.parent = result; - literal.text = propName; - result.argumentExpression = literal; - result.flowNode = parentAccess.flowNode; - return result; - } - } - } - - function getParentElementAccess(node: BindingElement - | PropertyAssignment | ShorthandPropertyAssignment | Expression) - { - const ancestor = node.parent.parent; - switch (ancestor.kind) { - case SyntaxKind.BindingElement: - case SyntaxKind.PropertyAssignment: - return getSyntheticElementAccess( ancestor); - case SyntaxKind.ArrayLiteralExpression: - return getSyntheticElementAccess( node.parent); - case SyntaxKind.VariableDeclaration: - return ( ancestor).initializer; - case SyntaxKind.BinaryExpression: - return ( ancestor).right; - } - } - - function getDestructuringPropertyName(node: BindingElement - | PropertyAssignment | ShorthandPropertyAssignment | Expression) - { - const parent = node.parent; - if (node.kind === SyntaxKind.BindingElement - && parent.kind === SyntaxKind.ObjectBindingPattern) - { - return getLiteralPropertyNameText(( node) - .propertyName - || ( node).name); - } - if (node.kind === SyntaxKind.PropertyAssignment - || node.kind === SyntaxKind.ShorthandPropertyAssignment) - { - return getLiteralPropertyNameText(( node).name); - } - return '' - + (> ( parent).elements).indexOf(node); - } - - function getLiteralPropertyNameText(name: PropertyName) { - const type = getLiteralTypeFromPropertyName(name); - return type.flags - & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral) - ? '' + ( type).value - : undefined; - } - - /** Return the inferred type for a binding element */ - function getTypeForBindingElement(declaration: BindingElement): Type - | undefined - { - const pattern = declaration.parent; - let parentType = getTypeForBindingElementParent(pattern.parent); - // If no type or an any type was inferred for parent, infer that for the binding element - if (!parentType || isTypeAny(parentType)) { - return parentType; - } - // Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation - if (strictNullChecks && declaration.flags & NodeFlags.Ambient - && isParameterDeclaration(declaration)) - { - parentType = getNonNullableType(parentType); - } - - let type: Type | undefined; - if (pattern.kind === SyntaxKind.ObjectBindingPattern) { - if (declaration.dotDotDotToken) { - if (parentType.flags & TypeFlags.Unknown - || !isValidSpreadType(parentType)) - { - error( - declaration, - Diagnostics - .Rest_types_may_only_be_created_from_object_types - ); - return errorType; - } - const literalMembers: PropertyName[] = []; - for (const element of pattern.elements) { - if (!element.dotDotDotToken) { - literalMembers - .push(element.propertyName - || element.name as Identifier); - } - } - type = getRestType( - parentType, - literalMembers, - declaration.symbol - ); - } else { - // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) - const name = declaration.propertyName - || declaration.name; - const indexType = getLiteralTypeFromPropertyName(name); - const declaredType = getConstraintForLocation( - getIndexedAccessType(parentType, indexType, name), - declaration.name - ); - type = getFlowTypeOfDestructuring( - declaration, - declaredType - ); - } - } else { - // This elementType will be used if the specific property corresponding to this index is not - // present (aka the tuple element property). This call also checks that the parentType is in - // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType( - IterationUse.Destructuring, - parentType, - undefinedType, - pattern - ); - const index = pattern.elements.indexOf(declaration); - if (declaration.dotDotDotToken) { - // If the parent is a tuple type, the rest element has a tuple type of the - // remaining tuple element types. Otherwise, the rest element has an array type with same - // element type as the parent type. - type = everyType(parentType, isTupleType) - ? mapType( - parentType, - t => sliceTupleType( t, index) - ) - : createArrayType(elementType); - } else if (isArrayLikeType(parentType)) { - const indexType = getLiteralType(index); - const accessFlags = hasDefaultValue(declaration) - ? AccessFlags.NoTupleBoundsCheck - : 0; - const declaredType = getConstraintForLocation( - getIndexedAccessTypeOrUndefined( - parentType, - indexType, - declaration.name, - accessFlags - ) || errorType, - declaration.name - ); - type = getFlowTypeOfDestructuring( - declaration, - declaredType - ); - } else { - type = elementType; - } - } - if (!declaration.initializer) { - return type; - } - if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - return strictNullChecks - && !(getFalsyFlags(checkDeclarationInitializer(declaration)) - & TypeFlags.Undefined) - ? getTypeWithFacts(type, TypeFacts.NEUndefined) - : type; - } - return getUnionType( - [getTypeWithFacts(type, TypeFacts.NEUndefined), - checkDeclarationInitializer(declaration)], - UnionReduction.Subtype - ); - } - - function getTypeForDeclarationFromJSDocComment(declaration: Node) { - const jsdocType = getJSDocType(declaration); - if (jsdocType) { - return getTypeFromTypeNode(jsdocType); - } - return undefined; - } - - function isNullOrUndefined(node: Expression) { - const expr = skipParentheses(node); - return expr.kind === SyntaxKind.NullKeyword - || expr.kind === SyntaxKind.Identifier - && getResolvedSymbol( expr) === undefinedSymbol; - } - - function isEmptyArrayLiteral(node: Expression) { - const expr = skipParentheses(node); - return expr.kind === SyntaxKind.ArrayLiteralExpression - && ( expr).elements.length === 0; - } - - function addOptionality(type: Type, optional = true): Type { - return strictNullChecks && optional ? getOptionalType(type) : type; - } - - function isParameterOfContextuallyTypedFunction(node: Declaration) { - return node.kind === SyntaxKind.Parameter - && (node.parent.kind === SyntaxKind.FunctionExpression - || node.parent.kind === SyntaxKind.ArrowFunction) - && !!getContextualType( node.parent); - } - - // Return the inferred type for a variable, parameter, or property declaration - function getTypeForVariableLikeDeclaration( - declaration: ParameterDeclaration | PropertyDeclaration - | PropertySignature | VariableDeclaration | BindingElement, - includeOptionality: boolean - ): Type | undefined { - // A variable declared in a for..in statement is of type string, or of type keyof T when the - // right hand expression is of a type parameter type. - if (isVariableDeclaration(declaration) - && declaration.parent.parent.kind - === SyntaxKind.ForInStatement) - { - const indexType = - getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration - .parent.parent.expression))); - return indexType.flags - & (TypeFlags.TypeParameter | TypeFlags.Index) - ? getExtractStringType(indexType) - : stringType; - } - - if (isVariableDeclaration(declaration) - && declaration.parent.parent.kind - === SyntaxKind.ForOfStatement) - { - // checkRightHandSideOfForOf will return undefined if the for-of expression type was - // missing properties/signatures required to get its iteratedType (like - // [Symbol.iterator] or next). This may be because we accessed properties from anyType, - // or it may have led to an error inside getElementTypeOfIterable. - const forOfStatement = declaration.parent.parent; - return checkRightHandSideOfForOf( - forOfStatement.expression, - forOfStatement.awaitModifier - ) || anyType; - } - - if (isBindingPattern(declaration.parent)) { - return getTypeForBindingElement( declaration); - } - - const isOptional = includeOptionality && ( - isParameter(declaration) - && isJSDocOptionalParameter(declaration) - || !isBindingElement(declaration) - && !isVariableDeclaration(declaration) - && !!declaration.questionToken - ); - - // Use type from type annotation if one is present - const declaredType = tryGetTypeFromEffectiveTypeNode(declaration); - if (declaredType) { - return addOptionality(declaredType, isOptional); - } - - if ((noImplicitAny || isInJSFile(declaration)) - && declaration.kind === SyntaxKind.VariableDeclaration - && !isBindingPattern(declaration.name) - && !(getCombinedModifierFlags(declaration) - & ModifierFlags.Export) - && !(declaration.flags & NodeFlags.Ambient)) - { - // If --noImplicitAny is on or the declaration is in a Javascript file, - // use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no - // initializer or a 'null' or 'undefined' initializer. - if (!(getCombinedNodeFlags(declaration) & NodeFlags.Const) - && (!declaration.initializer - || isNullOrUndefined(declaration.initializer))) - { - return autoType; - } - // Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array - // literal initializer. - if (declaration.initializer - && isEmptyArrayLiteral(declaration.initializer)) - { - return autoArrayType; - } - } - - if (declaration.kind === SyntaxKind.Parameter) { - const func = declaration.parent; - // For a parameter of a set accessor, use the type of the get accessor if one is present - if (func.kind === SyntaxKind.SetAccessor - && !hasNonBindableDynamicName(func)) - { - const getter = getDeclarationOfKind( - getSymbolOfNode(declaration.parent), - SyntaxKind.GetAccessor - ); - if (getter) { - const getterSignature = - getSignatureFromDeclaration(getter); - const thisParameter = - getAccessorThisParameter(func as AccessorDeclaration); - if (thisParameter && declaration === thisParameter) { - // Use the type from the *getter* - Debug.assert(!thisParameter.type); - return getTypeOfSymbol(getterSignature - .thisParameter!); - } - return getReturnTypeOfSignature(getterSignature); - } - } - if (isInJSFile(declaration)) { - const typeTag = getJSDocType(func); - if (typeTag && isFunctionTypeNode(typeTag)) { - return getTypeAtPosition( - getSignatureFromDeclaration(typeTag), - func.parameters.indexOf(declaration) - ); - } - } - // Use contextual parameter type if one is available - const type = - declaration.symbol.escapedName === InternalSymbolName.This - ? getContextualThisParameterType(func) - : getContextuallyTypedParameterType( - declaration, /*forCache*/ - true - ); - if (type) { - return addOptionality(type, isOptional); - } - } else if (isInJSFile(declaration)) { - const containerObjectType = getJSContainerObjectType( - declaration, - getSymbolOfNode(declaration), - getDeclaredExpandoInitializer(declaration) - ); - if (containerObjectType) { - return containerObjectType; - } - } - - // Use the type of the initializer expression if one is present and the declaration is - // not a parameter of a contextually typed function - if (declaration.initializer - && !isParameterOfContextuallyTypedFunction(declaration)) - { - const type = checkDeclarationInitializer(declaration); - return addOptionality(type, isOptional); - } - - if (isJsxAttribute(declaration)) { - // if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true. - // I.e is sugar for - return trueType; - } - - // If the declaration specifies a binding pattern and is not a parameter of a contextually - // typed function, use the type implied by the binding pattern - if (isBindingPattern(declaration.name) - && !isParameterOfContextuallyTypedFunction(declaration)) - { - return getTypeFromBindingPattern( - declaration.name, /*includePatternInType*/ - false, /*reportErrors*/ - true - ); - } - - // No type specified and nothing can be inferred - return undefined; - } - - function getWidenedTypeForAssignmentDeclaration( - symbol: Symbol, - resolvedSymbol?: Symbol - ) { - // function/class/{} initializers are themselves containers, so they won't merge in the same way as other initializers - const container = - getAssignedExpandoInitializer(symbol.valueDeclaration); - if (container) { - const tag = getJSDocTypeTag(container); - if (tag && tag.typeExpression) { - return getTypeFromTypeNode(tag.typeExpression); - } - const containerObjectType = getJSContainerObjectType( - symbol.valueDeclaration, - symbol, - container - ); - return containerObjectType - || getWidenedLiteralType(checkExpressionCached(container)); - } - let definedInConstructor = false; - let definedInMethod = false; - let jsdocType: Type | undefined; - let types: Type[] | undefined; - for (const declaration of symbol.declarations) { - const expression = - (isBinaryExpression(declaration) - || isCallExpression(declaration)) - ? declaration - : isAccessExpression(declaration) - ? isBinaryExpression(declaration.parent) - ? declaration.parent - : declaration - : undefined; - if (!expression) { - continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere - } - - const kind = isAccessExpression(expression) - ? getAssignmentDeclarationPropertyAccessKind(expression) - : getAssignmentDeclarationKind(expression); - if (kind === AssignmentDeclarationKind.ThisProperty) { - if (isDeclarationInConstructor(expression)) { - definedInConstructor = true; - } else { - definedInMethod = true; - } - } - if (!isCallExpression(expression)) { - jsdocType = getAnnotatedTypeForAssignmentDeclaration( - jsdocType, - expression, - symbol, - declaration - ); - } - if (!jsdocType) { - (types || (types = [])) - .push((isBinaryExpression(expression) - || isCallExpression(expression)) - ? getInitializerTypeFromAssignmentDeclaration( - symbol, - resolvedSymbol, - expression, - kind - ) - : neverType); - } - } - let type = jsdocType; - if (!type) { - if (!length(types)) { - return errorType; // No types from any declarations :( - } - let constructorTypes = definedInConstructor - ? getConstructorDefinedThisAssignmentTypes( - types!, - symbol.declarations - ) - : undefined; - // use only the constructor types unless they were only assigned null | undefined (including widening variants) - if (definedInMethod) { - const propType = - getTypeOfAssignmentDeclarationPropertyOfBaseType(symbol); - if (propType) { - (constructorTypes || (constructorTypes = [])) - .push(propType); - definedInConstructor = true; - } - } - const sourceTypes = - some( - constructorTypes, - t => !!(t.flags & ~TypeFlags.Nullable) - ) - ? constructorTypes - : types; // TODO: GH#18217 - type = getUnionType(sourceTypes!, UnionReduction.Subtype); - } - const widened = - getWidenedType(addOptionality( - type, - definedInMethod && !definedInConstructor - )); - if (filterType(widened, t => !!(t.flags & ~TypeFlags.Nullable)) - === neverType) - { - reportImplicitAny(symbol.valueDeclaration, anyType); - return anyType; - } - return widened; - } - - function getJSContainerObjectType( - decl: Node, - symbol: Symbol, - init: Expression | undefined - ): Type | undefined { - if (!isInJSFile(decl) || !init || !isObjectLiteralExpression(init) - || init.properties.length) - { - return undefined; - } - const exports = createSymbolTable(); - while (isBinaryExpression(decl) - || isPropertyAccessExpression(decl)) - { - const s = getSymbolOfNode(decl); - if (s && hasEntries(s.exports)) { - mergeSymbolTable(exports, s.exports); - } - decl = isBinaryExpression(decl) - ? decl.parent - : decl.parent.parent; - } - const s = getSymbolOfNode(decl); - if (s && hasEntries(s.exports)) { - mergeSymbolTable(exports, s.exports); - } - const type = createAnonymousType( - symbol, - exports, - emptyArray, - emptyArray, - undefined, - undefined - ); - type.objectFlags |= ObjectFlags.JSLiteral; - return type; - } - - function getAnnotatedTypeForAssignmentDeclaration( - declaredType: Type | undefined, - expression: Expression, - symbol: Symbol, - declaration: Declaration - ) { - const typeNode = getEffectiveTypeAnnotationNode(expression.parent); - if (typeNode) { - const type = getWidenedType(getTypeFromTypeNode(typeNode)); - if (!declaredType) { - return type; - } else if (declaredType !== errorType && type !== errorType - && !isTypeIdenticalTo(declaredType, type)) - { - errorNextVariableOrPropertyDeclarationMustHaveSameType( - /*firstDeclaration*/ undefined, - declaredType, - declaration, - type - ); - } - } - if (symbol.parent) { - const typeNode = - getEffectiveTypeAnnotationNode(symbol.parent - .valueDeclaration); - if (typeNode) { - return getTypeOfPropertyOfType( - getTypeFromTypeNode(typeNode), - symbol.escapedName - ); - } - } - - return declaredType; - } - - /** If we don't have an explicit JSDoc type, get the type from the initializer. */ - function getInitializerTypeFromAssignmentDeclaration( - symbol: Symbol, - resolvedSymbol: Symbol | undefined, - expression: BinaryExpression | CallExpression, - kind: AssignmentDeclarationKind - ) { - if (isCallExpression(expression)) { - if (resolvedSymbol) { - return getTypeOfSymbol(resolvedSymbol); // This shouldn't happen except under some hopefully forbidden merges of export assignments and object define assignments - } - const objectLitType = - checkExpressionCached((expression as BindableObjectDefinePropertyCall) - .arguments[2]); - const valueType = getTypeOfPropertyOfType( - objectLitType, - 'value' as __String - ); - if (valueType) { - return valueType; - } - const getFunc = getTypeOfPropertyOfType( - objectLitType, - 'get' as __String - ); - if (getFunc) { - const getSig = getSingleCallSignature(getFunc); - if (getSig) { - return getReturnTypeOfSignature(getSig); - } - } - const setFunc = getTypeOfPropertyOfType( - objectLitType, - 'set' as __String - ); - if (setFunc) { - const setSig = getSingleCallSignature(setFunc); - if (setSig) { - return getTypeOfFirstParameterOfSignature(setSig); - } - } - return anyType; - } - const type = resolvedSymbol - ? getTypeOfSymbol(resolvedSymbol) - : getWidenedLiteralType(checkExpressionCached(expression - .right)); - if (type.flags & TypeFlags.Object - && kind === AssignmentDeclarationKind.ModuleExports - && symbol.escapedName === InternalSymbolName.ExportEquals) - { - const exportedType = - resolveStructuredTypeMembers(type as ObjectType); - const members = createSymbolTable(); - copyEntries(exportedType.members, members); - if (resolvedSymbol && !resolvedSymbol.exports) { - resolvedSymbol.exports = createSymbolTable(); - } - (resolvedSymbol || symbol).exports!.forEach((s, name) => { - if (members.has(name)) { - const exportedMember = exportedType.members.get(name)!; - const union = createSymbol( - s.flags | exportedMember.flags, - name - ); - union - .type = getUnionType([getTypeOfSymbol(s), - getTypeOfSymbol(exportedMember)]); - members.set(name, union); - } else { - members.set(name, s); - } - }); - const result = createAnonymousType( - exportedType.symbol, - members, - exportedType.callSignatures, - exportedType.constructSignatures, - exportedType.stringIndexInfo, - exportedType.numberIndexInfo - ); - result - .objectFlags |= (getObjectFlags(type) - & ObjectFlags.JSLiteral); // Propagate JSLiteral flag - return result; - } - if (isEmptyArrayLiteralType(type)) { - reportImplicitAny(expression, anyArrayType); - return anyArrayType; - } - return type; - } - - function isDeclarationInConstructor(expression: Expression) { - const thisContainer = getThisContainer( - expression, /*includeArrowFunctions*/ - false - ); - // Properties defined in a constructor (or base constructor, or javascript constructor function) don't get undefined added. - // Function expressions that are assigned to the prototype count as methods. - return thisContainer.kind === SyntaxKind.Constructor - || thisContainer.kind === SyntaxKind.FunctionDeclaration - || (thisContainer.kind === SyntaxKind.FunctionExpression - && !isPrototypePropertyAssignment(thisContainer.parent)); - } - - function getConstructorDefinedThisAssignmentTypes( - types: Type[], - declarations: Declaration[] - ): Type[] | undefined { - Debug.assert(types.length === declarations.length); - return types.filter((_, i) => { - const declaration = declarations[i]; - const expression = isBinaryExpression(declaration) - ? declaration - : isBinaryExpression(declaration.parent) - ? declaration.parent - : undefined; - return expression && isDeclarationInConstructor(expression); - }); - } - - /** check for definition in base class if any declaration is in a class */ - function getTypeOfAssignmentDeclarationPropertyOfBaseType(property: - Symbol) - { - const parentDeclaration = forEach(property.declarations, d => { - const parent = getThisContainer( - d, /*includeArrowFunctions*/ - false - ).parent; - return isClassLike(parent) && parent; - }); - if (parentDeclaration) { - const classType = - getDeclaredTypeOfSymbol(getSymbolOfNode(parentDeclaration)) as InterfaceType; - const baseClassType = classType && getBaseTypes(classType)[0]; - if (baseClassType) { - return getTypeOfPropertyOfType( - baseClassType, - property.escapedName - ); - } - } - } - - // Return the type implied by a binding pattern element. This is the type of the initializer of the element if - // one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding - // pattern. Otherwise, it is the type any. - function getTypeFromBindingElement( - element: BindingElement, - includePatternInType?: boolean, - reportErrors?: boolean - ): Type { - if (element.initializer) { - return addOptionality(checkDeclarationInitializer(element)); - } - if (isBindingPattern(element.name)) { - return getTypeFromBindingPattern( - element.name, - includePatternInType, - reportErrors - ); - } - if (reportErrors - && !declarationBelongsToPrivateAmbientMember(element)) - { - reportImplicitAny(element, anyType); - } - // When we're including the pattern in the type (an indication we're obtaining a contextual type), we - // use the non-inferrable any type. Inference will never directly infer this type, but it is possible - // to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases, - // widening of the binding pattern type substitutes a regular any for the non-inferrable any. - return includePatternInType ? nonInferrableAnyType : anyType; - } - - // Return the type implied by an object binding pattern - function getTypeFromObjectBindingPattern( - pattern: ObjectBindingPattern, - includePatternInType: boolean, - reportErrors: boolean - ): Type { - const members = createSymbolTable(); - let stringIndexInfo: IndexInfo | undefined; - let objectFlags = - ObjectFlags.ObjectLiteral - | ObjectFlags.ContainsObjectOrArrayLiteral; - forEach(pattern.elements, e => { - const name = e.propertyName || e.name; - if (e.dotDotDotToken) { - stringIndexInfo = createIndexInfo( - anyType, /*isReadonly*/ - false - ); - return; - } - - const exprType = getLiteralTypeFromPropertyName(name); - if (!isTypeUsableAsPropertyName(exprType)) { - // do not include computed properties in the implied type - objectFlags |= ObjectFlags - .ObjectLiteralPatternWithComputedProperties; - return; - } - const text = getPropertyNameFromType(exprType); - const flags = - SymbolFlags.Property - | (e.initializer ? SymbolFlags.Optional : 0); - const symbol = createSymbol(flags, text); - symbol.type = getTypeFromBindingElement( - e, - includePatternInType, - reportErrors - ); - symbol.bindingElement = e; - members.set(symbol.escapedName, symbol); - }); - const result = createAnonymousType( - undefined, - members, - emptyArray, - emptyArray, - stringIndexInfo, - undefined - ); - result.objectFlags |= objectFlags; - if (includePatternInType) { - result.pattern = pattern; - result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; - } - return result; - } - - // Return the type implied by an array binding pattern - function getTypeFromArrayBindingPattern( - pattern: BindingPattern, - includePatternInType: boolean, - reportErrors: boolean - ): Type { - const elements = pattern.elements; - const lastElement = lastOrUndefined(elements); - const hasRestElement = - !!(lastElement - && lastElement.kind === SyntaxKind.BindingElement - && lastElement.dotDotDotToken); - if (elements.length === 0 || elements.length === 1 - && hasRestElement) - { - return languageVersion >= ScriptTarget.ES2015 - ? createIterableType(anyType) - : anyArrayType; - } - const elementTypes = map( - elements, - e => isOmittedExpression(e) - ? anyType - : getTypeFromBindingElement( - e, - includePatternInType, - reportErrors - ) - ); - const minLength = - findLastIndex( - elements, - e => !isOmittedExpression(e) && !hasDefaultValue(e), - elements.length - (hasRestElement ? 2 : 1) - ) + 1; - let result = createTupleType( - elementTypes, - minLength, - hasRestElement - ); - if (includePatternInType) { - result = cloneTypeReference(result); - result.pattern = pattern; - result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; - } - return result; - } - - // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself - // and without regard to its context (i.e. without regard any type annotation or initializer associated with the - // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] - // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is - // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring - // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of - // the parameter. - function getTypeFromBindingPattern( - pattern: BindingPattern, - includePatternInType = false, - reportErrors = false - ): Type { - return pattern.kind === SyntaxKind.ObjectBindingPattern - ? getTypeFromObjectBindingPattern( - pattern, - includePatternInType, - reportErrors - ) - : getTypeFromArrayBindingPattern( - pattern, - includePatternInType, - reportErrors - ); - } - - // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type - // specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it - // is a bit more involved. For example: - // - // var [x, s = ""] = [1, "one"]; - // - // Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the - // binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the - // tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string. - function getWidenedTypeForVariableLikeDeclaration( - declaration: ParameterDeclaration | PropertyDeclaration - | PropertySignature | VariableDeclaration | BindingElement, - reportErrors?: boolean - ): Type { - return widenTypeForVariableLikeDeclaration( - getTypeForVariableLikeDeclaration( - declaration, /*includeOptionality*/ - true - ), - declaration, - reportErrors - ); - } - - function widenTypeForVariableLikeDeclaration( - type: Type | undefined, - declaration: any, - reportErrors?: boolean - ) { - if (type) { - if (reportErrors) { - reportErrorsFromWidening(declaration, type); - } - - // always widen a 'unique symbol' type if the type was created for a different declaration. - if (type.flags & TypeFlags.UniqueESSymbol - && (isBindingElement(declaration) || !declaration.type) - && type.symbol !== getSymbolOfNode(declaration)) - { - type = esSymbolType; - } - - return getWidenedType(type); - } - - // Rest parameters default to type any[], other parameters default to type any - type = isParameter(declaration) && declaration.dotDotDotToken - ? anyArrayType - : anyType; - - // Report implicit any errors unless this is a private property within an ambient declaration - if (reportErrors) { - if (!declarationBelongsToPrivateAmbientMember(declaration)) { - reportImplicitAny(declaration, type); - } - } - return type; - } - - function declarationBelongsToPrivateAmbientMember(declaration: - VariableLikeDeclaration) - { - const root = getRootDeclaration(declaration); - const memberDeclaration = root.kind === SyntaxKind.Parameter - ? root.parent - : root; - return isPrivateWithinAmbient(memberDeclaration); - } - - function tryGetTypeFromEffectiveTypeNode(declaration: Declaration) { - const typeNode = getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - } - - function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.type) { - const type = - getTypeOfVariableOrParameterOrPropertyWorker(symbol); - // For a contextually typed parameter it is possible that a type has already - // been assigned (in assignTypeToParameterAndFixTypeParameters), and we want - // to preserve this type. - if (!links.type) { - links.type = type; - } - } - return links.type; - } - - function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol) { - // Handle prototype property - if (symbol.flags & SymbolFlags.Prototype) { - return getTypeOfPrototypeProperty(symbol); - } - // CommonsJS require and module both have type any. - if (symbol === requireSymbol) { - return anyType; - } - if (symbol.flags & SymbolFlags.ModuleExports) { - const fileSymbol = - getSymbolOfNode(getSourceFileOfNode(symbol - .valueDeclaration)); - const members = createSymbolTable(); - members.set('exports' as __String, fileSymbol); - return createAnonymousType( - symbol, - members, - emptyArray, - emptyArray, - undefined, - undefined - ); - } - // Handle catch clause variables - const declaration = symbol.valueDeclaration; - if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { - return anyType; - } - // Handle export default expressions - if (isSourceFile(declaration) && isJsonSourceFile(declaration)) { - if (!declaration.statements.length) { - return emptyObjectType; - } - return getWidenedType(getWidenedLiteralType(checkExpression(declaration - .statements[0].expression))); - } - - // Handle variable, parameter or property - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` - if (symbol.flags & SymbolFlags.ValueModule - && !(symbol.flags & SymbolFlags.Assignment)) - { - return getTypeOfFuncClassEnumModule(symbol); - } - return reportCircularityError(symbol); - } - let type: Type | undefined; - if (declaration.kind === SyntaxKind.ExportAssignment) { - type = widenTypeForVariableLikeDeclaration( - checkExpressionCached(( declaration) - .expression), - declaration - ); - } else if ( - isBinaryExpression(declaration) - || (isInJSFile(declaration) - && (isCallExpression(declaration) - || (isPropertyAccessExpression(declaration) - || isBindableStaticElementAccessExpression(declaration)) - && isBinaryExpression(declaration.parent))) - ) { - type = getWidenedTypeForAssignmentDeclaration(symbol); - } else if (isJSDocPropertyLikeTag(declaration) - || isPropertyAccessExpression(declaration) - || isElementAccessExpression(declaration) - || isIdentifier(declaration) - || isStringLiteralLike(declaration) - || isNumericLiteral(declaration) - || isClassDeclaration(declaration) - || isFunctionDeclaration(declaration) - || (isMethodDeclaration(declaration) - && !isObjectLiteralMethod(declaration)) - || isMethodSignature(declaration) - || isSourceFile(declaration)) - { - // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` - if (symbol.flags - & (SymbolFlags.Function | SymbolFlags.Method - | SymbolFlags.Class | SymbolFlags.Enum - | SymbolFlags.ValueModule)) - { - return getTypeOfFuncClassEnumModule(symbol); - } - type = isBinaryExpression(declaration.parent) - ? getWidenedTypeForAssignmentDeclaration(symbol) - : tryGetTypeFromEffectiveTypeNode(declaration) || anyType; - } else if (isPropertyAssignment(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) - || checkPropertyAssignment(declaration); - } else if (isJsxAttribute(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) - || checkJsxAttribute(declaration); - } else if (isShorthandPropertyAssignment(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) - || checkExpressionForMutableLocation( - declaration.name, - CheckMode.Normal - ); - } else if (isObjectLiteralMethod(declaration)) { - type = tryGetTypeFromEffectiveTypeNode(declaration) - || checkObjectLiteralMethod(declaration, CheckMode.Normal); - } else if (isParameter(declaration) - || isPropertyDeclaration(declaration) - || isPropertySignature(declaration) - || isVariableDeclaration(declaration) - || isBindingElement(declaration)) - { - type = getWidenedTypeForVariableLikeDeclaration( - declaration, /*includeOptionality*/ - true - ); - } // getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive. - // Re-dispatch based on valueDeclaration.kind instead. - else if (isEnumDeclaration(declaration)) { - type = getTypeOfFuncClassEnumModule(symbol); - } else if (isEnumMember(declaration)) { - type = getTypeOfEnumMember(symbol); - } else if (isAccessor(declaration)) { - type = resolveTypeOfAccessors(symbol); - } else { - return Debug - .fail('Unhandled declaration kind! ' - + Debug.formatSyntaxKind(declaration.kind) + ' for ' - + Debug.formatSymbol(symbol)); - } - - if (!popTypeResolution()) { - // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` - if (symbol.flags & SymbolFlags.ValueModule - && !(symbol.flags & SymbolFlags.Assignment)) - { - return getTypeOfFuncClassEnumModule(symbol); - } - return reportCircularityError(symbol); - } - return type; - } - - function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration - | undefined): TypeNode | undefined - { - if (accessor) { - if (accessor.kind === SyntaxKind.GetAccessor) { - const getterTypeAnnotation = - getEffectiveReturnTypeNode(accessor); - return getterTypeAnnotation; - } else { - const setterTypeAnnotation = - getEffectiveSetAccessorTypeAnnotationNode(accessor); - return setterTypeAnnotation; - } - } - return undefined; - } - - function getAnnotatedAccessorType(accessor: AccessorDeclaration - | undefined): Type | undefined - { - const node = getAnnotatedAccessorTypeNode(accessor); - return node && getTypeFromTypeNode(node); - } - - function getAnnotatedAccessorThisParameter(accessor: - AccessorDeclaration): Symbol | undefined - { - const parameter = getAccessorThisParameter(accessor); - return parameter && parameter.symbol; - } - - function getThisTypeOfDeclaration(declaration: - SignatureDeclaration): Type | undefined - { - return getThisTypeOfSignature(getSignatureFromDeclaration(declaration)); - } - - function getTypeOfAccessors(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - return links.type - || (links.type = getTypeOfAccessorsWorker(symbol)); - } - - function getTypeOfAccessorsWorker(symbol: Symbol): Type { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return errorType; - } - - let type = resolveTypeOfAccessors(symbol); - - if (!popTypeResolution()) { - type = anyType; - if (noImplicitAny) { - const getter = getDeclarationOfKind( - symbol, - SyntaxKind.GetAccessor - ); - error( - getter, - Diagnostics - ._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, - symbolToString(symbol) - ); - } - } - return type; - } - - function resolveTypeOfAccessors(symbol: Symbol) { - const getter = getDeclarationOfKind( - symbol, - SyntaxKind.GetAccessor - ); - const setter = getDeclarationOfKind( - symbol, - SyntaxKind.SetAccessor - ); - - if (getter && isInJSFile(getter)) { - const jsDocType = - getTypeForDeclarationFromJSDocComment(getter); - if (jsDocType) { - return jsDocType; - } - } - // First try to see if the user specified a return type on the get-accessor. - const getterReturnType = getAnnotatedAccessorType(getter); - if (getterReturnType) { - return getterReturnType; - } else { - // If the user didn't specify a return type, try to use the set-accessor's parameter type. - const setterParameterType = getAnnotatedAccessorType(setter); - if (setterParameterType) { - return setterParameterType; - } else { - // If there are no specified types, try to infer it from the body of the get accessor if it exists. - if (getter && getter.body) { - return getReturnTypeFromBody(getter); - } // Otherwise, fall back to 'any'. - else { - if (setter) { - if (!isPrivateWithinAmbient(setter)) { - errorOrSuggestion( - noImplicitAny, - setter, - Diagnostics - .Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, - symbolToString(symbol) - ); - } - } else { - Debug.assert( - !!getter, - 'there must exist a getter as we are current checking either setter or getter in this function' - ); - if (!isPrivateWithinAmbient(getter!)) { - errorOrSuggestion( - noImplicitAny, - getter!, - Diagnostics - .Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, - symbolToString(symbol) - ); - } - } - return anyType; - } - } - } - } - - function getBaseTypeVariableOfClass(symbol: Symbol) { - const baseConstructorType = - getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol)); - return baseConstructorType.flags & TypeFlags.TypeVariable - ? baseConstructorType - : baseConstructorType.flags & TypeFlags.Intersection - ? find( - (baseConstructorType as IntersectionType).types, - t => !!(t.flags & TypeFlags.TypeVariable) - ) - : undefined; - } - - function getTypeOfFuncClassEnumModule(symbol: Symbol): Type { - let links = getSymbolLinks(symbol); - const originalLinks = links; - if (!links.type) { - const jsDeclaration = - getDeclarationOfExpando(symbol.valueDeclaration); - if (jsDeclaration) { - const merged = mergeJSSymbols( - symbol, - getSymbolOfNode(jsDeclaration) - ); - if (merged) { - // note:we overwrite links because we just cloned the symbol - symbol = links = merged; - } - } - originalLinks.type = links - .type = getTypeOfFuncClassEnumModuleWorker(symbol); - } - return links.type; - } - - function getTypeOfFuncClassEnumModuleWorker(symbol: Symbol): Type { - const declaration = symbol.valueDeclaration; - if (symbol.flags & SymbolFlags.Module - && isShorthandAmbientModuleSymbol(symbol)) - { - return anyType; - } else if (declaration.kind === SyntaxKind.BinaryExpression - || isAccessExpression(declaration) - && declaration.parent.kind === SyntaxKind.BinaryExpression) - { - return getWidenedTypeForAssignmentDeclaration(symbol); - } else if (symbol.flags & SymbolFlags.ValueModule && declaration - && isSourceFile(declaration) - && declaration.commonJsModuleIndicator) - { - const resolvedModule = resolveExternalModuleSymbol(symbol); - if (resolvedModule !== symbol) { - if (!pushTypeResolution( - symbol, - TypeSystemPropertyName.Type - )) { - return errorType; - } - const exportEquals = - getMergedSymbol(symbol.exports! - .get(InternalSymbolName.ExportEquals)!); - const type = getWidenedTypeForAssignmentDeclaration( - exportEquals, - exportEquals === resolvedModule - ? undefined - : resolvedModule - ); - if (!popTypeResolution()) { - return reportCircularityError(symbol); - } - return type; - } - } - const type = createObjectType(ObjectFlags.Anonymous, symbol); - if (symbol.flags & SymbolFlags.Class) { - const baseTypeVariable = getBaseTypeVariableOfClass(symbol); - return baseTypeVariable - ? getIntersectionType([type, baseTypeVariable]) - : type; - } else { - return strictNullChecks && symbol.flags & SymbolFlags.Optional - ? getOptionalType(type) - : type; - } - } - - function getTypeOfEnumMember(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - return links.type - || (links.type = getDeclaredTypeOfEnumMember(symbol)); - } - - function getTypeOfAlias(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.type) { - const targetSymbol = resolveAlias(symbol); - - // It only makes sense to get the type of a value symbol. If the result of resolving - // the alias is not a value, then it has no type. To get the type associated with a - // type symbol, call getDeclaredTypeOfSymbol. - // This check is important because without it, a call to getTypeOfSymbol could end - // up recursively calling getTypeOfAlias, causing a stack overflow. - links.type = targetSymbol.flags & SymbolFlags.Value - ? getTypeOfSymbol(targetSymbol) - : errorType; - } - return links.type; - } - - function getTypeOfInstantiatedSymbol(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.type) { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return links.type = errorType; - } - let type = instantiateType( - getTypeOfSymbol(links.target!), - links.mapper - ); - if (!popTypeResolution()) { - type = reportCircularityError(symbol); - } - links.type = type; - } - return links.type; - } - - function reportCircularityError(symbol: Symbol) { - const declaration = symbol - .valueDeclaration; - // Check if variable has type annotation that circularly references the variable itself - if (getEffectiveTypeAnnotationNode(declaration)) { - error( - symbol.valueDeclaration, - Diagnostics - ._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, - symbolToString(symbol) - ); - return errorType; - } - // Check if variable has initializer that circularly references the variable itself - if (noImplicitAny - && (declaration.kind !== SyntaxKind.Parameter - || ( declaration).initializer)) - { - error( - symbol.valueDeclaration, - Diagnostics - ._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, - symbolToString(symbol) - ); - } - // Circularities could also result from parameters in function expressions that end up - // having themselves as contextual types following type argument inference. In those cases - // we have already reported an implicit any error so we don't report anything here. - return anyType; - } - - function getTypeOfSymbolWithDeferredType(symbol: Symbol) { - const links = getSymbolLinks(symbol); - if (!links.type) { - Debug.assertDefined(links.deferralParent); - Debug.assertDefined(links.deferralConstituents); - links.type = links.deferralParent!.flags & TypeFlags.Union - ? getUnionType(links.deferralConstituents!) - : getIntersectionType(links.deferralConstituents!); - } - return links.type; - } - - function getTypeOfSymbol(symbol: Symbol): Type { - if (getCheckFlags(symbol) & CheckFlags.DeferredType) { - return getTypeOfSymbolWithDeferredType(symbol); - } - if (getCheckFlags(symbol) & CheckFlags.Instantiated) { - return getTypeOfInstantiatedSymbol(symbol); - } - if (getCheckFlags(symbol) & CheckFlags.ReverseMapped) { - return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol); - } - if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { - return getTypeOfVariableOrParameterOrProperty(symbol); - } - if (symbol.flags - & (SymbolFlags.Function | SymbolFlags.Method - | SymbolFlags.Class | SymbolFlags.Enum - | SymbolFlags.ValueModule)) - { - return getTypeOfFuncClassEnumModule(symbol); - } - if (symbol.flags & SymbolFlags.EnumMember) { - return getTypeOfEnumMember(symbol); - } - if (symbol.flags & SymbolFlags.Accessor) { - return getTypeOfAccessors(symbol); - } - if (symbol.flags & SymbolFlags.Alias) { - return getTypeOfAlias(symbol); - } - return errorType; - } - - function isReferenceToType(type: Type, target: Type) { - return type !== undefined - && target !== undefined - && (getObjectFlags(type) & ObjectFlags.Reference) !== 0 - && ( type).target === target; - } - - function getTargetType(type: Type): Type { - return getObjectFlags(type) & ObjectFlags.Reference - ? ( type).target - : type; - } - - // TODO: GH#18217 If `checkBase` is undefined, we should not call this because this will always return false. - function hasBaseType(type: Type, checkBase: Type | undefined) { - return check(type); - function check(type: Type): boolean { - if (getObjectFlags(type) - & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) - { - const target = getTargetType(type); - return target === checkBase - || some(getBaseTypes(target), check); - } else if (type.flags & TypeFlags.Intersection) { - return some(( type).types, check); - } - return false; - } - } - - // Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set. - // The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set - // in-place and returns the same array. - function appendTypeParameters( - typeParameters: TypeParameter[] | undefined, - declarations: readonly TypeParameterDeclaration[] - ): TypeParameter[] | undefined { - for (const declaration of declarations) { - typeParameters = appendIfUnique( - typeParameters, - getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration)) - ); - } - return typeParameters; - } - - // Return the outer type parameters of a node or undefined if the node has no outer type parameters. - function getOuterTypeParameters( - node: Node, - includeThisTypes?: boolean - ): TypeParameter[] | undefined { - while (true) { - node = node - .parent; // TODO: GH#18217 Use SourceFile kind check instead - if (node && isBinaryExpression(node)) { - // prototype assignments get the outer type parameters of their constructor function - const assignmentKind = getAssignmentDeclarationKind(node); - if (assignmentKind === AssignmentDeclarationKind.Prototype - || assignmentKind - === AssignmentDeclarationKind.PrototypeProperty) - { - const symbol = getSymbolOfNode(node.left); - if (symbol && symbol.parent - && !findAncestor( - symbol.parent.valueDeclaration, - d => node === d - )) - { - node = symbol.parent.valueDeclaration; - } - } - } - if (!node) { - return undefined; - } - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.MethodSignature: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.JSDocTemplateTag: - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocEnumTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.MappedType: - case SyntaxKind.ConditionalType: - const outerTypeParameters = getOuterTypeParameters( - node, - includeThisTypes - ); - if (node.kind === SyntaxKind.MappedType) { - return append( - outerTypeParameters, - getDeclaredTypeOfTypeParameter(getSymbolOfNode(( node) - .typeParameter)) - ); - } else if (node.kind === SyntaxKind.ConditionalType) { - return concatenate( - outerTypeParameters, - getInferTypeParameters( node) - ); - } - const outerAndOwnTypeParameters = appendTypeParameters( - outerTypeParameters, - getEffectiveTypeParameterDeclarations( node) - ); - const thisType = includeThisTypes - && (node.kind === SyntaxKind.ClassDeclaration - || node.kind === SyntaxKind.ClassExpression - || node.kind - === SyntaxKind.InterfaceDeclaration - || isJSConstructor(node)) - && getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node as ClassLikeDeclaration - | InterfaceDeclaration)).thisType; - return thisType - ? append(outerAndOwnTypeParameters, thisType) - : outerAndOwnTypeParameters; - } - } - } - - // The outer type parameters are those defined by enclosing generic classes, methods, or functions. - function getOuterTypeParametersOfClassOrInterface(symbol: - Symbol): TypeParameter[] | undefined - { - const declaration = symbol.flags & SymbolFlags.Class - ? symbol.valueDeclaration - : getDeclarationOfKind( - symbol, - SyntaxKind.InterfaceDeclaration - )!; - Debug.assert( - !!declaration, - 'Class was missing valueDeclaration -OR- non-class had no interface declarations' - ); - return getOuterTypeParameters(declaration); - } - - // The local type parameters are the combined set of type parameters from all declarations of the class, - // interface, or type alias. - function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: - Symbol): TypeParameter[] | undefined - { - let result: TypeParameter[] | undefined; - for (const node of symbol.declarations) { - if (node.kind === SyntaxKind.InterfaceDeclaration - || node.kind === SyntaxKind.ClassDeclaration - || node.kind === SyntaxKind.ClassExpression - || isJSConstructor(node) - || isTypeAlias(node)) - { - const declaration = node; - result = appendTypeParameters( - result, - getEffectiveTypeParameterDeclarations(declaration) - ); - } - } - return result; - } - - // The full set of type parameters for a generic class or interface type consists of its outer type parameters plus - // its locally declared type parameters. - function getTypeParametersOfClassOrInterface(symbol: - Symbol): TypeParameter[] | undefined - { - return concatenate( - getOuterTypeParametersOfClassOrInterface(symbol), - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) - ); - } - - // A type is a mixin constructor if it has a single construct signature taking no type parameters and a single - // rest parameter of type any[]. - function isMixinConstructorType(type: Type) { - const signatures = getSignaturesOfType( - type, - SignatureKind.Construct - ); - if (signatures.length === 1) { - const s = signatures[0]; - return !s.typeParameters && s.parameters.length === 1 - && signatureHasRestParameter(s) - && getElementTypeOfArrayType(getTypeOfParameter(s - .parameters[0])) === anyType; - } - return false; - } - - function isConstructorType(type: Type): boolean { - if (getSignaturesOfType(type, SignatureKind.Construct).length - > 0) - { - return true; - } - if (type.flags & TypeFlags.TypeVariable) { - const constraint = getBaseConstraintOfType(type); - return !!constraint && isMixinConstructorType(constraint); - } - return false; - } - - function getBaseTypeNodeOfClass(type: - InterfaceType): ExpressionWithTypeArguments | undefined - { - return getEffectiveBaseTypeNode(type.symbol - .valueDeclaration as ClassLikeDeclaration); - } - - function getConstructorsForTypeArguments( - type: Type, - typeArgumentNodes: readonly TypeNode[] | undefined, - location: Node - ): readonly Signature[] { - const typeArgCount = length(typeArgumentNodes); - const isJavascript = isInJSFile(location); - return filter( - getSignaturesOfType(type, SignatureKind.Construct), - sig => (isJavascript - || typeArgCount - >= getMinTypeArgumentCount(sig.typeParameters)) - && typeArgCount <= length(sig.typeParameters) - ); - } - - function getInstantiatedConstructorsForTypeArguments( - type: Type, - typeArgumentNodes: readonly TypeNode[] | undefined, - location: Node - ): readonly Signature[] { - const signatures = getConstructorsForTypeArguments( - type, - typeArgumentNodes, - location - ); - const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode); - return sameMap( - signatures, - sig => some(sig.typeParameters) - ? getSignatureInstantiation( - sig, - typeArguments, - isInJSFile(location) - ) - : sig - ); - } - - /** - * The base constructor of a class can resolve to - * * undefinedType if the class has no extends clause, - * * unknownType if an error occurred during resolution of the extends expression, - * * nullType if the extends expression is the null value, - * * anyType if the extends expression has type any, or - * * an object type with at least one construct signature. - */ - function getBaseConstructorTypeOfClass(type: InterfaceType): Type { - if (!type.resolvedBaseConstructorType) { - const decl = type.symbol - .valueDeclaration; - const extended = getEffectiveBaseTypeNode(decl); - const baseTypeNode = getBaseTypeNodeOfClass(type); - if (!baseTypeNode) { - return type.resolvedBaseConstructorType = undefinedType; - } - if (!pushTypeResolution( - type, - TypeSystemPropertyName.ResolvedBaseConstructorType - )) { - return errorType; - } - const baseConstructorType = - checkExpression(baseTypeNode.expression); - if (extended && baseTypeNode !== extended) { - Debug - .assert(!extended - .typeArguments); // Because this is in a JS file, and baseTypeNode is in an @extends tag - checkExpression(extended.expression); - } - if (baseConstructorType.flags - & (TypeFlags.Object | TypeFlags.Intersection)) - { - // Resolving the members of a class requires us to resolve the base class of that class. - // We force resolution here such that we catch circularities now. - resolveStructuredTypeMembers( baseConstructorType); - } - if (!popTypeResolution()) { - error( - type.symbol.valueDeclaration, - Diagnostics - ._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, - symbolToString(type.symbol) - ); - return type.resolvedBaseConstructorType = errorType; - } - if (!(baseConstructorType.flags & TypeFlags.Any) - && baseConstructorType !== nullWideningType - && !isConstructorType(baseConstructorType)) - { - const err = error( - baseTypeNode.expression, - Diagnostics.Type_0_is_not_a_constructor_function_type, - typeToString(baseConstructorType) - ); - if (baseConstructorType.flags & TypeFlags.TypeParameter) { - const constraint = - getConstraintFromTypeParameter(baseConstructorType); - let ctorReturn: Type = unknownType; - if (constraint) { - const ctorSig = getSignaturesOfType( - constraint, - SignatureKind.Construct - ); - if (ctorSig[0]) { - ctorReturn = getReturnTypeOfSignature(ctorSig - [0]); - } - } - addRelatedInfo( - err, - createDiagnosticForNode( - baseConstructorType.symbol.declarations[0], - Diagnostics - .Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, - symbolToString(baseConstructorType.symbol), - typeToString(ctorReturn) - ) - ); - } - return type.resolvedBaseConstructorType = errorType; - } - type.resolvedBaseConstructorType = baseConstructorType; - } - return type.resolvedBaseConstructorType; - } - - function getBaseTypes(type: InterfaceType): BaseType[] { - if (!type.resolvedBaseTypes) { - if (type.objectFlags & ObjectFlags.Tuple) { - type - .resolvedBaseTypes = [createArrayType( - getUnionType(type.typeParameters || emptyArray), - ( type).readonly - )]; - } else if (type.symbol.flags - & (SymbolFlags.Class | SymbolFlags.Interface)) - { - if (type.symbol.flags & SymbolFlags.Class) { - resolveBaseTypesOfClass(type); - } - if (type.symbol.flags & SymbolFlags.Interface) { - resolveBaseTypesOfInterface(type); - } - } else { - Debug.fail('type must be class or interface'); - } - } - return type.resolvedBaseTypes; - } - - function resolveBaseTypesOfClass(type: InterfaceType) { - type.resolvedBaseTypes = resolvingEmptyArray; - const baseConstructorType = - getApparentType(getBaseConstructorTypeOfClass(type)); - if (!(baseConstructorType.flags - & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) - { - return type.resolvedBaseTypes = emptyArray; - } - const baseTypeNode = getBaseTypeNodeOfClass(type)!; - let baseType: Type; - const originalBaseType = baseConstructorType.symbol - ? getDeclaredTypeOfSymbol(baseConstructorType.symbol) - : undefined; - if (baseConstructorType.symbol - && baseConstructorType.symbol.flags & SymbolFlags.Class - && areAllOuterTypeParametersApplied(originalBaseType!)) - { - // When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the - // class and all return the instance type of the class. There is no need for further checks and we can apply the - // type arguments in the same manner as a type reference to get the same error reporting experience. - baseType = getTypeFromClassOrInterfaceReference( - baseTypeNode, - baseConstructorType.symbol - ); - } else if (baseConstructorType.flags & TypeFlags.Any) { - baseType = baseConstructorType; - } else { - // The class derives from a "class-like" constructor function, check that we have at least one construct signature - // with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere - // we check that all instantiated signatures return the same type. - const constructors = - getInstantiatedConstructorsForTypeArguments( - baseConstructorType, - baseTypeNode.typeArguments, - baseTypeNode - ); - if (!constructors.length) { - error( - baseTypeNode.expression, - Diagnostics - .No_base_constructor_has_the_specified_number_of_type_arguments - ); - return type.resolvedBaseTypes = emptyArray; - } - baseType = getReturnTypeOfSignature(constructors[0]); - } - - if (baseType === errorType) { - return type.resolvedBaseTypes = emptyArray; - } - if (!isValidBaseType(baseType)) { - error( - baseTypeNode.expression, - Diagnostics - .Base_constructor_return_type_0_is_not_an_object_type_or_intersection_of_object_types_with_statically_known_members, - typeToString(baseType) - ); - return type.resolvedBaseTypes = emptyArray; - } - if (type === baseType || hasBaseType(baseType, type)) { - error( - type.symbol.valueDeclaration, - Diagnostics - .Type_0_recursively_references_itself_as_a_base_type, - typeToString( - type, /*enclosingDeclaration*/ - undefined, - TypeFormatFlags.WriteArrayAsGenericType - ) - ); - return type.resolvedBaseTypes = emptyArray; - } - if (type.resolvedBaseTypes === resolvingEmptyArray) { - // Circular reference, likely through instantiation of default parameters - // (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset - // as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a - // partial instantiation of the members without the base types fully resolved - type.members = undefined; - } - return type.resolvedBaseTypes = [baseType]; - } - - function areAllOuterTypeParametersApplied(type: - Type): boolean - { // TODO: GH#18217 Shouldn't this take an InterfaceType? - // An unapplied type parameter has its symbol still the same as the matching argument symbol. - // Since parameters are applied outer-to-inner, only the last outer parameter needs to be checked. - const outerTypeParameters = ( type) - .outerTypeParameters; - if (outerTypeParameters) { - const last = outerTypeParameters.length - 1; - const typeArguments = getTypeArguments( type); - return outerTypeParameters[last].symbol - !== typeArguments[last].symbol; - } - return true; - } - - // A valid base type is `any`, an object type or intersection of object types. - function isValidBaseType(type: Type): type is BaseType { - if (type.flags & TypeFlags.TypeParameter) { - const constraint = getBaseConstraintOfType(type); - if (constraint) { - return isValidBaseType(constraint); - } - } - // TODO: Given that we allow type parmeters here now, is this `!isGenericMappedType(type)` check really needed? - // There's no reason a `T` should be allowed while a `Readonly` should not. - return !!(type.flags - & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any)) - && !isGenericMappedType(type) - || !!(type.flags & TypeFlags.Intersection) - && every(( type).types, isValidBaseType); - } - - function resolveBaseTypesOfInterface(type: InterfaceType): void { - type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; - for (const declaration of type.symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration - && getInterfaceBaseTypeNodes( declaration)) - { - for (const node - of getInterfaceBaseTypeNodes( declaration)!) - { - const baseType = getTypeFromTypeNode(node); - if (baseType !== errorType) { - if (isValidBaseType(baseType)) { - if (type !== baseType - && !hasBaseType(baseType, type)) - { - if (type.resolvedBaseTypes - === emptyArray) - { - type - .resolvedBaseTypes = [ baseType]; - } else { - type.resolvedBaseTypes.push(baseType); - } - } else { - error( - declaration, - Diagnostics - .Type_0_recursively_references_itself_as_a_base_type, - typeToString( - type, /*enclosingDeclaration*/ - undefined, - TypeFormatFlags - .WriteArrayAsGenericType - ) - ); - } - } else { - error( - node, - Diagnostics - .An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members - ); - } - } - } - } - } - } - - /** - * Returns true if the interface given by the symbol is free of "this" references. - * - * Specifically, the result is true if the interface itself contains no references - * to "this" in its body, if all base types are interfaces, - * and if none of the base interfaces have a "this" type. - */ - function isThislessInterface(symbol: Symbol): boolean { - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration) { - if (declaration.flags & NodeFlags.ContainsThis) { - return false; - } - const baseTypeNodes = - getInterfaceBaseTypeNodes( declaration); - if (baseTypeNodes) { - for (const node of baseTypeNodes) { - if (isEntityNameExpression(node.expression)) { - const baseSymbol = resolveEntityName( - node.expression, - SymbolFlags.Type, /*ignoreErrors*/ - true - ); - if (!baseSymbol - || !(baseSymbol.flags - & SymbolFlags.Interface) - || getDeclaredTypeOfClassOrInterface(baseSymbol) - .thisType) - { - return false; - } - } - } - } - } - } - return true; - } - - function getDeclaredTypeOfClassOrInterface(symbol: - Symbol): InterfaceType - { - let links = getSymbolLinks(symbol); - const originalLinks = links; - if (!links.declaredType) { - const kind = symbol.flags & SymbolFlags.Class - ? ObjectFlags.Class - : ObjectFlags.Interface; - const merged = mergeJSSymbols( - symbol, - getAssignedClassSymbol(symbol.valueDeclaration) - ); - if (merged) { - // note:we overwrite links because we just cloned the symbol - symbol = links = merged; - } - - const type = originalLinks.declaredType = links - .declaredType = createObjectType( - kind, - symbol - ); - const outerTypeParameters = - getOuterTypeParametersOfClassOrInterface(symbol); - const localTypeParameters = - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - // A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type - // because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular, - // property types inferred from initializers and method return types inferred from return statements are very hard - // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of - // "this" references. - if (outerTypeParameters || localTypeParameters - || kind === ObjectFlags.Class - || !isThislessInterface(symbol)) - { - type.objectFlags |= ObjectFlags.Reference; - type.typeParameters = concatenate( - outerTypeParameters, - localTypeParameters - ); - type.outerTypeParameters = outerTypeParameters; - type.localTypeParameters = localTypeParameters; - ( type) - .instantiations = createMap(); - ( type).instantiations.set( - getTypeListId(type.typeParameters), - type - ); - ( type).target = type; - ( type).resolvedTypeArguments = type - .typeParameters; - type.thisType = createTypeParameter(symbol); - type.thisType.isThisType = true; - type.thisType.constraint = type; - } - } - return links.declaredType; - } - - function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.declaredType) { - // Note that we use the links object as the target here because the symbol object is used as the unique - // identity for resolution of the 'type' property in SymbolLinks. - if (!pushTypeResolution( - symbol, - TypeSystemPropertyName.DeclaredType - )) { - return errorType; - } - - let type: Type; - let declaration; - if (isTypeOnlyAlias(symbol)) { - // Symbol is synthetic type alias for type-only import or export. - // See `createSyntheticTypeAlias`. - type = getDeclaredTypeOfSymbol(symbol.immediateTarget); - declaration = symbol.valueDeclaration; - } else { - declaration = Debug.assertDefined( - find(symbol.declarations, isTypeAlias), - 'Type alias symbol with no valid declaration found' - ); - const typeNode = isJSDocTypeAlias(declaration) - ? declaration.typeExpression - : declaration.type; - // If typeNode is missing, we will error in checkJSDocTypedefTag. - type = typeNode - ? getTypeFromTypeNode(typeNode) - : errorType; - } - - if (popTypeResolution()) { - const typeParameters = - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - if (typeParameters) { - // Initialize the instantiation cache for generic type aliases. The declared type corresponds to - // an instantiation of the type alias with the type parameters supplied as type arguments. - links.typeParameters = typeParameters; - links.instantiations = createMap(); - links.instantiations.set( - getTypeListId(typeParameters), - type - ); - } - } else { - type = errorType; - error( - isNamedDeclaration(declaration) - ? declaration.name - : declaration || declaration, - Diagnostics.Type_alias_0_circularly_references_itself, - symbolToString(symbol) - ); - } - links.declaredType = type; - } - return links.declaredType; - } - - function isStringConcatExpression(expr: Node): boolean { - if (isStringLiteralLike(expr)) { - return true; - } else if (expr.kind === SyntaxKind.BinaryExpression) { - return isStringConcatExpression(( expr).left) - && isStringConcatExpression(( expr) - .right); - } - return false; - } - - function isLiteralEnumMember(member: EnumMember) { - const expr = member.initializer; - if (!expr) { - return !(member.flags & NodeFlags.Ambient); - } - switch (expr.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return true; - case SyntaxKind.PrefixUnaryExpression: - return ( expr).operator - === SyntaxKind.MinusToken - && ( expr).operand.kind - === SyntaxKind.NumericLiteral; - case SyntaxKind.Identifier: - return nodeIsMissing(expr) - || !!getSymbolOfNode(member.parent).exports! - .get(( expr).escapedText); - case SyntaxKind.BinaryExpression: - return isStringConcatExpression(expr); - default: - return false; - } - } - - function getEnumKind(symbol: Symbol): EnumKind { - const links = getSymbolLinks(symbol); - if (links.enumKind !== undefined) { - return links.enumKind; - } - let hasNonLiteralMember = false; - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { - for (const member of ( declaration) - .members) - { - if (member.initializer - && isStringLiteralLike(member.initializer)) - { - return links.enumKind = EnumKind.Literal; - } - if (!isLiteralEnumMember(member)) { - hasNonLiteralMember = true; - } - } - } - } - return links.enumKind = hasNonLiteralMember - ? EnumKind.Numeric - : EnumKind.Literal; - } - - function getBaseTypeOfEnumLiteralType(type: Type) { - return type.flags & TypeFlags.EnumLiteral - && !(type.flags & TypeFlags.Union) - ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)!) - : type; - } - - function getDeclaredTypeOfEnum(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (links.declaredType) { - return links.declaredType; - } - if (getEnumKind(symbol) === EnumKind.Literal) { - enumCount++; - const memberTypeList: Type[] = []; - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { - for (const member of ( declaration) - .members) - { - const value = getEnumMemberValue(member); - const memberType = - getFreshTypeOfLiteralType(getLiteralType( - value !== undefined ? value : 0, - enumCount, - getSymbolOfNode(member) - )); - getSymbolLinks(getSymbolOfNode(member)) - .declaredType = memberType; - memberTypeList - .push(getRegularTypeOfLiteralType(memberType)); - } - } - } - if (memberTypeList.length) { - const enumType = getUnionType( - memberTypeList, - UnionReduction.Literal, - symbol, /*aliasTypeArguments*/ - undefined - ); - if (enumType.flags & TypeFlags.Union) { - enumType.flags |= TypeFlags.EnumLiteral; - enumType.symbol = symbol; - } - return links.declaredType = enumType; - } - } - const enumType = createType(TypeFlags.Enum); - enumType.symbol = symbol; - return links.declaredType = enumType; - } - - function getDeclaredTypeOfEnumMember(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - if (!links.declaredType) { - const enumType = - getDeclaredTypeOfEnum(getParentOfSymbol(symbol)!); - if (!links.declaredType) { - links.declaredType = enumType; - } - } - return links.declaredType; - } - - function getDeclaredTypeOfTypeParameter(symbol: - Symbol): TypeParameter - { - const links = getSymbolLinks(symbol); - return links.declaredType - || (links.declaredType = createTypeParameter(symbol)); - } - - function getDeclaredTypeOfAlias(symbol: Symbol): Type { - const links = getSymbolLinks(symbol); - return links.declaredType - || (links - .declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol))); - } - - function getDeclaredTypeOfSymbol(symbol: Symbol): Type { - return tryGetDeclaredTypeOfSymbol(symbol) || errorType; - } - - function tryGetDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined { - if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - return getDeclaredTypeOfClassOrInterface(symbol); - } - if (symbol.flags & SymbolFlags.TypeAlias) { - return getDeclaredTypeOfTypeAlias(symbol); - } - if (symbol.flags & SymbolFlags.TypeParameter) { - return getDeclaredTypeOfTypeParameter(symbol); - } - if (symbol.flags & SymbolFlags.Enum) { - return getDeclaredTypeOfEnum(symbol); - } - if (symbol.flags & SymbolFlags.EnumMember) { - return getDeclaredTypeOfEnumMember(symbol); - } - if (symbol.flags & SymbolFlags.Alias) { - return getDeclaredTypeOfAlias(symbol); - } - return undefined; - } - - /** - * A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string - * literal type, an array with an element type that is free of this references, or a type reference that is - * free of this references. - */ - function isThislessType(node: TypeNode): boolean { - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.UnknownKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.BigIntKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.ObjectKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.LiteralType: - return true; - case SyntaxKind.ArrayType: - return isThislessType(( node).elementType); - case SyntaxKind.TypeReference: - return !(node as TypeReferenceNode).typeArguments - || (node as TypeReferenceNode).typeArguments! - .every(isThislessType); - } - return false; - } - - /** A type parameter is thisless if its constraint is thisless, or if it has no constraint. */ - function isThislessTypeParameter(node: TypeParameterDeclaration) { - const constraint = getEffectiveConstraintOfTypeParameter(node); - return !constraint || isThislessType(constraint); - } - - /** - * A variable-like declaration is free of this references if it has a type annotation - * that is thisless, or if it has no type annotation and no initializer (and is thus of type any). - */ - function isThislessVariableLikeDeclaration(node: - VariableLikeDeclaration): boolean - { - const typeNode = getEffectiveTypeAnnotationNode(node); - return typeNode ? isThislessType(typeNode) : !hasInitializer(node); - } - - /** - * A function-like declaration is considered free of `this` references if it has a return type - * annotation that is free of this references and if each parameter is thisless and if - * each type parameter (if present) is thisless. - */ - function isThislessFunctionLikeDeclaration(node: - FunctionLikeDeclaration): boolean - { - const returnType = getEffectiveReturnTypeNode(node); - const typeParameters = getEffectiveTypeParameterDeclarations(node); - return (node.kind === SyntaxKind.Constructor - || (!!returnType && isThislessType(returnType))) - && node.parameters.every(isThislessVariableLikeDeclaration) - && typeParameters.every(isThislessTypeParameter); - } - - /** - * Returns true if the class or interface member given by the symbol is free of "this" references. The - * function may return false for symbols that are actually free of "this" references because it is not - * feasible to perform a complete analysis in all cases. In particular, property members with types - * inferred from their initializers and function members with inferred return types are conservatively - * assumed not to be free of "this" references. - */ - function isThisless(symbol: Symbol): boolean { - if (symbol.declarations && symbol.declarations.length === 1) { - const declaration = symbol.declarations[0]; - if (declaration) { - switch (declaration.kind) { - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - return isThislessVariableLikeDeclaration( declaration); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return isThislessFunctionLikeDeclaration( declaration); - } - } - } - return false; - } - - // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, - // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. - function createInstantiatedSymbolTable( - symbols: Symbol[], - mapper: TypeMapper, - mappingThisOnly: boolean - ): SymbolTable { - const result = createSymbolTable(); - for (const symbol of symbols) { - result.set( - symbol.escapedName, - mappingThisOnly && isThisless(symbol) - ? symbol - : instantiateSymbol(symbol, mapper) - ); - } - return result; - } - - function addInheritedMembers( - symbols: SymbolTable, - baseSymbols: Symbol[] - ) { - for (const s of baseSymbols) { - if (!symbols.has(s.escapedName) - && !isStaticPrivateIdentifierProperty(s)) - { - symbols.set(s.escapedName, s); - } - } - } - - function isStaticPrivateIdentifierProperty(s: Symbol): boolean { - return !!s.valueDeclaration - && isPrivateIdentifierPropertyDeclaration(s.valueDeclaration) - && hasModifier(s.valueDeclaration, ModifierFlags.Static); - } - - function resolveDeclaredMembers(type: - InterfaceType): InterfaceTypeWithDeclaredMembers - { - if (!( type) - .declaredProperties) - { - const symbol = type.symbol; - const members = getMembersOfSymbol(symbol); - ( type) - .declaredProperties = getNamedMembers(members); - // Start with signatures at empty array in case of recursive types - ( type) - .declaredCallSignatures = emptyArray; - ( type) - .declaredConstructSignatures = emptyArray; - - ( type) - .declaredCallSignatures = getSignaturesOfSymbol(members - .get(InternalSymbolName.Call)); - ( type) - .declaredConstructSignatures = getSignaturesOfSymbol(members - .get(InternalSymbolName.New)); - ( type) - .declaredStringIndexInfo = getIndexInfoOfSymbol( - symbol, - IndexKind.String - ); - ( type) - .declaredNumberIndexInfo = getIndexInfoOfSymbol( - symbol, - IndexKind.Number - ); - } - return type; - } - - /** - * Indicates whether a type can be used as a property name. - */ - function isTypeUsableAsPropertyName(type: - Type - ): type is StringLiteralType | NumberLiteralType | UniqueESSymbolType { - return !!(type.flags & TypeFlags.StringOrNumberLiteralOrUnique); - } - - /** - * Indicates whether a declaration name is definitely late-bindable. - * A declaration name is only late-bindable if: - * - It is a `ComputedPropertyName`. - * - Its expression is an `Identifier` or either a `PropertyAccessExpression` an - * `ElementAccessExpression` consisting only of these same three types of nodes. - * - The type of its expression is a string or numeric literal type, or is a `unique symbol` type. - */ - function isLateBindableName(node: - DeclarationName): node is LateBoundName - { - if (!isComputedPropertyName(node) - && !isElementAccessExpression(node)) - { - return false; - } - const expr = isComputedPropertyName(node) - ? node.expression - : node.argumentExpression; - return isEntityNameExpression(expr) - && isTypeUsableAsPropertyName(isComputedPropertyName(node) - ? checkComputedPropertyName(node) - : checkExpressionCached(expr)); - } - - function isLateBoundName(name: __String): boolean { - return (name as string).charCodeAt(0) === CharacterCodes._ - && (name as string).charCodeAt(1) === CharacterCodes._ - && (name as string).charCodeAt(2) === CharacterCodes.at; - } - - /** - * Indicates whether a declaration has a late-bindable dynamic name. - */ - function hasLateBindableName(node: - Declaration - ): node is LateBoundDeclaration - | LateBoundBinaryExpressionDeclaration - { - const name = getNameOfDeclaration(node); - return !!name && isLateBindableName(name); - } - - /** - * Indicates whether a declaration has a dynamic name that cannot be late-bound. - */ - function hasNonBindableDynamicName(node: Declaration) { - return hasDynamicName(node) && !hasLateBindableName(node); - } - - /** - * Indicates whether a declaration name is a dynamic name that cannot be late-bound. - */ - function isNonBindableDynamicName(node: DeclarationName) { - return isDynamicName(node) && !isLateBindableName(node); - } - - /** - * Gets the symbolic name for a member from its type. - */ - function getPropertyNameFromType(type: StringLiteralType - | NumberLiteralType | UniqueESSymbolType): __String - { - if (type.flags & TypeFlags.UniqueESSymbol) { - return ( type).escapedName; - } - if (type.flags - & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) - { - return escapeLeadingUnderscores('' - + ( type).value); - } - return Debug.fail(); - } - - /** - * Adds a declaration to a late-bound dynamic member. This performs the same function for - * late-bound members that `addDeclarationToSymbol` in binder.ts performs for early-bound - * members. - */ - function addDeclarationToLateBoundSymbol( - symbol: Symbol, - member: LateBoundDeclaration | BinaryExpression, - symbolFlags: SymbolFlags - ) { - Debug.assert( - !!(getCheckFlags(symbol) & CheckFlags.Late), - 'Expected a late-bound symbol.' - ); - symbol.flags |= symbolFlags; - getSymbolLinks(member.symbol).lateSymbol = symbol; - if (!symbol.declarations) { - symbol.declarations = [member]; - } else { - symbol.declarations.push(member); - } - if (symbolFlags & SymbolFlags.Value) { - if (!symbol.valueDeclaration - || symbol.valueDeclaration.kind !== member.kind) - { - symbol.valueDeclaration = member; - } - } - } - - /** - * Performs late-binding of a dynamic member. This performs the same function for - * late-bound members that `declareSymbol` in binder.ts performs for early-bound - * members. - * - * If a symbol is a dynamic name from a computed property, we perform an additional "late" - * binding phase to attempt to resolve the name for the symbol from the type of the computed - * property's expression. If the type of the expression is a string-literal, numeric-literal, - * or unique symbol type, we can use that type as the name of the symbol. - * - * For example, given: - * - * const x = Symbol(); - * - * interface I { - * [x]: number; - * } - * - * The binder gives the property `[x]: number` a special symbol with the name "__computed". - * In the late-binding phase we can type-check the expression `x` and see that it has a - * unique symbol type which we can then use as the name of the member. This allows users - * to define custom symbols that can be used in the members of an object type. - * - * @param parent The containing symbol for the member. - * @param earlySymbols The early-bound symbols of the parent. - * @param lateSymbols The late-bound symbols of the parent. - * @param decl The member to bind. - */ - function lateBindMember( - parent: Symbol, - earlySymbols: SymbolTable | undefined, - lateSymbols: SymbolTable, - decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration - ) { - Debug.assert( - !!decl.symbol, - 'The member is expected to have a symbol.' - ); - const links = getNodeLinks(decl); - if (!links.resolvedSymbol) { - // In the event we attempt to resolve the late-bound name of this member recursively, - // fall back to the early-bound name of this member. - links.resolvedSymbol = decl.symbol; - const declName = isBinaryExpression(decl) - ? decl.left - : decl.name; - const type = isElementAccessExpression(declName) - ? checkExpressionCached(declName.argumentExpression) - : checkComputedPropertyName(declName); - if (isTypeUsableAsPropertyName(type)) { - const memberName = getPropertyNameFromType(type); - const symbolFlags = decl.symbol.flags; - - // Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations. - let lateSymbol = lateSymbols.get(memberName); - if (!lateSymbol) { - lateSymbols.set( - memberName, - lateSymbol = createSymbol( - SymbolFlags.None, - memberName, - CheckFlags.Late - ) - ); - } - - // Report an error if a late-bound member has the same name as an early-bound member, - // or if we have another early-bound symbol declaration with the same name and - // conflicting flags. - const earlySymbol = earlySymbols - && earlySymbols.get(memberName); - if (lateSymbol.flags & getExcludedSymbolFlags(symbolFlags) - || earlySymbol) - { - // If we have an existing early-bound member, combine its declarations so that we can - // report an error at each declaration. - const declarations = earlySymbol - ? concatenate( - earlySymbol.declarations, - lateSymbol.declarations - ) - : lateSymbol.declarations; - const name = !(type.flags & TypeFlags.UniqueESSymbol) - && unescapeLeadingUnderscores(memberName) - || declarationNameToString(declName); - forEach( - declarations, - declaration => error( - getNameOfDeclaration(declaration) - || declaration, - Diagnostics.Property_0_was_also_declared_here, - name - ) - ); - error( - declName || decl, - Diagnostics.Duplicate_property_0, - name - ); - lateSymbol = createSymbol( - SymbolFlags.None, - memberName, - CheckFlags.Late - ); - } - lateSymbol.nameType = type; - addDeclarationToLateBoundSymbol( - lateSymbol, - decl, - symbolFlags - ); - if (lateSymbol.parent) { - Debug.assert( - lateSymbol.parent === parent, - 'Existing symbol parent should match new one' - ); - } else { - lateSymbol.parent = parent; - } - return links.resolvedSymbol = lateSymbol; - } - } - return links.resolvedSymbol; - } - - function getResolvedMembersOrExportsOfSymbol( - symbol: Symbol, - resolutionKind: MembersOrExportsResolutionKind - ): UnderscoreEscapedMap { - const links = getSymbolLinks(symbol); - if (!links[resolutionKind]) { - const isStatic = - resolutionKind - === MembersOrExportsResolutionKind.resolvedExports; - const earlySymbols = !isStatic - ? symbol.members - : symbol.flags & SymbolFlags.Module - ? getExportsOfModuleWorker(symbol) - : symbol.exports; - - // In the event we recursively resolve the members/exports of the symbol, we - // set the initial value of resolvedMembers/resolvedExports to the early-bound - // members/exports of the symbol. - links[resolutionKind] = earlySymbols || emptySymbols; - - // fill in any as-yet-unresolved late-bound members. - const lateSymbols = createSymbolTable(); - for (const decl of symbol.declarations) { - const members = getMembersOfDeclaration(decl); - if (members) { - for (const member of members) { - if (isStatic === hasStaticModifier(member) - && hasLateBindableName(member)) - { - lateBindMember( - symbol, - earlySymbols, - lateSymbols, - member - ); - } - } - } - } - const assignments = symbol.assignmentDeclarationMembers; - if (assignments) { - const decls = arrayFrom(assignments.values()); - for (const member of decls) { - const assignmentKind = - getAssignmentDeclarationKind(member as BinaryExpression - | CallExpression); - const isInstanceMember = - assignmentKind - === AssignmentDeclarationKind.PrototypeProperty - || assignmentKind - === AssignmentDeclarationKind.ThisProperty - || assignmentKind - === AssignmentDeclarationKind - .ObjectDefinePrototypeProperty - || assignmentKind - === AssignmentDeclarationKind - .Prototype; // A straight `Prototype` assignment probably can never have a computed name - if (isStatic === !isInstanceMember - && hasLateBindableName(member)) - { - lateBindMember( - symbol, - earlySymbols, - lateSymbols, - member - ); - } - } - } - - links - [resolutionKind] = combineSymbolTables( - earlySymbols, - lateSymbols - ) || emptySymbols; - } - - return links[resolutionKind]!; - } - - /** - * Gets a SymbolTable containing both the early- and late-bound members of a symbol. - * - * For a description of late-binding, see `lateBindMember`. - */ - function getMembersOfSymbol(symbol: Symbol) { - return symbol.flags & SymbolFlags.LateBindingContainer - ? getResolvedMembersOrExportsOfSymbol( - symbol, - MembersOrExportsResolutionKind.resolvedMembers - ) - : symbol.members || emptySymbols; - } - - /** - * If a symbol is the dynamic name of the member of an object type, get the late-bound - * symbol of the member. - * - * For a description of late-binding, see `lateBindMember`. - */ - function getLateBoundSymbol(symbol: Symbol): Symbol { - if (symbol.flags & SymbolFlags.ClassMember - && symbol.escapedName === InternalSymbolName.Computed) - { - const links = getSymbolLinks(symbol); - if (!links.lateSymbol - && some(symbol.declarations, hasLateBindableName)) - { - // force late binding of members/exports. This will set the late-bound symbol - const parent = getMergedSymbol(symbol.parent)!; - if (some(symbol.declarations, hasStaticModifier)) { - getExportsOfSymbol(parent); - } else { - getMembersOfSymbol(parent); - } - } - return links.lateSymbol || (links.lateSymbol = symbol); - } - return symbol; - } - - function getTypeWithThisArgument( - type: Type, - thisArgument?: Type, - needApparentType?: boolean - ): Type { - if (getObjectFlags(type) & ObjectFlags.Reference) { - const target = ( type).target; - const typeArguments = getTypeArguments( type); - if (length(target.typeParameters) === length(typeArguments)) { - const ref = createTypeReference( - target, - concatenate( - typeArguments, - [thisArgument || target.thisType!] - ) - ); - return needApparentType ? getApparentType(ref) : ref; - } - } else if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map( - ( type).types, - t => getTypeWithThisArgument( - t, - thisArgument, - needApparentType - ) - )); - } - return needApparentType ? getApparentType(type) : type; - } - - function resolveObjectTypeMembers( - type: ObjectType, - source: InterfaceTypeWithDeclaredMembers, - typeParameters: readonly TypeParameter[], - typeArguments: readonly Type[] - ) { - let mapper: TypeMapper; - let members: SymbolTable; - let callSignatures: readonly Signature[]; - let constructSignatures: readonly Signature[] | undefined; - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - if (rangeEquals( - typeParameters, - typeArguments, - 0, - typeParameters.length - )) { - mapper = identityMapper; - members = source.symbol - ? getMembersOfSymbol(source.symbol) - : createSymbolTable(source.declaredProperties); - callSignatures = source.declaredCallSignatures; - constructSignatures = source.declaredConstructSignatures; - stringIndexInfo = source.declaredStringIndexInfo; - numberIndexInfo = source.declaredNumberIndexInfo; - } else { - mapper = createTypeMapper(typeParameters, typeArguments); - members = createInstantiatedSymbolTable( - source.declaredProperties, - mapper, /*mappingThisOnly*/ - typeParameters.length === 1 - ); - callSignatures = instantiateSignatures( - source.declaredCallSignatures, - mapper - ); - constructSignatures = instantiateSignatures( - source.declaredConstructSignatures, - mapper - ); - stringIndexInfo = instantiateIndexInfo( - source.declaredStringIndexInfo, - mapper - ); - numberIndexInfo = instantiateIndexInfo( - source.declaredNumberIndexInfo, - mapper - ); - } - const baseTypes = getBaseTypes(source); - if (baseTypes.length) { - if (source.symbol - && members === getMembersOfSymbol(source.symbol)) - { - members = createSymbolTable(source.declaredProperties); - } - setStructuredTypeMembers( - type, - members, - callSignatures, - constructSignatures, - stringIndexInfo, - numberIndexInfo - ); - const thisArgument = lastOrUndefined(typeArguments); - for (const baseType of baseTypes) { - const instantiatedBaseType = thisArgument - ? getTypeWithThisArgument( - instantiateType(baseType, mapper), - thisArgument - ) - : baseType; - addInheritedMembers( - members, - getPropertiesOfType(instantiatedBaseType) - ); - callSignatures = concatenate( - callSignatures, - getSignaturesOfType( - instantiatedBaseType, - SignatureKind.Call - ) - ); - constructSignatures = concatenate( - constructSignatures, - getSignaturesOfType( - instantiatedBaseType, - SignatureKind.Construct - ) - ); - if (!stringIndexInfo) { - stringIndexInfo = instantiatedBaseType === anyType - ? createIndexInfo(anyType, /*isReadonly*/ false) - : getIndexInfoOfType( - instantiatedBaseType, - IndexKind.String - ); - } - numberIndexInfo = numberIndexInfo - || getIndexInfoOfType( - instantiatedBaseType, - IndexKind.Number - ); - } - } - setStructuredTypeMembers( - type, - members, - callSignatures, - constructSignatures, - stringIndexInfo, - numberIndexInfo - ); - } - - function resolveClassOrInterfaceMembers(type: InterfaceType): void { - resolveObjectTypeMembers( - type, - resolveDeclaredMembers(type), - emptyArray, - emptyArray - ); - } - - function resolveTypeReferenceMembers(type: TypeReference): void { - const source = resolveDeclaredMembers(type.target); - const typeParameters = concatenate( - source.typeParameters!, - [source.thisType!] - ); - const typeArguments = getTypeArguments(type); - const paddedTypeArguments = - typeArguments.length === typeParameters.length - ? typeArguments - : concatenate(typeArguments, [type]); - resolveObjectTypeMembers( - type, - source, - typeParameters, - paddedTypeArguments - ); - } - - function createSignature( - declaration: SignatureDeclaration | JSDocSignature | undefined, - typeParameters: readonly TypeParameter[] | undefined, - thisParameter: Symbol | undefined, - parameters: readonly Symbol[], - resolvedReturnType: Type | undefined, - resolvedTypePredicate: TypePredicate | undefined, - minArgumentCount: number, - flags: SignatureFlags - ): Signature { - const sig = new Signature(checker, flags); - sig.declaration = declaration; - sig.typeParameters = typeParameters; - sig.parameters = parameters; - sig.thisParameter = thisParameter; - sig.resolvedReturnType = resolvedReturnType; - sig.resolvedTypePredicate = resolvedTypePredicate; - sig.minArgumentCount = minArgumentCount; - sig.target = undefined; - sig.mapper = undefined; - sig.unionSignatures = undefined; - return sig; - } - - function cloneSignature(sig: Signature): Signature { - const result = createSignature( - sig.declaration, - sig.typeParameters, - sig.thisParameter, - sig.parameters, /*resolvedReturnType*/ - undefined, - /*resolvedTypePredicate*/ undefined, - sig.minArgumentCount, - sig.flags & SignatureFlags.PropagatingFlags - ); - result.target = sig.target; - result.mapper = sig.mapper; - result.unionSignatures = sig.unionSignatures; - return result; - } - - function createUnionSignature( - signature: Signature, - unionSignatures: Signature[] - ) { - const result = cloneSignature(signature); - result.unionSignatures = unionSignatures; - result.target = undefined; - result.mapper = undefined; - return result; - } - - function getOptionalCallSignature( - signature: Signature, - callChainFlags: SignatureFlags - ): Signature { - if ((signature.flags & SignatureFlags.CallChainFlags) - === callChainFlags) - { - return signature; - } - if (!signature.optionalCallSignatureCache) { - signature.optionalCallSignatureCache = {}; - } - const key = callChainFlags === SignatureFlags.IsInnerCallChain - ? 'inner' - : 'outer'; - return signature.optionalCallSignatureCache[key] - || (signature.optionalCallSignatureCache - [key] = createOptionalCallSignature( - signature, - callChainFlags - )); - } - - function createOptionalCallSignature( - signature: Signature, - callChainFlags: SignatureFlags - ) { - Debug.assert( - callChainFlags === SignatureFlags.IsInnerCallChain - || callChainFlags === SignatureFlags.IsOuterCallChain, - 'An optional call signature can either be for an inner call chain or an outer call chain, but not both.' - ); - const result = cloneSignature(signature); - result.flags |= callChainFlags; - return result; - } - - function getExpandedParameters(sig: Signature): readonly Symbol[] { - if (signatureHasRestParameter(sig)) { - const restIndex = sig.parameters.length - 1; - const restParameter = sig.parameters[restIndex]; - const restType = getTypeOfSymbol(restParameter); - if (isTupleType(restType)) { - const elementTypes = getTypeArguments(restType); - const minLength = restType.target.minLength; - const tupleRestIndex = restType.target.hasRestElement - ? elementTypes.length - 1 - : -1; - const restParams = map(elementTypes, (t, i) => { - const name = getParameterNameAtPosition( - sig, - restIndex + i - ); - const checkFlags = i === tupleRestIndex - ? CheckFlags.RestParameter - : i >= minLength - ? CheckFlags.OptionalParameter - : 0; - const symbol = createSymbol( - SymbolFlags.FunctionScopedVariable, - name, - checkFlags - ); - symbol.type = i === tupleRestIndex - ? createArrayType(t) - : t; - return symbol; - }); - return concatenate( - sig.parameters.slice(0, restIndex), - restParams - ); - } - } - return sig.parameters; - } - - function getDefaultConstructSignatures(classType: - InterfaceType): Signature[] - { - const baseConstructorType = - getBaseConstructorTypeOfClass(classType); - const baseSignatures = getSignaturesOfType( - baseConstructorType, - SignatureKind.Construct - ); - if (baseSignatures.length === 0) { - return [createSignature( - undefined, - classType.localTypeParameters, - undefined, - emptyArray, - classType, /*resolvedTypePredicate*/ - undefined, - 0, - SignatureFlags.None - )]; - } - const baseTypeNode = getBaseTypeNodeOfClass(classType)!; - const isJavaScript = isInJSFile(baseTypeNode); - const typeArguments = - typeArgumentsFromTypeReferenceNode(baseTypeNode); - const typeArgCount = length(typeArguments); - const result: Signature[] = []; - for (const baseSig of baseSignatures) { - const minTypeArgumentCount = - getMinTypeArgumentCount(baseSig.typeParameters); - const typeParamCount = length(baseSig.typeParameters); - if (isJavaScript || typeArgCount >= minTypeArgumentCount - && typeArgCount <= typeParamCount) - { - const sig = typeParamCount - ? createSignatureInstantiation( - baseSig, - fillMissingTypeArguments( - typeArguments, - baseSig.typeParameters, - minTypeArgumentCount, - isJavaScript - ) - ) - : cloneSignature(baseSig); - sig.typeParameters = classType.localTypeParameters; - sig.resolvedReturnType = classType; - result.push(sig); - } - } - return result; - } - - function findMatchingSignature( - signatureList: readonly Signature[], - signature: Signature, - partialMatch: boolean, - ignoreThisTypes: boolean, - ignoreReturnTypes: boolean - ): Signature | undefined { - for (const s of signatureList) { - if (compareSignaturesIdentical( - s, - signature, - partialMatch, - ignoreThisTypes, - ignoreReturnTypes, - partialMatch - ? compareTypesSubtypeOf - : compareTypesIdentical - )) { - return s; - } - } - } - - function findMatchingSignatures( - signatureLists: readonly (readonly Signature[])[], - signature: Signature, - listIndex: number - ): Signature[] | undefined { - if (signature.typeParameters) { - // We require an exact match for generic signatures, so we only return signatures from the first - // signature list and only if they have exact matches in the other signature lists. - if (listIndex > 0) { - return undefined; - } - for (let i = 1; i < signatureLists.length; i++) { - if (!findMatchingSignature( - signatureLists[i], - signature, /*partialMatch*/ - false, /*ignoreThisTypes*/ - false, /*ignoreReturnTypes*/ - false - )) { - return undefined; - } - } - return [signature]; - } - let result: Signature[] | undefined; - for (let i = 0; i < signatureLists.length; i++) { - // Allow matching non-generic signatures to have excess parameters and different return types. - // Prefer matching this types if possible. - const match = i === listIndex - ? signature - : findMatchingSignature( - signatureLists[i], - signature, /*partialMatch*/ - true, /*ignoreThisTypes*/ - false, /*ignoreReturnTypes*/ - true - ); - if (!match) { - return undefined; - } - result = appendIfUnique(result, match); - } - return result; - } - - // The signatures of a union type are those signatures that are present in each of the constituent types. - // Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional - // parameters and may differ in return types. When signatures differ in return types, the resulting return - // type is the union of the constituent return types. - function getUnionSignatures(signatureLists: - readonly (readonly Signature[])[]): Signature[] - { - let result: Signature[] | undefined; - let indexWithLengthOverOne: number | undefined; - for (let i = 0; i < signatureLists.length; i++) { - if (signatureLists[i].length === 0) return emptyArray; - if (signatureLists[i].length > 1) { - indexWithLengthOverOne = indexWithLengthOverOne - === undefined - ? i - : -1; // -1 is a signal there are multiple overload sets - } - for (const signature of signatureLists[i]) { - // Only process signatures with parameter lists that aren't already in the result list - if (!result - || !findMatchingSignature( - result, - signature, /*partialMatch*/ - false, /*ignoreThisTypes*/ - false, /*ignoreReturnTypes*/ - true - )) - { - const unionSignatures = findMatchingSignatures( - signatureLists, - signature, - i - ); - if (unionSignatures) { - let s = signature; - // Union the result types when more than one signature matches - if (unionSignatures.length > 1) { - let thisParameter = signature.thisParameter; - const firstThisParameterOfUnionSignatures = - forEach( - unionSignatures, - sig => sig.thisParameter - ); - if (firstThisParameterOfUnionSignatures) { - const thisType = - getIntersectionType(mapDefined( - unionSignatures, - sig => sig.thisParameter - && getTypeOfSymbol(sig - .thisParameter) - )); - thisParameter = createSymbolWithType( - firstThisParameterOfUnionSignatures, - thisType - ); - } - s = createUnionSignature( - signature, - unionSignatures - ); - s.thisParameter = thisParameter; - } - (result || (result = [])).push(s); - } - } - } - } - if (!length(result) && indexWithLengthOverOne !== -1) { - // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single - // signature that handles all over them. We only do this when there are overloads in only one constituent. - // (Overloads are conditional in nature and having overloads in multiple constituents would necessitate making a power set of - // signatures from the type, whose ordering would be non-obvious) - const masterList = signatureLists - [indexWithLengthOverOne !== undefined - ? indexWithLengthOverOne - : 0]; - let results: Signature[] | undefined = masterList.slice(); - for (const signatures of signatureLists) { - if (signatures !== masterList) { - const signature = signatures[0]; - Debug.assert( - !!signature, - 'getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass' - ); - results = signature.typeParameters - && some(results, s => !!s.typeParameters) - ? undefined - : map( - results, - sig => combineSignaturesOfUnionMembers( - sig, - signature - ) - ); - if (!results) { - break; - } - } - } - result = results; - } - return result || emptyArray; - } - - function combineUnionThisParam( - left: Symbol | undefined, - right: Symbol | undefined - ): Symbol | undefined { - if (!left || !right) { - return left || right; - } - // A signature `this` type might be a read or a write position... It's very possible that it should be invariant - // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be - // permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures. - const thisType = - getIntersectionType([getTypeOfSymbol(left), - getTypeOfSymbol(right)]); - return createSymbolWithType(left, thisType); - } - - function combineUnionParameters(left: Signature, right: Signature) { - const leftCount = getParameterCount(left); - const rightCount = getParameterCount(right); - const longest = leftCount >= rightCount ? left : right; - const shorter = longest === left ? right : left; - const longestCount = longest === left ? leftCount : rightCount; - const eitherHasEffectiveRest = - (hasEffectiveRestParameter(left) - || hasEffectiveRestParameter(right)); - const needsExtraRestElement = eitherHasEffectiveRest - && !hasEffectiveRestParameter(longest); - const params = - new Array(longestCount - + (needsExtraRestElement ? 1 : 0)); - for (let i = 0; i < longestCount; i++) { - const longestParamType = tryGetTypeAtPosition(longest, i)!; - const shorterParamType = tryGetTypeAtPosition(shorter, i) - || unknownType; - const unionParamType = - getIntersectionType([longestParamType, shorterParamType]); - const isRestParam = eitherHasEffectiveRest - && !needsExtraRestElement && i === (longestCount - 1); - const isOptional = i >= getMinArgumentCount(longest) - && i >= getMinArgumentCount(shorter); - const leftName = i >= leftCount - ? undefined - : getParameterNameAtPosition(left, i); - const rightName = i >= rightCount - ? undefined - : getParameterNameAtPosition(right, i); - - const paramName = leftName === rightName - ? leftName - : !leftName - ? rightName - : !rightName - ? leftName - : undefined; - const paramSymbol = createSymbol( - SymbolFlags.FunctionScopedVariable - | (isOptional && !isRestParam - ? SymbolFlags.Optional - : 0), - paramName || `arg${i}` as __String - ); - paramSymbol.type = isRestParam - ? createArrayType(unionParamType) - : unionParamType; - params[i] = paramSymbol; - } - if (needsExtraRestElement) { - const restParamSymbol = createSymbol( - SymbolFlags.FunctionScopedVariable, - 'args' as __String - ); - restParamSymbol - .type = createArrayType(getTypeAtPosition( - shorter, - longestCount - )); - params[longestCount] = restParamSymbol; - } - return params; - } - - function combineSignaturesOfUnionMembers( - left: Signature, - right: Signature - ): Signature { - const declaration = left.declaration; - const params = combineUnionParameters(left, right); - const thisParam = combineUnionThisParam( - left.thisParameter, - right.thisParameter - ); - const minArgCount = Math.max( - left.minArgumentCount, - right.minArgumentCount - ); - const result = createSignature( - declaration, - left.typeParameters || right.typeParameters, - thisParam, - params, - /*resolvedReturnType*/ undefined, - /*resolvedTypePredicate*/ undefined, - minArgCount, - (left.flags | right.flags) & SignatureFlags.PropagatingFlags - ); - result.unionSignatures = concatenate( - left.unionSignatures || [left], - [right] - ); - return result; - } - - function getUnionIndexInfo( - types: readonly Type[], - kind: IndexKind - ): IndexInfo | undefined { - const indexTypes: Type[] = []; - let isAnyReadonly = false; - for (const type of types) { - const indexInfo = getIndexInfoOfType(type, kind); - if (!indexInfo) { - return undefined; - } - indexTypes.push(indexInfo.type); - isAnyReadonly = isAnyReadonly || indexInfo.isReadonly; - } - return createIndexInfo( - getUnionType(indexTypes, UnionReduction.Subtype), - isAnyReadonly - ); - } - - function resolveUnionTypeMembers(type: UnionType) { - // The members and properties collections are empty for union types. To get all properties of a union - // type use getPropertiesOfType (only the language service uses this). - const callSignatures = - getUnionSignatures(map( - type.types, - t => t === globalFunctionType - ? [unknownSignature] - : getSignaturesOfType(t, SignatureKind.Call) - )); - const constructSignatures = - getUnionSignatures(map( - type.types, - t => getSignaturesOfType(t, SignatureKind.Construct) - )); - const stringIndexInfo = getUnionIndexInfo( - type.types, - IndexKind.String - ); - const numberIndexInfo = getUnionIndexInfo( - type.types, - IndexKind.Number - ); - setStructuredTypeMembers( - type, - emptySymbols, - callSignatures, - constructSignatures, - stringIndexInfo, - numberIndexInfo - ); - } - - function intersectTypes(type1: Type, type2: Type): Type; - function intersectTypes( - type1: Type | undefined, - type2: Type | undefined - ): Type | undefined; - function intersectTypes( - type1: Type | undefined, - type2: Type | undefined - ): Type | undefined { - return !type1 - ? type2 - : !type2 ? type1 : getIntersectionType([type1, type2]); - } - - function intersectIndexInfos( - info1: IndexInfo | undefined, - info2: IndexInfo | undefined - ): IndexInfo | undefined { - return !info1 ? info2 : !info2 ? info1 : createIndexInfo( - getIntersectionType([info1.type, info2.type]), - info1.isReadonly && info2.isReadonly - ); - } - - function unionSpreadIndexInfos( - info1: IndexInfo | undefined, - info2: IndexInfo | undefined - ): IndexInfo | undefined { - return info1 && info2 && createIndexInfo( - getUnionType([info1.type, info2.type]), - info1.isReadonly || info2.isReadonly - ); - } - - function findMixins(types: readonly Type[]): readonly boolean[] { - const constructorTypeCount = countWhere( - types, - (t) => getSignaturesOfType(t, SignatureKind.Construct).length - > 0 - ); - const mixinFlags = map(types, isMixinConstructorType); - if (constructorTypeCount > 0 - && constructorTypeCount === countWhere(mixinFlags, (b) => b)) - { - const firstMixinIndex = mixinFlags - .indexOf(/*searchElement*/ true); - mixinFlags[firstMixinIndex] = false; - } - return mixinFlags; - } - - function includeMixinType( - type: Type, - types: readonly Type[], - mixinFlags: readonly boolean[], - index: number - ): Type { - const mixedTypes: Type[] = []; - for (let i = 0; i < types.length; i++) { - if (i === index) { - mixedTypes.push(type); - } else if (mixinFlags[i]) { - mixedTypes - .push(getReturnTypeOfSignature(getSignaturesOfType( - types[i], - SignatureKind.Construct - )[0])); - } - } - return getIntersectionType(mixedTypes); - } - - function resolveIntersectionTypeMembers(type: IntersectionType) { - // The members and properties collections are empty for intersection types. To get all properties of an - // intersection type use getPropertiesOfType (only the language service uses this). - let callSignatures: Signature[] | undefined; - let constructSignatures: Signature[] | undefined; - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - const types = type.types; - const mixinFlags = findMixins(types); - const mixinCount = countWhere(mixinFlags, (b) => b); - for (let i = 0; i < types.length; i++) { - const t = type.types[i]; - // When an intersection type contains mixin constructor types, the construct signatures from - // those types are discarded and their return types are mixed into the return types of all - // other construct signatures in the intersection type. For example, the intersection type - // '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature - // 'new(s: string) => A & B'. - if (!mixinFlags[i]) { - let signatures = getSignaturesOfType( - t, - SignatureKind.Construct - ); - if (signatures.length && mixinCount > 0) { - signatures = map(signatures, s => { - const clone = cloneSignature(s); - clone.resolvedReturnType = includeMixinType( - getReturnTypeOfSignature(s), - types, - mixinFlags, - i - ); - return clone; - }); - } - constructSignatures = appendSignatures( - constructSignatures, - signatures - ); - } - callSignatures = appendSignatures( - callSignatures, - getSignaturesOfType(t, SignatureKind.Call) - ); - stringIndexInfo = intersectIndexInfos( - stringIndexInfo, - getIndexInfoOfType(t, IndexKind.String) - ); - numberIndexInfo = intersectIndexInfos( - numberIndexInfo, - getIndexInfoOfType(t, IndexKind.Number) - ); - } - setStructuredTypeMembers( - type, - emptySymbols, - callSignatures || emptyArray, - constructSignatures || emptyArray, - stringIndexInfo, - numberIndexInfo - ); - } - - function appendSignatures( - signatures: Signature[] | undefined, - newSignatures: readonly Signature[] - ) { - for (const sig of newSignatures) { - if (!signatures - || every( - signatures, - s => !compareSignaturesIdentical( - s, - sig, /*partialMatch*/ - false, /*ignoreThisTypes*/ - false, /*ignoreReturnTypes*/ - false, - compareTypesIdentical - ) - )) - { - signatures = append(signatures, sig); - } - } - return signatures; - } - - /** - * Converts an AnonymousType to a ResolvedType. - */ - function resolveAnonymousTypeMembers(type: AnonymousType) { - const symbol = getMergedSymbol(type.symbol); - if (type.target) { - setStructuredTypeMembers( - type, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - const members = createInstantiatedSymbolTable( - getPropertiesOfObjectType(type.target), - type.mapper!, /*mappingThisOnly*/ - false - ); - const callSignatures = instantiateSignatures( - getSignaturesOfType(type.target, SignatureKind.Call), - type.mapper! - ); - const constructSignatures = instantiateSignatures( - getSignaturesOfType(type.target, SignatureKind.Construct), - type.mapper! - ); - const stringIndexInfo = instantiateIndexInfo( - getIndexInfoOfType(type.target, IndexKind.String), - type.mapper! - ); - const numberIndexInfo = instantiateIndexInfo( - getIndexInfoOfType(type.target, IndexKind.Number), - type.mapper! - ); - setStructuredTypeMembers( - type, - members, - callSignatures, - constructSignatures, - stringIndexInfo, - numberIndexInfo - ); - } else if (symbol.flags & SymbolFlags.TypeLiteral) { - setStructuredTypeMembers( - type, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - const members = getMembersOfSymbol(symbol); - const callSignatures = - getSignaturesOfSymbol(members - .get(InternalSymbolName.Call)); - const constructSignatures = - getSignaturesOfSymbol(members.get(InternalSymbolName.New)); - const stringIndexInfo = getIndexInfoOfSymbol( - symbol, - IndexKind.String - ); - const numberIndexInfo = getIndexInfoOfSymbol( - symbol, - IndexKind.Number - ); - setStructuredTypeMembers( - type, - members, - callSignatures, - constructSignatures, - stringIndexInfo, - numberIndexInfo - ); - } else { - // Combinations of function, class, enum and module - let members = emptySymbols; - let stringIndexInfo: IndexInfo | undefined; - if (symbol.exports) { - members = getExportsOfSymbol(symbol); - if (symbol === globalThisSymbol) { - const varsOnly = createMap() as SymbolTable; - members.forEach(p => { - if (!(p.flags & SymbolFlags.BlockScoped)) { - varsOnly.set(p.escapedName, p); - } - }); - members = varsOnly; - } - } - setStructuredTypeMembers( - type, - members, - emptyArray, - emptyArray, - undefined, - undefined - ); - if (symbol.flags & SymbolFlags.Class) { - const classType = - getDeclaredTypeOfClassOrInterface(symbol); - const baseConstructorType = - getBaseConstructorTypeOfClass(classType); - if (baseConstructorType.flags - & (TypeFlags.Object | TypeFlags.Intersection - | TypeFlags.TypeVariable)) - { - members = createSymbolTable(getNamedMembers(members)); - addInheritedMembers( - members, - getPropertiesOfType(baseConstructorType) - ); - } else if (baseConstructorType === anyType) { - stringIndexInfo = createIndexInfo( - anyType, /*isReadonly*/ - false - ); - } - } - const numberIndexInfo = - symbol.flags & SymbolFlags.Enum - && (getDeclaredTypeOfSymbol(symbol).flags - & TypeFlags.Enum - || some( - type.properties, - prop => !!(getTypeOfSymbol(prop).flags - & TypeFlags.NumberLike) - )) - ? enumNumberIndexInfo - : undefined; - setStructuredTypeMembers( - type, - members, - emptyArray, - emptyArray, - stringIndexInfo, - numberIndexInfo - ); - // We resolve the members before computing the signatures because a signature may use - // typeof with a qualified name expression that circularly references the type we are - // in the process of resolving (see issue #6072). The temporarily empty signature list - // will never be observed because a qualified name can't reference signatures. - if (symbol.flags - & (SymbolFlags.Function | SymbolFlags.Method)) - { - type.callSignatures = getSignaturesOfSymbol(symbol); - } - // And likewise for construct signatures for classes - if (symbol.flags & SymbolFlags.Class) { - const classType = - getDeclaredTypeOfClassOrInterface(symbol); - let constructSignatures = symbol.members - ? getSignaturesOfSymbol(symbol.members - .get(InternalSymbolName.Constructor)) - : emptyArray; - if (symbol.flags & SymbolFlags.Function) { - constructSignatures = addRange( - constructSignatures.slice(), - mapDefined( - type.callSignatures, - sig => isJSConstructor(sig.declaration) - ? createSignature( - sig.declaration, - sig.typeParameters, - sig.thisParameter, - sig.parameters, - classType, /*resolvedTypePredicate*/ - undefined, - sig.minArgumentCount, - sig.flags - & SignatureFlags.PropagatingFlags - ) - : undefined - ) - ); - } - if (!constructSignatures.length) { - constructSignatures = getDefaultConstructSignatures(classType); - } - type.constructSignatures = constructSignatures; - } - } - } - - function resolveReverseMappedTypeMembers(type: ReverseMappedType) { - const indexInfo = getIndexInfoOfType( - type.source, - IndexKind.String - ); - const modifiers = getMappedTypeModifiers(type.mappedType); - const readonlyMask = - modifiers & MappedTypeModifiers.IncludeReadonly ? false : true; - const optionalMask = - modifiers & MappedTypeModifiers.IncludeOptional - ? 0 - : SymbolFlags.Optional; - const stringIndexInfo = indexInfo - && createIndexInfo( - inferReverseMappedType( - indexInfo.type, - type.mappedType, - type.constraintType - ), - readonlyMask && indexInfo.isReadonly - ); - const members = createSymbolTable(); - for (const prop of getPropertiesOfType(type.source)) { - const checkFlags = - CheckFlags.ReverseMapped - | (readonlyMask && isReadonlySymbol(prop) - ? CheckFlags.Readonly - : 0); - const inferredProp = createSymbol( - SymbolFlags.Property | prop.flags & optionalMask, - prop.escapedName, - checkFlags - ) as ReverseMappedSymbol; - inferredProp.declarations = prop.declarations; - inferredProp.nameType = prop.nameType; - inferredProp.propertyType = getTypeOfSymbol(prop); - inferredProp.mappedType = type.mappedType; - inferredProp.constraintType = type.constraintType; - members.set(prop.escapedName, inferredProp); - } - setStructuredTypeMembers( - type, - members, - emptyArray, - emptyArray, - stringIndexInfo, - undefined - ); - } - - // Return the lower bound of the key type in a mapped type. Intuitively, the lower - // bound includes those keys that are known to always be present, for example because - // because of constraints on type parameters (e.g. 'keyof T' for a constrained T). - function getLowerBoundOfKeyType(type: Type): Type { - if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) { - return type; - } - if (type.flags & TypeFlags.Index) { - return getIndexType(getApparentType(( type).type)); - } - if (type.flags & TypeFlags.Conditional) { - if (( type).root.isDistributive) { - const checkType = ( type).checkType; - const constraint = getLowerBoundOfKeyType(checkType); - if (constraint !== checkType) { - const mapper = makeUnaryTypeMapper( - ( type).root.checkType, - constraint - ); - return getConditionalTypeInstantiation( - type, - combineTypeMappers( - mapper, - ( type).mapper - ) - ); - } - } - return type; - } - if (type.flags & TypeFlags.Union) { - return getUnionType(sameMap( - ( type).types, - getLowerBoundOfKeyType - )); - } - if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(sameMap( - ( type).types, - getLowerBoundOfKeyType - )); - } - return neverType; - } - - /** Resolve the members of a mapped type { [P in K]: T } */ - function resolveMappedTypeMembers(type: MappedType) { - const members: SymbolTable = createSymbolTable(); - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - // Resolve upfront such that recursive references see an empty object type. - setStructuredTypeMembers( - type, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, - // and T as the template type. - const typeParameter = getTypeParameterFromMappedType(type); - const constraintType = getConstraintTypeFromMappedType(type); - const templateType = - getTemplateTypeFromMappedType( type.target - || type); - const modifiersType = - getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' - const templateModifiers = getMappedTypeModifiers(type); - const include = keyofStringsOnly - ? TypeFlags.StringLiteral - : TypeFlags.StringOrNumberLiteralOrUnique; - if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - // We have a { [P in keyof T]: X } - for (const prop of getPropertiesOfType(modifiersType)) { - addMemberForKeyType(getLiteralTypeFromProperty( - prop, - include - )); - } - if (modifiersType.flags & TypeFlags.Any - || getIndexInfoOfType(modifiersType, IndexKind.String)) - { - addMemberForKeyType(stringType); - } - if (!keyofStringsOnly - && getIndexInfoOfType(modifiersType, IndexKind.Number)) - { - addMemberForKeyType(numberType); - } - } else { - forEachType( - getLowerBoundOfKeyType(constraintType), - addMemberForKeyType - ); - } - setStructuredTypeMembers( - type, - members, - emptyArray, - emptyArray, - stringIndexInfo, - numberIndexInfo - ); - - function addMemberForKeyType(t: Type) { - // Create a mapper from T to the current iteration type constituent. Then, if the - // mapped type is itself an instantiated type, combine the iteration mapper with the - // instantiation mapper. - const templateMapper = combineTypeMappers( - type.mapper, - createTypeMapper([typeParameter], [t]) - ); - const propType = instantiateType(templateType, templateMapper); - // If the current iteration type constituent is a string literal type, create a property. - // Otherwise, for type string create a string index signature. - if (isTypeUsableAsPropertyName(t)) { - const propName = getPropertyNameFromType(t); - const modifiersProp = getPropertyOfType( - modifiersType, - propName - ); - const isOptional = - !!(templateModifiers - & MappedTypeModifiers.IncludeOptional - || !(templateModifiers - & MappedTypeModifiers.ExcludeOptional) - && modifiersProp - && modifiersProp.flags & SymbolFlags.Optional); - const isReadonly = - !!(templateModifiers - & MappedTypeModifiers.IncludeReadonly - || !(templateModifiers - & MappedTypeModifiers.ExcludeReadonly) - && modifiersProp - && isReadonlySymbol(modifiersProp)); - const prop = createSymbol( - SymbolFlags.Property | (isOptional - ? SymbolFlags.Optional - : 0), - propName, - isReadonly ? CheckFlags.Readonly : 0 - ); - // When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the - // type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks - // mode, if the underlying property is optional we remove 'undefined' from the type. - prop - .type = strictNullChecks && isOptional - && !isTypeAssignableTo(undefinedType, propType) - ? getOptionalType(propType) - : strictNullChecks && !isOptional && modifiersProp - && modifiersProp.flags & SymbolFlags.Optional - ? getTypeWithFacts( - propType, - TypeFacts.NEUndefined - ) - : propType; - if (modifiersProp) { - prop.syntheticOrigin = modifiersProp; - prop.declarations = modifiersProp.declarations; - } - prop.nameType = t; - members.set(propName, prop); - } else if (t.flags & (TypeFlags.Any | TypeFlags.String)) { - stringIndexInfo = createIndexInfo( - propType, - !!(templateModifiers - & MappedTypeModifiers.IncludeReadonly) - ); - } else if (t.flags & (TypeFlags.Number | TypeFlags.Enum)) { - numberIndexInfo = createIndexInfo( - numberIndexInfo - ? getUnionType([numberIndexInfo.type, propType]) - : propType, - !!(templateModifiers - & MappedTypeModifiers.IncludeReadonly) - ); - } - } - } - - function getTypeParameterFromMappedType(type: MappedType) { - return type.typeParameter - || (type - .typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(type - .declaration.typeParameter))); - } - - function getConstraintTypeFromMappedType(type: MappedType) { - return type.constraintType - || (type - .constraintType = getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)) - || errorType); - } - - function getTemplateTypeFromMappedType(type: MappedType) { - return type.templateType - || (type.templateType = type.declaration.type - ? instantiateType( - addOptionality( - getTypeFromTypeNode(type.declaration.type), - !!(getMappedTypeModifiers(type) - & MappedTypeModifiers.IncludeOptional) - ), - type.mapper || identityMapper - ) - : errorType); - } - - function getConstraintDeclarationForMappedType(type: MappedType) { - return getEffectiveConstraintOfTypeParameter(type.declaration - .typeParameter); - } - - function isMappedTypeWithKeyofConstraintDeclaration(type: MappedType) { - const constraintDeclaration = - getConstraintDeclarationForMappedType(type)!; // TODO: GH#18217 - return constraintDeclaration.kind === SyntaxKind.TypeOperator - && ( constraintDeclaration).operator - === SyntaxKind.KeyOfKeyword; - } - - function getModifiersTypeFromMappedType(type: MappedType) { - if (!type.modifiersType) { - if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check - // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves - // 'keyof T' to a literal union type and we can't recover T from that type. - type.modifiersType = instantiateType( - getTypeFromTypeNode(( getConstraintDeclarationForMappedType(type)) - .type), - type.mapper || identityMapper - ); - } else { - // Otherwise, get the declared constraint type, and if the constraint type is a type parameter, - // get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T', - // the modifiers type is T. Otherwise, the modifiers type is unknown. - const declaredType = - getTypeFromMappedTypeNode(type - .declaration); - const constraint = - getConstraintTypeFromMappedType(declaredType); - const extendedConstraint = - constraint - && constraint.flags & TypeFlags.TypeParameter - ? getConstraintOfTypeParameter( constraint) - : constraint; - type - .modifiersType = extendedConstraint - && extendedConstraint.flags & TypeFlags.Index - ? instantiateType( - ( extendedConstraint).type, - type.mapper || identityMapper - ) - : unknownType; - } - } - return type.modifiersType; - } - - function getMappedTypeModifiers(type: - MappedType): MappedTypeModifiers - { - const declaration = type.declaration; - return (declaration.readonlyToken - ? declaration.readonlyToken.kind === SyntaxKind.MinusToken - ? MappedTypeModifiers.ExcludeReadonly - : MappedTypeModifiers.IncludeReadonly - : 0) - | (declaration.questionToken - ? declaration.questionToken.kind === SyntaxKind.MinusToken - ? MappedTypeModifiers.ExcludeOptional - : MappedTypeModifiers.IncludeOptional - : 0); - } - - function getMappedTypeOptionality(type: MappedType): number { - const modifiers = getMappedTypeModifiers(type); - return modifiers & MappedTypeModifiers.ExcludeOptional - ? -1 - : modifiers & MappedTypeModifiers.IncludeOptional ? 1 : 0; - } - - function getCombinedMappedTypeOptionality(type: MappedType): number { - const optionality = getMappedTypeOptionality(type); - const modifiersType = getModifiersTypeFromMappedType(type); - return optionality - || (isGenericMappedType(modifiersType) - ? getMappedTypeOptionality(modifiersType) - : 0); - } - - function isPartialMappedType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.Mapped - && getMappedTypeModifiers( type) - & MappedTypeModifiers.IncludeOptional); - } - - function isGenericMappedType(type: Type): type is MappedType { - return !!(getObjectFlags(type) & ObjectFlags.Mapped) - && isGenericIndexType(getConstraintTypeFromMappedType( type)); - } - - function resolveStructuredTypeMembers(type: - StructuredType): ResolvedType - { - if (!( type).members) { - if (type.flags & TypeFlags.Object) { - if (( type).objectFlags - & ObjectFlags.Reference) - { - resolveTypeReferenceMembers( type); - } else if (( type).objectFlags - & ObjectFlags.ClassOrInterface) - { - resolveClassOrInterfaceMembers( type); - } else if (( type).objectFlags - & ObjectFlags.ReverseMapped) - { - resolveReverseMappedTypeMembers(type as ReverseMappedType); - } else if (( type).objectFlags - & ObjectFlags.Anonymous) - { - resolveAnonymousTypeMembers( type); - } else if (( type).objectFlags - & ObjectFlags.Mapped) - { - resolveMappedTypeMembers( type); - } - } else if (type.flags & TypeFlags.Union) { - resolveUnionTypeMembers( type); - } else if (type.flags & TypeFlags.Intersection) { - resolveIntersectionTypeMembers( type); - } - } - return type; - } - - /** Return properties of an object type or an empty array for other types */ - function getPropertiesOfObjectType(type: Type): Symbol[] { - if (type.flags & TypeFlags.Object) { - return resolveStructuredTypeMembers( type) - .properties; - } - return emptyArray; - } - - /** If the given type is an object type and that type has a property by the given name, - * return the symbol for that property. Otherwise return undefined. - */ - function getPropertyOfObjectType(type: Type, name: __String): Symbol - | undefined - { - if (type.flags & TypeFlags.Object) { - const resolved = - resolveStructuredTypeMembers( type); - const symbol = resolved.members.get(name); - if (symbol && symbolIsValue(symbol)) { - return symbol; - } - } - } - - function getPropertiesOfUnionOrIntersectionType(type: - UnionOrIntersectionType): Symbol[] - { - if (!type.resolvedProperties) { - const members = createSymbolTable(); - for (const current of type.types) { - for (const prop of getPropertiesOfType(current)) { - if (!members.has(prop.escapedName)) { - const combinedProp = - getPropertyOfUnionOrIntersectionType( - type, - prop.escapedName - ); - if (combinedProp) { - members.set(prop.escapedName, combinedProp); - } - } - } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type - if (type.flags & TypeFlags.Union) { - break; - } - } - type.resolvedProperties = getNamedMembers(members); - } - return type.resolvedProperties; - } - - function getPropertiesOfType(type: Type): Symbol[] { - type = getApparentType(type); - return type.flags & TypeFlags.UnionOrIntersection - ? getPropertiesOfUnionOrIntersectionType( type) - : getPropertiesOfObjectType(type); - } - - function isTypeInvalidDueToUnionDiscriminant( - contextualType: Type, - obj: ObjectLiteralExpression | JsxAttributes - ): boolean { - const list = obj - .properties as NodeArray; - return list.some(property => { - const nameType = property.name - && getLiteralTypeFromPropertyName(property.name); - const name = nameType && isTypeUsableAsPropertyName(nameType) - ? getPropertyNameFromType(nameType) - : undefined; - const expected = name === undefined - ? undefined - : getTypeOfPropertyOfType(contextualType, name); - return !!expected && isLiteralType(expected) - && !isTypeAssignableTo(getTypeOfNode(property), expected); - }); - } - - function getAllPossiblePropertiesOfTypes(types: - readonly Type[]): Symbol[] - { - const unionType = getUnionType(types); - if (!(unionType.flags & TypeFlags.Union)) { - return getAugmentedPropertiesOfType(unionType); - } - - const props = createSymbolTable(); - for (const memberType of types) { - for (const { escapedName } - of getAugmentedPropertiesOfType(memberType)) - { - if (!props.has(escapedName)) { - const prop = createUnionOrIntersectionProperty( - unionType as UnionType, - escapedName - ); - // May be undefined if the property is private - if (prop) props.set(escapedName, prop); - } - } - } - return arrayFrom(props.values()); - } - - function getConstraintOfType(type: InstantiableType - | UnionOrIntersectionType): Type | undefined - { - return type.flags & TypeFlags.TypeParameter - ? getConstraintOfTypeParameter( type) - : type.flags & TypeFlags.IndexedAccess - ? getConstraintOfIndexedAccess( type) - : type.flags & TypeFlags.Conditional - ? getConstraintOfConditionalType( type) - : getBaseConstraintOfType(type); - } - - function getConstraintOfTypeParameter(typeParameter: - TypeParameter): Type | undefined - { - return hasNonCircularBaseConstraint(typeParameter) - ? getConstraintFromTypeParameter(typeParameter) - : undefined; - } - - function getConstraintOfIndexedAccess(type: IndexedAccessType) { - return hasNonCircularBaseConstraint(type) - ? getConstraintFromIndexedAccess(type) - : undefined; - } - - function getSimplifiedTypeOrConstraint(type: Type) { - const simplified = getSimplifiedType(type, /*writing*/ false); - return simplified !== type - ? simplified - : getConstraintOfType(type); - } - - function getConstraintFromIndexedAccess(type: IndexedAccessType) { - const indexConstraint = - getSimplifiedTypeOrConstraint(type.indexType); - if (indexConstraint && indexConstraint !== type.indexType) { - const indexedAccess = getIndexedAccessTypeOrUndefined( - type.objectType, - indexConstraint - ); - if (indexedAccess) { - return indexedAccess; - } - } - const objectConstraint = - getSimplifiedTypeOrConstraint(type.objectType); - if (objectConstraint && objectConstraint !== type.objectType) { - return getIndexedAccessTypeOrUndefined( - objectConstraint, - type.indexType - ); - } - return undefined; - } - - function getDefaultConstraintOfConditionalType(type: ConditionalType) { - if (!type.resolvedDefaultConstraint) { - // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, - // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to - // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, - // in effect treating `any` like `never` rather than `unknown` in this location. - const trueConstraint = - getInferredTrueTypeFromConditionalType(type); - const falseConstraint = getFalseTypeFromConditionalType(type); - type.resolvedDefaultConstraint = isTypeAny(trueConstraint) - ? falseConstraint - : isTypeAny(falseConstraint) - ? trueConstraint - : getUnionType([trueConstraint, falseConstraint]); - } - return type.resolvedDefaultConstraint; - } - - function getConstraintOfDistributiveConditionalType(type: - ConditionalType): Type | undefined - { - // Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained - // type parameter. If so, create an instantiation of the conditional type where T is replaced - // with its constraint. We do this because if the constraint is a union type it will be distributed - // over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T' - // removes 'undefined' from T. - // We skip returning a distributive constraint for a restrictive instantiation of a conditional type - // as the constraint for all type params (check type included) have been replace with `unknown`, which - // is going to produce even more false positive/negative results than the distribute constraint already does. - // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter - // a union - once negated types exist and are applied to the conditional false branch, this "constraint" - // likely doesn't need to exist. - if (type.root.isDistributive - && type.restrictiveInstantiation !== type) - { - const simplified = getSimplifiedType( - type.checkType, /*writing*/ - false - ); - const constraint = simplified === type.checkType - ? getConstraintOfType(simplified) - : simplified; - if (constraint && constraint !== type.checkType) { - const mapper = makeUnaryTypeMapper( - type.root.checkType, - constraint - ); - const instantiated = getConditionalTypeInstantiation( - type, - combineTypeMappers(mapper, type.mapper) - ); - if (!(instantiated.flags & TypeFlags.Never)) { - return instantiated; - } - } - } - return undefined; - } - - function getConstraintFromConditionalType(type: ConditionalType) { - return getConstraintOfDistributiveConditionalType(type) - || getDefaultConstraintOfConditionalType(type); - } - - function getConstraintOfConditionalType(type: ConditionalType) { - return hasNonCircularBaseConstraint(type) - ? getConstraintFromConditionalType(type) - : undefined; - } - - function getEffectiveConstraintOfIntersection( - types: readonly Type[], - targetIsUnion: boolean - ) { - let constraints: Type[] | undefined; - let hasDisjointDomainType = false; - for (const t of types) { - if (t.flags & TypeFlags.Instantiable) { - // We keep following constraints as long as we have an instantiable type that is known - // not to be circular or infinite (hence we stop on index access types). - let constraint = getConstraintOfType(t); - while (constraint - && constraint.flags - & (TypeFlags.TypeParameter | TypeFlags.Index - | TypeFlags.Conditional)) - { - constraint = getConstraintOfType(constraint); - } - if (constraint) { - constraints = append(constraints, constraint); - if (targetIsUnion) { - constraints = append(constraints, t); - } - } - } else if (t.flags & TypeFlags.DisjointDomains) { - hasDisjointDomainType = true; - } - } - // If the target is a union type or if we are intersecting with types belonging to one of the - // disjoint domains, we may end up producing a constraint that hasn't been examined before. - if (constraints && (targetIsUnion || hasDisjointDomainType)) { - if (hasDisjointDomainType) { - // We add any types belong to one of the disjoint domains because they might cause the final - // intersection operation to reduce the union constraints. - for (const t of types) { - if (t.flags & TypeFlags.DisjointDomains) { - constraints = append(constraints, t); - } - } - } - return getIntersectionType(constraints); - } - return undefined; - } - - function getBaseConstraintOfType(type: Type): Type | undefined { - if (type.flags - & (TypeFlags.InstantiableNonPrimitive - | TypeFlags.UnionOrIntersection)) - { - const constraint = - getResolvedBaseConstraint( type); - return constraint !== noConstraintType - && constraint !== circularConstraintType - ? constraint - : undefined; - } - return type.flags & TypeFlags.Index - ? keyofConstraintType - : undefined; - } - - /** - * This is similar to `getBaseConstraintOfType` except it returns the input type if there's no base constraint, instead of `undefined` - * It also doesn't map indexes to `string`, as where this is used this would be unneeded (and likely undesirable) - */ - function getBaseConstraintOrType(type: Type) { - return getBaseConstraintOfType(type) || type; - } - - function hasNonCircularBaseConstraint(type: - InstantiableType): boolean - { - return getResolvedBaseConstraint(type) !== circularConstraintType; - } - - /** - * Return the resolved base constraint of a type variable. The noConstraintType singleton is returned if the - * type variable has no constraint, and the circularConstraintType singleton is returned if the constraint - * circularly references the type variable. - */ - function getResolvedBaseConstraint(type: InstantiableType - | UnionOrIntersectionType): Type - { - let nonTerminating = false; - return type.resolvedBaseConstraint - || (type.resolvedBaseConstraint = getTypeWithThisArgument( - getImmediateBaseConstraint(type), - type - )); - - function getImmediateBaseConstraint(t: Type): Type { - if (!t.immediateBaseConstraint) { - if (!pushTypeResolution( - t, - TypeSystemPropertyName.ImmediateBaseConstraint - )) { - return circularConstraintType; - } - if (constraintDepth >= 50) { - // We have reached 50 recursive invocations of getImmediateBaseConstraint and there is a - // very high likelihood we're dealing with an infinite generic type that perpetually generates - // new type identities as we descend into it. We stop the recursion here and mark this type - // and the outer types as having circular constraints. - error( - currentNode, - Diagnostics - .Type_instantiation_is_excessively_deep_and_possibly_infinite - ); - nonTerminating = true; - return t.immediateBaseConstraint = noConstraintType; - } - constraintDepth++; - let result = - computeBaseConstraint(getSimplifiedType( - t, /*writing*/ - false - )); - constraintDepth--; - if (!popTypeResolution()) { - if (t.flags & TypeFlags.TypeParameter) { - const errorNode = - getConstraintDeclaration( t); - if (errorNode) { - const diagnostic = error( - errorNode, - Diagnostics - .Type_parameter_0_has_a_circular_constraint, - typeToString(t) - ); - if (currentNode - && !isNodeDescendantOf( - errorNode, - currentNode - ) - && !isNodeDescendantOf( - currentNode, - errorNode - )) - { - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - currentNode, - Diagnostics - .Circularity_originates_in_type_at_this_location - ) - ); - } - } - } - result = circularConstraintType; - } - if (nonTerminating) { - result = circularConstraintType; - } - t.immediateBaseConstraint = result || noConstraintType; - } - return t.immediateBaseConstraint; - } - - function getBaseConstraint(t: Type): Type | undefined { - const c = getImmediateBaseConstraint(t); - return c !== noConstraintType && c !== circularConstraintType - ? c - : undefined; - } - - function computeBaseConstraint(t: Type): Type | undefined { - if (t.flags & TypeFlags.TypeParameter) { - const constraint = - getConstraintFromTypeParameter( t); - return (t as TypeParameter).isThisType || !constraint - ? constraint - : getBaseConstraint(constraint); - } - if (t.flags & TypeFlags.UnionOrIntersection) { - const types = ( t).types; - const baseTypes: Type[] = []; - for (const type of types) { - const baseType = getBaseConstraint(type); - if (baseType) { - baseTypes.push(baseType); - } - } - return t.flags & TypeFlags.Union - && baseTypes.length === types.length - ? getUnionType(baseTypes) - : t.flags & TypeFlags.Intersection && baseTypes.length - ? getIntersectionType(baseTypes) - : undefined; - } - if (t.flags & TypeFlags.Index) { - return keyofConstraintType; - } - if (t.flags & TypeFlags.IndexedAccess) { - const baseObjectType = - getBaseConstraint(( t).objectType); - const baseIndexType = - getBaseConstraint(( t).indexType); - const baseIndexedAccess = baseObjectType && baseIndexType - && getIndexedAccessTypeOrUndefined( - baseObjectType, - baseIndexType - ); - return baseIndexedAccess - && getBaseConstraint(baseIndexedAccess); - } - if (t.flags & TypeFlags.Conditional) { - const constraint = - getConstraintFromConditionalType( t); - constraintDepth++; // Penalize repeating conditional types (this captures the recursion within getConstraintFromConditionalType and carries it forward) - const result = constraint && getBaseConstraint(constraint); - constraintDepth--; - return result; - } - if (t.flags & TypeFlags.Substitution) { - return getBaseConstraint(( t) - .substitute); - } - return t; - } - } - - function getApparentTypeOfIntersectionType(type: IntersectionType) { - return type.resolvedApparentType - || (type.resolvedApparentType = getTypeWithThisArgument( - type, - type, /*apparentType*/ - true - )); - } - - function getResolvedTypeParameterDefault(typeParameter: - TypeParameter): Type | undefined - { - if (!typeParameter.default) { - if (typeParameter.target) { - const targetDefault = - getResolvedTypeParameterDefault(typeParameter.target); - typeParameter.default = targetDefault - ? instantiateType(targetDefault, typeParameter.mapper) - : noConstraintType; - } else { - // To block recursion, set the initial value to the resolvingDefaultType. - typeParameter.default = resolvingDefaultType; - const defaultDeclaration = typeParameter.symbol - && forEach( - typeParameter.symbol.declarations, - decl => isTypeParameterDeclaration(decl) - && decl.default - ); - const defaultType = defaultDeclaration - ? getTypeFromTypeNode(defaultDeclaration) - : noConstraintType; - if (typeParameter.default === resolvingDefaultType) { - // If we have not been called recursively, set the correct default type. - typeParameter.default = defaultType; - } - } - } else if (typeParameter.default === resolvingDefaultType) { - // If we are called recursively for this type parameter, mark the default as circular. - typeParameter.default = circularConstraintType; - } - return typeParameter.default; - } - - /** - * Gets the default type for a type parameter. - * - * If the type parameter is the result of an instantiation, this gets the instantiated - * default type of its target. If the type parameter has no default type or the default is - * circular, `undefined` is returned. - */ - function getDefaultFromTypeParameter(typeParameter: - TypeParameter): Type | undefined - { - const defaultType = getResolvedTypeParameterDefault(typeParameter); - return defaultType !== noConstraintType - && defaultType !== circularConstraintType - ? defaultType - : undefined; - } - - function hasNonCircularTypeParameterDefault(typeParameter: - TypeParameter) - { - return getResolvedTypeParameterDefault(typeParameter) - !== circularConstraintType; - } - - /** - * Indicates whether the declaration of a typeParameter has a default type. - */ - function hasTypeParameterDefault(typeParameter: - TypeParameter): boolean - { - return !!(typeParameter.symbol - && forEach( - typeParameter.symbol.declarations, - decl => isTypeParameterDeclaration(decl) && decl.default - )); - } - - function getApparentTypeOfMappedType(type: MappedType) { - return type.resolvedApparentType - || (type - .resolvedApparentType = getResolvedApparentTypeOfMappedType(type)); - } - - function getResolvedApparentTypeOfMappedType(type: MappedType) { - const typeVariable = getHomomorphicTypeVariable(type); - if (typeVariable) { - const constraint = getConstraintOfTypeParameter(typeVariable); - if (constraint - && (isArrayType(constraint) || isTupleType(constraint))) - { - const mapper = makeUnaryTypeMapper( - typeVariable, - constraint - ); - return instantiateType( - type, - combineTypeMappers(mapper, type.mapper) - ); - } - } - return type; - } - - /** - * For a type parameter, return the base constraint of the type parameter. For the string, number, - * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the - * type itself. Note that the apparent type of a union type is the union type itself. - */ - function getApparentType(type: Type): Type { - const t = type.flags & TypeFlags.Instantiable - ? getBaseConstraintOfType(type) || unknownType - : type; - return getObjectFlags(t) & ObjectFlags.Mapped - ? getApparentTypeOfMappedType( t) - : t.flags & TypeFlags.Intersection - ? getApparentTypeOfIntersectionType( t) - : t.flags & TypeFlags.StringLike - ? globalStringType - : t.flags & TypeFlags.NumberLike - ? globalNumberType - : t.flags & TypeFlags.BigIntLike - ? getGlobalBigIntType(/*reportErrors*/ languageVersion - >= ScriptTarget.ESNext) - : t.flags & TypeFlags.BooleanLike - ? globalBooleanType - : t.flags & TypeFlags.ESSymbolLike - ? getGlobalESSymbolType(/*reportErrors*/ languageVersion - >= ScriptTarget.ES2015) - : t.flags & TypeFlags.NonPrimitive - ? emptyObjectType - : t.flags & TypeFlags.Index - ? keyofConstraintType - : t.flags & TypeFlags.Unknown - && !strictNullChecks - ? emptyObjectType - : t; - } - - function createUnionOrIntersectionProperty( - containingType: UnionOrIntersectionType, - name: __String - ): Symbol | undefined { - const propSet = createMap(); - let indexTypes: Type[] | undefined; - const isUnion = containingType.flags & TypeFlags.Union; - const excludeModifiers = isUnion - ? ModifierFlags.NonPublicAccessibilityModifier - : 0; - // Flags we want to propagate to the result if they exist in all source symbols - let optionalFlag = isUnion - ? SymbolFlags.None - : SymbolFlags.Optional; - let syntheticFlag = CheckFlags.SyntheticMethod; - let checkFlags = 0; - for (const current of containingType.types) { - const type = getApparentType(current); - if (type !== errorType) { - const prop = getPropertyOfType(type, name); - const modifiers = prop - ? getDeclarationModifierFlagsFromSymbol(prop) - : 0; - if (prop && !(modifiers & excludeModifiers)) { - if (isUnion) { - optionalFlag |= (prop.flags - & SymbolFlags.Optional); - } else { - optionalFlag &= prop.flags; - } - const id = '' + getSymbolId(prop); - if (!propSet.has(id)) { - propSet.set(id, prop); - } - checkFlags |= (isReadonlySymbol(prop) - ? CheckFlags.Readonly - : 0) - | (!(modifiers - & ModifierFlags.NonPublicAccessibilityModifier) - ? CheckFlags.ContainsPublic - : 0) - | (modifiers & ModifierFlags.Protected - ? CheckFlags.ContainsProtected - : 0) - | (modifiers & ModifierFlags.Private - ? CheckFlags.ContainsPrivate - : 0) - | (modifiers & ModifierFlags.Static - ? CheckFlags.ContainsStatic - : 0); - if (!isPrototypeProperty(prop)) { - syntheticFlag = CheckFlags.SyntheticProperty; - } - } else if (isUnion) { - const indexInfo = !isLateBoundName(name) - && (isNumericLiteralName(name) - && getIndexInfoOfType(type, IndexKind.Number) - || getIndexInfoOfType(type, IndexKind.String)); - if (indexInfo) { - checkFlags |= CheckFlags.WritePartial - | (indexInfo.isReadonly - ? CheckFlags.Readonly - : 0); - indexTypes = append( - indexTypes, - isTupleType(type) - ? getRestTypeOfTupleType(type) - || undefinedType - : indexInfo.type - ); - } else if (isObjectLiteralType(type)) { - checkFlags |= CheckFlags.WritePartial; - indexTypes = append(indexTypes, undefinedType); - } else { - checkFlags |= CheckFlags.ReadPartial; - } - } - } - } - if (!propSet.size) { - return undefined; - } - const props = arrayFrom(propSet.values()); - if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial) - && !indexTypes) - { - return props[0]; - } - let declarations: Declaration[] | undefined; - let firstType: Type | undefined; - let nameType: Type | undefined; - const propTypes: Type[] = []; - let firstValueDeclaration: Declaration | undefined; - let hasNonUniformValueDeclaration = false; - for (const prop of props) { - if (!firstValueDeclaration) { - firstValueDeclaration = prop.valueDeclaration; - } else if (prop.valueDeclaration !== firstValueDeclaration) { - hasNonUniformValueDeclaration = true; - } - declarations = addRange(declarations, prop.declarations); - const type = getTypeOfSymbol(prop); - if (!firstType) { - firstType = type; - nameType = prop.nameType; - } else if (type !== firstType) { - checkFlags |= CheckFlags.HasNonUniformType; - } - if (isLiteralType(type)) { - checkFlags |= CheckFlags.HasLiteralType; - } - propTypes.push(type); - } - addRange(propTypes, indexTypes); - const result = createSymbol( - SymbolFlags.Property | optionalFlag, - name, - syntheticFlag | checkFlags - ); - result.containingType = containingType; - if (!hasNonUniformValueDeclaration && firstValueDeclaration) { - result.valueDeclaration = firstValueDeclaration; - - // Inherit information about parent type. - if (firstValueDeclaration.symbol.parent) { - result.parent = firstValueDeclaration.symbol.parent; - } - } - - result.declarations = declarations!; - result.nameType = nameType; - if (propTypes.length > 2) { - // When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed - result.checkFlags |= CheckFlags.DeferredType; - result.deferralParent = containingType; - result.deferralConstituents = propTypes; - } else { - result.type = isUnion - ? getUnionType(propTypes) - : getIntersectionType(propTypes); - } - return result; - } - - // Return the symbol for a given property in a union or intersection type, or undefined if the property - // does not exist in any constituent type. Note that the returned property may only be present in some - // constituents, in which case the isPartial flag is set when the containing type is union type. We need - // these partial properties when identifying discriminant properties, but otherwise they are filtered out - // and do not appear to be present in the union type. - function getUnionOrIntersectionProperty( - type: UnionOrIntersectionType, - name: __String - ): Symbol | undefined { - const properties = type.propertyCache - || (type.propertyCache = createSymbolTable()); - let property = properties.get(name); - if (!property) { - property = createUnionOrIntersectionProperty(type, name); - if (property) { - properties.set(name, property); - } - } - return property; - } - - function getPropertyOfUnionOrIntersectionType( - type: UnionOrIntersectionType, - name: __String - ): Symbol | undefined { - const property = getUnionOrIntersectionProperty(type, name); - // We need to filter out partial properties in union types - return property - && !(getCheckFlags(property) & CheckFlags.ReadPartial) - ? property - : undefined; - } - - /** - * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - * Object and Function as appropriate. - * - * @param type a type to look up property from - * @param name a name of property to look up in a given type - */ - function getPropertyOfType(type: Type, name: __String): Symbol - | undefined - { - type = getApparentType(type); - if (type.flags & TypeFlags.Object) { - const resolved = - resolveStructuredTypeMembers( type); - const symbol = resolved.members.get(name); - if (symbol && symbolIsValue(symbol)) { - return symbol; - } - const functionType = resolved === anyFunctionType - ? globalFunctionType - : resolved.callSignatures.length - ? globalCallableFunctionType - : resolved.constructSignatures.length - ? globalNewableFunctionType - : undefined; - if (functionType) { - const symbol = getPropertyOfObjectType(functionType, name); - if (symbol) { - return symbol; - } - } - return getPropertyOfObjectType(globalObjectType, name); - } - if (type.flags & TypeFlags.UnionOrIntersection) { - return getPropertyOfUnionOrIntersectionType( - type, - name - ); - } - return undefined; - } - - function getSignaturesOfStructuredType( - type: Type, - kind: SignatureKind - ): readonly Signature[] { - if (type.flags & TypeFlags.StructuredType) { - const resolved = - resolveStructuredTypeMembers( type); - return kind === SignatureKind.Call - ? resolved.callSignatures - : resolved.constructSignatures; - } - return emptyArray; - } - - /** - * Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and - * maps primitive types and type parameters are to their apparent types. - */ - function getSignaturesOfType( - type: Type, - kind: SignatureKind - ): readonly Signature[] { - return getSignaturesOfStructuredType(getApparentType(type), kind); - } - - function getIndexInfoOfStructuredType( - type: Type, - kind: IndexKind - ): IndexInfo | undefined { - if (type.flags & TypeFlags.StructuredType) { - const resolved = - resolveStructuredTypeMembers( type); - return kind === IndexKind.String - ? resolved.stringIndexInfo - : resolved.numberIndexInfo; - } - } - - function getIndexTypeOfStructuredType( - type: Type, - kind: IndexKind - ): Type | undefined { - const info = getIndexInfoOfStructuredType(type, kind); - return info && info.type; - } - - // Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and - // maps primitive types and type parameters are to their apparent types. - function getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo - | undefined - { - return getIndexInfoOfStructuredType(getApparentType(type), kind); - } - - // Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and - // maps primitive types and type parameters are to their apparent types. - function getIndexTypeOfType(type: Type, kind: IndexKind): Type - | undefined - { - return getIndexTypeOfStructuredType(getApparentType(type), kind); - } - - function getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type - | undefined - { - if (isObjectTypeWithInferableIndex(type)) { - const propTypes: Type[] = []; - for (const prop of getPropertiesOfType(type)) { - if (kind === IndexKind.String - || isNumericLiteralName(prop.escapedName)) - { - propTypes.push(getTypeOfSymbol(prop)); - } - } - if (kind === IndexKind.String) { - append( - propTypes, - getIndexTypeOfType(type, IndexKind.Number) - ); - } - if (propTypes.length) { - return getUnionType(propTypes, UnionReduction.Subtype); - } - } - return undefined; - } - - // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual - // type checking functions). - function getTypeParametersFromDeclaration(declaration: - DeclarationWithTypeParameters): TypeParameter[] | undefined - { - let result: TypeParameter[] | undefined; - for (const node - of getEffectiveTypeParameterDeclarations(declaration)) - { - result = appendIfUnique( - result, - getDeclaredTypeOfTypeParameter(node.symbol) - ); - } - return result; - } - - function symbolsToArray(symbols: SymbolTable): Symbol[] { - const result: Symbol[] = []; - symbols.forEach((symbol, id) => { - if (!isReservedMemberName(id)) { - result.push(symbol); - } - }); - return result; - } - - function isJSDocOptionalParameter(node: ParameterDeclaration) { - return isInJSFile(node) && ( - // node.type should only be a JSDocOptionalType when node is a parameter of a JSDocFunctionType - node.type && node.type.kind === SyntaxKind.JSDocOptionalType - || getJSDocParameterTags(node) - .some(({ isBracketed, typeExpression }) => isBracketed - || !!typeExpression - && typeExpression.type.kind - === SyntaxKind.JSDocOptionalType) - ); - } - - function tryFindAmbientModule( - moduleName: string, - withAugmentations: boolean - ) { - if (isExternalModuleNameRelative(moduleName)) { - return undefined; - } - const symbol = getSymbol( - globals, - '"' + moduleName + '"' as __String, - SymbolFlags.ValueModule - ); - // merged symbol is module declaration symbol combined with all augmentations - return symbol && withAugmentations - ? getMergedSymbol(symbol) - : symbol; - } - - function isOptionalParameter(node: ParameterDeclaration - | JSDocParameterTag) - { - if (hasQuestionToken(node) || isOptionalJSDocParameterTag(node) - || isJSDocOptionalParameter(node)) - { - return true; - } - - if (node.initializer) { - const signature = getSignatureFromDeclaration(node.parent); - const parameterIndex = node.parent.parameters.indexOf(node); - Debug.assert(parameterIndex >= 0); - return parameterIndex >= getMinArgumentCount(signature); - } - const iife = getImmediatelyInvokedFunctionExpression(node.parent); - if (iife) { - return !node.type - && !node.dotDotDotToken - && node.parent.parameters.indexOf(node) - >= iife.arguments.length; - } - - return false; - } - - function isOptionalJSDocParameterTag(node: - Node): node is JSDocParameterTag - { - if (!isJSDocParameterTag(node)) { - return false; - } - const { isBracketed, typeExpression } = node; - return isBracketed || !!typeExpression - && typeExpression.type.kind === SyntaxKind.JSDocOptionalType; - } - - function createTypePredicate( - kind: TypePredicateKind, - parameterName: string | undefined, - parameterIndex: number | undefined, - type: Type | undefined - ): TypePredicate { - return { kind, parameterName, parameterIndex, - type } as TypePredicate; - } - - /** - * Gets the minimum number of type arguments needed to satisfy all non-optional type - * parameters. - */ - function getMinTypeArgumentCount(typeParameters: - readonly TypeParameter[] | undefined): number - { - let minTypeArgumentCount = 0; - if (typeParameters) { - for (let i = 0; i < typeParameters.length; i++) { - if (!hasTypeParameterDefault(typeParameters[i])) { - minTypeArgumentCount = i + 1; - } - } - } - return minTypeArgumentCount; - } - - /** - * Fill in default types for unsupplied type arguments. If `typeArguments` is undefined - * when a default type is supplied, a new array will be created and returned. - * - * @param typeArguments The supplied type arguments. - * @param typeParameters The requested type parameters. - * @param minTypeArgumentCount The minimum number of required type arguments. - */ - function fillMissingTypeArguments( - typeArguments: readonly Type[], - typeParameters: readonly TypeParameter[] | undefined, - minTypeArgumentCount: number, - isJavaScriptImplicitAny: boolean - ): Type[]; - function fillMissingTypeArguments( - typeArguments: readonly Type[] | undefined, - typeParameters: readonly TypeParameter[] | undefined, - minTypeArgumentCount: number, - isJavaScriptImplicitAny: boolean - ): Type[] | undefined; - function fillMissingTypeArguments( - typeArguments: readonly Type[] | undefined, - typeParameters: readonly TypeParameter[] | undefined, - minTypeArgumentCount: number, - isJavaScriptImplicitAny: boolean - ) { - const numTypeParameters = length(typeParameters); - if (!numTypeParameters) { - return []; - } - const numTypeArguments = length(typeArguments); - if (isJavaScriptImplicitAny - || (numTypeArguments >= minTypeArgumentCount - && numTypeArguments <= numTypeParameters)) - { - const result = typeArguments ? typeArguments.slice() : []; - // Map invalid forward references in default types to the error type - for (let i = numTypeArguments; i < numTypeParameters; i++) { - result[i] = errorType; - } - const baseDefaultType = - getDefaultTypeArgumentType(isJavaScriptImplicitAny); - for (let i = numTypeArguments; i < numTypeParameters; i++) { - let defaultType = - getDefaultFromTypeParameter(typeParameters![i]); - if (isJavaScriptImplicitAny && defaultType - && (isTypeIdenticalTo(defaultType, unknownType) - || isTypeIdenticalTo( - defaultType, - emptyObjectType - ))) - { - defaultType = anyType; - } - result[i] = defaultType - ? instantiateType( - defaultType, - createTypeMapper(typeParameters!, result) - ) - : baseDefaultType; - } - result.length = typeParameters!.length; - return result; - } - return typeArguments && typeArguments.slice(); - } - - function getSignatureFromDeclaration(declaration: SignatureDeclaration - | JSDocSignature): Signature - { - const links = getNodeLinks(declaration); - if (!links.resolvedSignature) { - const parameters: Symbol[] = []; - let flags = SignatureFlags.None; - let minArgumentCount = 0; - let thisParameter: Symbol | undefined; - let hasThisParameter = false; - const iife = - getImmediatelyInvokedFunctionExpression(declaration); - const isJSConstructSignature = - isJSDocConstructSignature(declaration); - const isUntypedSignatureInJSFile = !iife - && isInJSFile(declaration) - && isValueSignatureDeclaration(declaration) - && !hasJSDocParameterTags(declaration) - && !getJSDocType(declaration); - - // If this is a JSDoc construct signature, then skip the first parameter in the - // parameter list. The first parameter represents the return type of the construct - // signature. - for (let i = isJSConstructSignature ? 1 : 0; - i < declaration.parameters.length; i++) - { - const param = declaration.parameters[i]; - - let paramSymbol = param.symbol; - const type = isJSDocParameterTag(param) - ? (param.typeExpression && param.typeExpression.type) - : param.type; - // Include parameter symbol instead of property symbol in the signature - if (paramSymbol - && !!(paramSymbol.flags & SymbolFlags.Property) - && !isBindingPattern(param.name)) - { - const resolvedSymbol = resolveName( - param, - paramSymbol.escapedName, - SymbolFlags.Value, - undefined, - undefined, /*isUse*/ - false - ); - paramSymbol = resolvedSymbol!; - } - if (i === 0 - && paramSymbol.escapedName === InternalSymbolName.This) - { - hasThisParameter = true; - thisParameter = param.symbol; - } else { - parameters.push(paramSymbol); - } - - if (type && type.kind === SyntaxKind.LiteralType) { - flags |= SignatureFlags.HasLiteralTypes; - } - - // Record a new minimum argument count if this is not an optional parameter - const isOptionalParameter = - isOptionalJSDocParameterTag(param) - || param.initializer || param.questionToken - || param.dotDotDotToken - || iife - && parameters.length > iife.arguments.length - && !type - || isUntypedSignatureInJSFile - || isJSDocOptionalParameter(param); - if (!isOptionalParameter) { - minArgumentCount = parameters.length; - } - } - - // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation - if ((declaration.kind === SyntaxKind.GetAccessor - || declaration.kind === SyntaxKind.SetAccessor) - && !hasNonBindableDynamicName(declaration) - && (!hasThisParameter || !thisParameter)) - { - const otherKind = - declaration.kind === SyntaxKind.GetAccessor - ? SyntaxKind.SetAccessor - : SyntaxKind.GetAccessor; - const other = getDeclarationOfKind( - getSymbolOfNode(declaration), - otherKind - ); - if (other) { - thisParameter = getAnnotatedAccessorThisParameter(other); - } - } - - const classType = declaration.kind === SyntaxKind.Constructor - ? getDeclaredTypeOfClassOrInterface(getMergedSymbol(( declaration - .parent).symbol)) - : undefined; - const typeParameters = classType - ? classType.localTypeParameters - : getTypeParametersFromDeclaration(declaration); - if (hasRestParameter(declaration) || isInJSFile(declaration) - && maybeAddJsSyntheticRestParameter( - declaration, - parameters - )) - { - flags |= SignatureFlags.HasRestParameter; - } - links.resolvedSignature = createSignature( - declaration, - typeParameters, - thisParameter, - parameters, - /*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ - undefined, - minArgumentCount, - flags - ); - } - return links.resolvedSignature; - } - - /** - * A JS function gets a synthetic rest parameter if it references `arguments` AND: - * 1. It has no parameters but at least one `@param` with a type that starts with `...` - * OR - * 2. It has at least one parameter, and the last parameter has a matching `@param` with a type that starts with `...` - */ - function maybeAddJsSyntheticRestParameter( - declaration: SignatureDeclaration | JSDocSignature, - parameters: Symbol[] - ): boolean { - if (isJSDocSignature(declaration) - || !containsArgumentsReference(declaration)) - { - return false; - } - const lastParam = lastOrUndefined(declaration.parameters); - const lastParamTags = lastParam - ? getJSDocParameterTags(lastParam) - : getJSDocTags(declaration).filter(isJSDocParameterTag); - const lastParamVariadicType = firstDefined( - lastParamTags, - p => p.typeExpression - && isJSDocVariadicType(p.typeExpression.type) - ? p.typeExpression.type - : undefined - ); - - const syntheticArgsSymbol = createSymbol( - SymbolFlags.Variable, - 'args' as __String, - CheckFlags.RestParameter - ); - syntheticArgsSymbol.type = lastParamVariadicType - ? createArrayType(getTypeFromTypeNode(lastParamVariadicType - .type)) - : anyArrayType; - if (lastParamVariadicType) { - // Replace the last parameter with a rest parameter. - parameters.pop(); - } - parameters.push(syntheticArgsSymbol); - return true; - } - - function getSignatureOfTypeTag(node: SignatureDeclaration - | JSDocSignature) - { - const typeTag = isInJSFile(node) - ? getJSDocTypeTag(node) - : undefined; - const signature = typeTag && typeTag.typeExpression - && getSingleCallSignature(getTypeFromTypeNode(typeTag - .typeExpression)); - return signature && getErasedSignature(signature); - } - - function getReturnTypeOfTypeTag(node: SignatureDeclaration - | JSDocSignature) - { - const signature = getSignatureOfTypeTag(node); - return signature && getReturnTypeOfSignature(signature); - } - - function containsArgumentsReference(declaration: - SignatureDeclaration): boolean - { - const links = getNodeLinks(declaration); - if (links.containsArgumentsReference === undefined) { - if (links.flags & NodeCheckFlags.CaptureArguments) { - links.containsArgumentsReference = true; - } else { - links - .containsArgumentsReference = traverse((declaration as FunctionLikeDeclaration) - .body!); - } - } - return links.containsArgumentsReference; - - function traverse(node: Node): boolean { - if (!node) return false; - switch (node.kind) { - case SyntaxKind.Identifier: - return ( node).escapedText === 'arguments' - && isExpressionNode(node); - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return ( node).name!.kind - === SyntaxKind.ComputedPropertyName - && traverse(( node).name!); - - default: - return !nodeStartsNewLexicalEnvironment(node) - && !isPartOfTypeNode(node) - && !!forEachChild(node, traverse); - } - } - } - - function getSignaturesOfSymbol(symbol: Symbol - | undefined): Signature[] - { - if (!symbol) return emptyArray; - const result: Signature[] = []; - for (let i = 0; i < symbol.declarations.length; i++) { - const decl = symbol.declarations[i]; - if (!isFunctionLike(decl)) continue; - // Don't include signature if node is the implementation of an overloaded function. A node is considered - // an implementation node if it has a body and the previous node is of the same kind and immediately - // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). - if (i > 0 && (decl as FunctionLikeDeclaration).body) { - const previous = symbol.declarations[i - 1]; - if (decl.parent === previous.parent - && decl.kind === previous.kind - && decl.pos === previous.end) - { - continue; - } - } - result.push(getSignatureFromDeclaration(decl)); - } - return result; - } - - function resolveExternalModuleTypeByLiteral(name: StringLiteral) { - const moduleSym = resolveExternalModuleName(name, name); - if (moduleSym) { - const resolvedModuleSymbol = - resolveExternalModuleSymbol(moduleSym); - if (resolvedModuleSymbol) { - return getTypeOfSymbol(resolvedModuleSymbol); - } - } - - return anyType; - } - - function getThisTypeOfSignature(signature: Signature): Type - | undefined - { - if (signature.thisParameter) { - return getTypeOfSymbol(signature.thisParameter); - } - } - - function getTypePredicateOfSignature(signature: - Signature): TypePredicate | undefined - { - if (!signature.resolvedTypePredicate) { - if (signature.target) { - const targetTypePredicate = - getTypePredicateOfSignature(signature.target); - signature.resolvedTypePredicate = targetTypePredicate - ? instantiateTypePredicate( - targetTypePredicate, - signature.mapper! - ) - : noTypePredicate; - } else if (signature.unionSignatures) { - signature - .resolvedTypePredicate = getUnionTypePredicate(signature - .unionSignatures) || noTypePredicate; - } else { - const type = signature.declaration - && getEffectiveReturnTypeNode(signature.declaration); - let jsdocPredicate: TypePredicate | undefined; - if (!type && isInJSFile(signature.declaration)) { - const jsdocSignature = - getSignatureOfTypeTag(signature.declaration!); - if (jsdocSignature && signature !== jsdocSignature) { - jsdocPredicate = getTypePredicateOfSignature(jsdocSignature); - } - } - signature - .resolvedTypePredicate = type - && isTypePredicateNode(type) - ? createTypePredicateFromTypePredicateNode( - type, - signature - ) - : jsdocPredicate || noTypePredicate; - } - Debug.assert(!!signature.resolvedTypePredicate); - } - return signature.resolvedTypePredicate === noTypePredicate - ? undefined - : signature.resolvedTypePredicate; - } - - function createTypePredicateFromTypePredicateNode( - node: TypePredicateNode, - signature: Signature - ): TypePredicate { - const parameterName = node.parameterName; - const type = node.type && getTypeFromTypeNode(node.type); - return parameterName.kind === SyntaxKind.ThisType - ? createTypePredicate( - node.assertsModifier - ? TypePredicateKind.AssertsThis - : TypePredicateKind.This, /*parameterName*/ - undefined, /*parameterIndex*/ - undefined, - type - ) - : createTypePredicate( - node.assertsModifier - ? TypePredicateKind.AssertsIdentifier - : TypePredicateKind.Identifier, - parameterName.escapedText as string, - findIndex( - signature.parameters, - p => p.escapedName === parameterName.escapedText - ), - type - ); - } - - function getReturnTypeOfSignature(signature: Signature): Type { - if (!signature.resolvedReturnType) { - if (!pushTypeResolution( - signature, - TypeSystemPropertyName.ResolvedReturnType - )) { - return errorType; - } - let type = signature.target - ? instantiateType( - getReturnTypeOfSignature(signature.target), - signature.mapper - ) - : signature.unionSignatures - ? getUnionType( - map( - signature.unionSignatures, - getReturnTypeOfSignature - ), - UnionReduction.Subtype - ) - : getReturnTypeFromAnnotation(signature.declaration!) - || (nodeIsMissing(( signature - .declaration).body) - ? anyType - : getReturnTypeFromBody( signature - .declaration)); - if (signature.flags & SignatureFlags.IsInnerCallChain) { - type = addOptionalTypeMarker(type); - } else if (signature.flags & SignatureFlags.IsOuterCallChain) { - type = getOptionalType(type); - } - if (!popTypeResolution()) { - if (signature.declaration) { - const typeNode = - getEffectiveReturnTypeNode(signature.declaration); - if (typeNode) { - error( - typeNode, - Diagnostics - .Return_type_annotation_circularly_references_itself - ); - } else if (noImplicitAny) { - const declaration = signature - .declaration; - const name = getNameOfDeclaration(declaration); - if (name) { - error( - name, - Diagnostics - ._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, - declarationNameToString(name) - ); - } else { - error( - declaration, - Diagnostics - .Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions - ); - } - } - } - type = anyType; - } - signature.resolvedReturnType = type; - } - return signature.resolvedReturnType; - } - - function getReturnTypeFromAnnotation(declaration: SignatureDeclaration - | JSDocSignature) - { - if (declaration.kind === SyntaxKind.Constructor) { - return getDeclaredTypeOfClassOrInterface(getMergedSymbol(( declaration - .parent).symbol)); - } - if (isJSDocConstructSignature(declaration)) { - return getTypeFromTypeNode((declaration.parameters - [0] as ParameterDeclaration).type!); // TODO: GH#18217 - } - const typeNode = getEffectiveReturnTypeNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - if (declaration.kind === SyntaxKind.GetAccessor - && !hasNonBindableDynamicName(declaration)) - { - const jsDocType = isInJSFile(declaration) - && getTypeForDeclarationFromJSDocComment(declaration); - if (jsDocType) { - return jsDocType; - } - const setter = getDeclarationOfKind( - getSymbolOfNode(declaration), - SyntaxKind.SetAccessor - ); - const setterType = getAnnotatedAccessorType(setter); - if (setterType) { - return setterType; - } - } - return getReturnTypeOfTypeTag(declaration); - } - - function isResolvingReturnTypeOfSignature(signature: Signature) { - return !signature.resolvedReturnType - && findResolutionCycleStartIndex( - signature, - TypeSystemPropertyName.ResolvedReturnType - ) >= 0; - } - - function getRestTypeOfSignature(signature: Signature): Type { - return tryGetRestTypeOfSignature(signature) || anyType; - } - - function tryGetRestTypeOfSignature(signature: Signature): Type - | undefined - { - if (signatureHasRestParameter(signature)) { - const sigRestType = - getTypeOfSymbol(signature.parameters - [signature.parameters.length - 1]); - const restType = isTupleType(sigRestType) - ? getRestTypeOfTupleType(sigRestType) - : sigRestType; - return restType - && getIndexTypeOfType(restType, IndexKind.Number); - } - return undefined; - } - - function getSignatureInstantiation( - signature: Signature, - typeArguments: Type[] | undefined, - isJavascript: boolean, - inferredTypeParameters?: readonly TypeParameter[] - ): Signature { - const instantiatedSignature = - getSignatureInstantiationWithoutFillingInTypeArguments( - signature, - fillMissingTypeArguments( - typeArguments, - signature.typeParameters, - getMinTypeArgumentCount(signature.typeParameters), - isJavascript - ) - ); - if (inferredTypeParameters) { - const returnSignature = - getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature)); - if (returnSignature) { - const newReturnSignature = cloneSignature(returnSignature); - newReturnSignature.typeParameters = inferredTypeParameters; - const newInstantiatedSignature = - cloneSignature(instantiatedSignature); - newInstantiatedSignature - .resolvedReturnType = getOrCreateTypeFromSignature(newReturnSignature); - return newInstantiatedSignature; - } - } - return instantiatedSignature; - } - - function getSignatureInstantiationWithoutFillingInTypeArguments( - signature: Signature, - typeArguments: readonly Type[] | undefined - ): Signature { - const instantiations = signature.instantiations - || (signature.instantiations = createMap()); - const id = getTypeListId(typeArguments); - let instantiation = instantiations.get(id); - if (!instantiation) { - instantiations.set( - id, - instantiation = createSignatureInstantiation( - signature, - typeArguments - ) - ); - } - return instantiation; - } - - function createSignatureInstantiation( - signature: Signature, - typeArguments: readonly Type[] | undefined - ): Signature { - return instantiateSignature( - signature, - createSignatureTypeMapper( - signature, - typeArguments - ), /*eraseTypeParameters*/ - true - ); - } - - function createSignatureTypeMapper( - signature: Signature, - typeArguments: readonly Type[] | undefined - ): TypeMapper { - return createTypeMapper(signature.typeParameters!, typeArguments); - } - - function getErasedSignature(signature: Signature): Signature { - return signature.typeParameters - ? signature.erasedSignatureCache - || (signature - .erasedSignatureCache = createErasedSignature(signature)) - : signature; - } - - function createErasedSignature(signature: Signature) { - // Create an instantiation of the signature where all type arguments are the any type. - return instantiateSignature( - signature, - createTypeEraser(signature - .typeParameters!), /*eraseTypeParameters*/ - true - ); - } - - function getCanonicalSignature(signature: Signature): Signature { - return signature.typeParameters - ? signature.canonicalSignatureCache - || (signature - .canonicalSignatureCache = createCanonicalSignature(signature)) - : signature; - } - - function createCanonicalSignature(signature: Signature) { - // Create an instantiation of the signature where each unconstrained type parameter is replaced with - // its original. When a generic class or interface is instantiated, each generic method in the class or - // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios - // where different generations of the same type parameter are in scope). This leads to a lot of new type - // identities, and potentially a lot of work comparing those identities, so here we create an instantiation - // that uses the original type identities for all unconstrained type parameters. - return getSignatureInstantiation( - signature, - map( - signature.typeParameters, - tp => tp.target && !getConstraintOfTypeParameter(tp.target) - ? tp.target - : tp - ), - isInJSFile(signature.declaration) - ); - } - - function getBaseSignature(signature: Signature) { - const typeParameters = signature.typeParameters; - if (typeParameters) { - const typeEraser = createTypeEraser(typeParameters); - const baseConstraints = map( - typeParameters, - tp => instantiateType( - getBaseConstraintOfType(tp), - typeEraser - ) || unknownType - ); - return instantiateSignature( - signature, - createTypeMapper( - typeParameters, - baseConstraints - ), /*eraseTypeParameters*/ - true - ); - } - return signature; - } - - function getOrCreateTypeFromSignature(signature: - Signature): ObjectType - { - // There are two ways to declare a construct signature, one is by declaring a class constructor - // using the constructor keyword, and the other is declaring a bare construct signature in an - // object type literal or interface (using the new keyword). Each way of declaring a constructor - // will result in a different declaration kind. - if (!signature.isolatedSignatureType) { - const kind = signature.declaration - ? signature.declaration.kind - : SyntaxKind.Unknown; - const isConstructor = kind === SyntaxKind.Constructor - || kind === SyntaxKind.ConstructSignature - || kind === SyntaxKind.ConstructorType; - const type = createObjectType(ObjectFlags.Anonymous); - type.members = emptySymbols; - type.properties = emptyArray; - type.callSignatures = !isConstructor - ? [signature] - : emptyArray; - type.constructSignatures = isConstructor - ? [signature] - : emptyArray; - signature.isolatedSignatureType = type; - } - - return signature.isolatedSignatureType; - } - - function getIndexSymbol(symbol: Symbol): Symbol | undefined { - return symbol.members!.get(InternalSymbolName.Index); - } - - function getIndexDeclarationOfSymbol( - symbol: Symbol, - kind: IndexKind - ): IndexSignatureDeclaration | undefined { - const syntaxKind = kind === IndexKind.Number - ? SyntaxKind.NumberKeyword - : SyntaxKind.StringKeyword; - const indexSymbol = getIndexSymbol(symbol); - if (indexSymbol) { - for (const decl of indexSymbol.declarations) { - const node = cast(decl, isIndexSignatureDeclaration); - if (node.parameters.length === 1) { - const parameter = node.parameters[0]; - if (parameter.type - && parameter.type.kind === syntaxKind) - { - return node; - } - } - } - } - - return undefined; - } - - function createIndexInfo( - type: Type, - isReadonly: boolean, - declaration?: IndexSignatureDeclaration - ): IndexInfo { - return { type, isReadonly, declaration }; - } - - function getIndexInfoOfSymbol( - symbol: Symbol, - kind: IndexKind - ): IndexInfo | undefined { - const declaration = getIndexDeclarationOfSymbol(symbol, kind); - if (declaration) { - return createIndexInfo( - declaration.type - ? getTypeFromTypeNode(declaration.type) - : anyType, - hasModifier(declaration, ModifierFlags.Readonly), - declaration - ); - } - return undefined; - } - - function getConstraintDeclaration(type: TypeParameter): TypeNode - | undefined - { - return mapDefined( - filter( - type.symbol && type.symbol.declarations, - isTypeParameterDeclaration - ), - getEffectiveConstraintOfTypeParameter - )[0]; - } - - function getInferredTypeParameterConstraint(typeParameter: - TypeParameter) - { - let inferences: Type[] | undefined; - if (typeParameter.symbol) { - for (const declaration of typeParameter.symbol.declarations) { - if (declaration.parent.kind === SyntaxKind.InferType) { - // When an 'infer T' declaration is immediately contained in a type reference node - // (such as 'Foo'), T's constraint is inferred from the constraint of the - // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are - // present, we form an intersection of the inferred constraint types. - const grandParent = declaration.parent.parent; - if (grandParent.kind === SyntaxKind.TypeReference) { - const typeReference = - grandParent; - const typeParameters = - getTypeParametersForTypeReference(typeReference); - if (typeParameters) { - const index = typeReference.typeArguments! - .indexOf( declaration.parent); - if (index < typeParameters.length) { - const declaredConstraint = - getConstraintOfTypeParameter(typeParameters - [index]); - if (declaredConstraint) { - // Type parameter constraints can reference other type parameters so - // constraints need to be instantiated. If instantiation produces the - // type parameter itself, we discard that inference. For example, in - // type Foo = [T, U]; - // type Bar = T extends Foo ? Foo : T; - // the instantiated constraint for U is X, so we discard that inference. - const mapper = createTypeMapper( - typeParameters, - getEffectiveTypeArguments( - typeReference, - typeParameters - ) - ); - const constraint = instantiateType( - declaredConstraint, - mapper - ); - if (constraint !== typeParameter) { - inferences = append( - inferences, - constraint - ); - } - } - } - } - } // When an 'infer T' declaration is immediately contained in a rest parameter - // declaration, we infer an 'unknown[]' constraint. - else if (grandParent.kind === SyntaxKind.Parameter - && ( grandParent) - .dotDotDotToken) - { - inferences = append( - inferences, - createArrayType(unknownType) - ); - } - } - } - } - return inferences && getIntersectionType(inferences); - } - - /** This is a worker function. Use getConstraintOfTypeParameter which guards against circular constraints. */ - function getConstraintFromTypeParameter(typeParameter: - TypeParameter): Type | undefined - { - if (!typeParameter.constraint) { - if (typeParameter.target) { - const targetConstraint = - getConstraintOfTypeParameter(typeParameter.target); - typeParameter.constraint = targetConstraint - ? instantiateType( - targetConstraint, - typeParameter.mapper - ) - : noConstraintType; - } else { - const constraintDeclaration = - getConstraintDeclaration(typeParameter); - typeParameter.constraint = constraintDeclaration - ? getTypeFromTypeNode(constraintDeclaration) - : getInferredTypeParameterConstraint(typeParameter) - || noConstraintType; - } - } - return typeParameter.constraint === noConstraintType - ? undefined - : typeParameter.constraint; - } - - function getParentSymbolOfTypeParameter(typeParameter: - TypeParameter): Symbol | undefined - { - const tp = getDeclarationOfKind( - typeParameter.symbol, - SyntaxKind.TypeParameter - )!; - const host = isJSDocTemplateTag(tp.parent) - ? getHostSignatureFromJSDoc(tp.parent) - : tp.parent; - return host && getSymbolOfNode(host); - } - - function getTypeListId(types: readonly Type[] | undefined) { - let result = ''; - if (types) { - const length = types.length; - let i = 0; - while (i < length) { - const startId = types[i].id; - let count = 1; - while (i + count < length - && types[i + count].id === startId + count) - { - count++; - } - if (result.length) { - result += ','; - } - result += startId; - if (count > 1) { - result += ':' + count; - } - i += count; - } - } - return result; - } - - // This function is used to propagate certain flags when creating new object type references and union types. - // It is only necessary to do so if a constituent type might be the undefined type, the null type, the type - // of an object literal or the anyFunctionType. This is because there are operations in the type checker - // that care about the presence of such types at arbitrary depth in a containing type. - function getPropagatingFlagsOfTypes( - types: readonly Type[], - excludeKinds: TypeFlags - ): ObjectFlags { - let result: ObjectFlags = 0; - for (const type of types) { - if (!(type.flags & excludeKinds)) { - result |= getObjectFlags(type); - } - } - return result & ObjectFlags.PropagatingFlags; - } - - function createTypeReference( - target: GenericType, - typeArguments: readonly Type[] | undefined - ): TypeReference { - const id = getTypeListId(typeArguments); - let type = target.instantiations.get(id); - if (!type) { - type = createObjectType( - ObjectFlags.Reference, - target.symbol - ); - target.instantiations.set(id, type); - type.objectFlags |= typeArguments - ? getPropagatingFlagsOfTypes( - typeArguments, /*excludeKinds*/ - 0 - ) - : 0; - type.target = target; - type.resolvedTypeArguments = typeArguments; - } - return type; - } - - function cloneTypeReference(source: TypeReference): TypeReference { - const type = createType(source.flags); - type.symbol = source.symbol; - type.objectFlags = source.objectFlags; - type.target = source.target; - type.resolvedTypeArguments = source.resolvedTypeArguments; - return type; - } - - function createDeferredTypeReference( - target: GenericType, - node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, - mapper?: TypeMapper - ): DeferredTypeReference { - const aliasSymbol = getAliasSymbolForTypeNode(node); - const aliasTypeArguments = - getTypeArgumentsForAliasSymbol(aliasSymbol); - const type = createObjectType( - ObjectFlags.Reference, - target.symbol - ); - type.target = target; - type.node = node; - type.mapper = mapper; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = mapper - ? instantiateTypes(aliasTypeArguments, mapper) - : aliasTypeArguments; - return type; - } - - function getTypeArguments(type: TypeReference): readonly Type[] { - if (!type.resolvedTypeArguments) { - if (!pushTypeResolution( - type, - TypeSystemPropertyName.ResolvedTypeArguments - )) { - return type.target.localTypeParameters - ?.map(() => errorType) || emptyArray; - } - const node = type.node; - const typeArguments = !node - ? emptyArray - : node.kind === SyntaxKind.TypeReference - ? concatenate( - type.target.outerTypeParameters, - getEffectiveTypeArguments( - node, - type.target.localTypeParameters! - ) - ) - : node.kind === SyntaxKind.ArrayType - ? [getTypeFromTypeNode(node.elementType)] - : map(node.elementTypes, getTypeFromTypeNode); - if (popTypeResolution()) { - type.resolvedTypeArguments = type.mapper - ? instantiateTypes(typeArguments, type.mapper) - : typeArguments; - } else { - type - .resolvedTypeArguments = type.target - .localTypeParameters?.map(() => errorType) - || emptyArray; - error( - type.node || currentNode, - type.target.symbol - ? Diagnostics - .Type_arguments_for_0_circularly_reference_themselves - : Diagnostics - .Tuple_type_arguments_circularly_reference_themselves, - type.target.symbol - && symbolToString(type.target.symbol) - ); - } - } - return type.resolvedTypeArguments; - } - - function getTypeReferenceArity(type: TypeReference): number { - return length(type.target.typeParameters); - } - - /** - * Get type from type-reference that reference to class or interface - */ - function getTypeFromClassOrInterfaceReference( - node: NodeWithTypeArguments, - symbol: Symbol - ): Type { - const type = - getDeclaredTypeOfSymbol(getMergedSymbol(symbol)); - const typeParameters = type.localTypeParameters; - if (typeParameters) { - const numTypeArguments = length(node.typeArguments); - const minTypeArgumentCount = - getMinTypeArgumentCount(typeParameters); - const isJs = isInJSFile(node); - const isJsImplicitAny = !noImplicitAny && isJs; - if (!isJsImplicitAny - && (numTypeArguments < minTypeArgumentCount - || numTypeArguments > typeParameters.length)) - { - const missingAugmentsTag = isJs - && isExpressionWithTypeArguments(node) - && !isJSDocAugmentsTag(node.parent); - const diag = minTypeArgumentCount === typeParameters.length - ? missingAugmentsTag - ? Diagnostics - .Expected_0_type_arguments_provide_these_with_an_extends_tag - : Diagnostics - .Generic_type_0_requires_1_type_argument_s - : missingAugmentsTag - ? Diagnostics - .Expected_0_1_type_arguments_provide_these_with_an_extends_tag - : Diagnostics - .Generic_type_0_requires_between_1_and_2_type_arguments; - - const typeStr = typeToString( - type, /*enclosingDeclaration*/ - undefined, - TypeFormatFlags.WriteArrayAsGenericType - ); - error( - node, - diag, - typeStr, - minTypeArgumentCount, - typeParameters.length - ); - if (!isJs) { - // TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments) - return errorType; - } - } - if (node.kind === SyntaxKind.TypeReference - && isAliasedType(node)) - { - return createDeferredTypeReference( - type, - node, /*mapper*/ - undefined - ); - } - // In a type reference, the outer type parameters of the referenced class or interface are automatically - // supplied as type arguments and the type reference only specifies arguments for the local type parameters - // of the class or interface. - const typeArguments = concatenate( - type.outerTypeParameters, - fillMissingTypeArguments( - typeArgumentsFromTypeReferenceNode(node), - typeParameters, - minTypeArgumentCount, - isJs - ) - ); - return createTypeReference( type, typeArguments); - } - return checkNoTypeArguments(node, symbol) ? type : errorType; - } - - function getTypeAliasInstantiation( - symbol: Symbol, - typeArguments: readonly Type[] | undefined - ): Type { - const type = getDeclaredTypeOfSymbol(symbol); - const links = getSymbolLinks(symbol); - const typeParameters = links.typeParameters!; - const id = getTypeListId(typeArguments); - let instantiation = links.instantiations!.get(id); - if (!instantiation) { - links.instantiations!.set( - id, - instantiation = instantiateType( - type, - createTypeMapper( - typeParameters, - fillMissingTypeArguments( - typeArguments, - typeParameters, - getMinTypeArgumentCount(typeParameters), - isInJSFile(symbol.valueDeclaration) - ) - ) - ) - ); - } - return instantiation; - } - - /** - * Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include - * references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the - * declared type. Instantiations are cached using the type identities of the type arguments as the key. - */ - function getTypeFromTypeAliasReference( - node: NodeWithTypeArguments, - symbol: Symbol - ): Type { - const type = getDeclaredTypeOfSymbol(symbol); - const typeParameters = getSymbolLinks(symbol).typeParameters; - if (typeParameters) { - const numTypeArguments = length(node.typeArguments); - const minTypeArgumentCount = - getMinTypeArgumentCount(typeParameters); - if (numTypeArguments < minTypeArgumentCount - || numTypeArguments > typeParameters.length) - { - error( - node, - minTypeArgumentCount === typeParameters.length - ? Diagnostics - .Generic_type_0_requires_1_type_argument_s - : Diagnostics - .Generic_type_0_requires_between_1_and_2_type_arguments, - symbolToString(symbol), - minTypeArgumentCount, - typeParameters.length - ); - return errorType; - } - return getTypeAliasInstantiation( - symbol, - typeArgumentsFromTypeReferenceNode(node) - ); - } - return checkNoTypeArguments(node, symbol) ? type : errorType; - } - - function getTypeReferenceName(node: - TypeReferenceType): EntityNameOrEntityNameExpression | undefined - { - switch (node.kind) { - case SyntaxKind.TypeReference: - return node.typeName; - case SyntaxKind.ExpressionWithTypeArguments: - // We only support expressions that are simple qualified names. For other - // expressions this produces undefined. - const expr = node.expression; - if (isEntityNameExpression(expr)) { - return expr; - } - // fall through; - } - - return undefined; - } - - function resolveTypeReferenceName( - typeReferenceName: EntityNameExpression | EntityName | undefined, - meaning: SymbolFlags, - ignoreErrors?: boolean - ) { - if (!typeReferenceName) { - return unknownSymbol; - } - - return resolveEntityName(typeReferenceName, meaning, ignoreErrors) - || unknownSymbol; - } - - function getTypeReferenceType( - node: NodeWithTypeArguments, - symbol: Symbol - ): Type { - if (symbol === unknownSymbol) { - return errorType; - } - symbol = getExpandoSymbol(symbol) || symbol; - if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - return getTypeFromClassOrInterfaceReference(node, symbol); - } - if (isTypeOnlyAlias(symbol)) { - return getTypeReferenceType(node, symbol.immediateTarget); - } - if (symbol.flags & SymbolFlags.TypeAlias) { - return getTypeFromTypeAliasReference(node, symbol); - } - // Get type from reference to named type that cannot be generic (enum or type parameter) - const res = tryGetDeclaredTypeOfSymbol(symbol); - if (res) { - return checkNoTypeArguments(node, symbol) - ? res.flags & TypeFlags.TypeParameter - ? getConstrainedTypeVariable( res, node) - : getRegularTypeOfLiteralType(res) - : errorType; - } - if (symbol.flags & SymbolFlags.Value - && isJSDocTypeReference(node)) - { - const jsdocType = getTypeFromJSDocValueReference(node, symbol); - if (jsdocType) { - return jsdocType; - } else { - // Resolve the type reference as a Type for the purpose of reporting errors. - resolveTypeReferenceName( - getTypeReferenceName(node), - SymbolFlags.Type - ); - return getTypeOfSymbol(symbol); - } - } - return errorType; - } - - /** - * A JSdoc TypeReference may be to a value, but resolve it as a type anyway. - * Note: If the value is imported from commonjs, it should really be an alias, - * but this function's special-case code fakes alias resolution as well. - */ - function getTypeFromJSDocValueReference( - node: NodeWithTypeArguments, - symbol: Symbol - ): Type | undefined { - const valueType = getTypeOfSymbol(symbol); - let typeType = valueType; - if (symbol.valueDeclaration) { - const decl = getRootDeclaration(symbol.valueDeclaration); - let isRequireAlias = false; - if (isVariableDeclaration(decl) && decl.initializer) { - let expr = decl.initializer; - // skip past entity names, eg `require("x").a.b.c` - while (isPropertyAccessExpression(expr)) { - expr = expr.expression; - } - isRequireAlias = isCallExpression(expr) - && isRequireCall( - expr, /*requireStringLiteralLikeArgument*/ - true - ) && !!valueType.symbol; - } - const isImportTypeWithQualifier = - node.kind === SyntaxKind.ImportType - && (node as ImportTypeNode).qualifier; - // valueType might not have a symbol, eg, {import('./b').STRING_LITERAL} - if (valueType.symbol - && (isRequireAlias || isImportTypeWithQualifier)) - { - typeType = getTypeReferenceType(node, valueType.symbol); - } - } - return getSymbolLinks(symbol).resolvedJSDocType = typeType; - } - - function getSubstitutionType( - typeVariable: TypeVariable, - substitute: Type - ) { - if (substitute.flags & TypeFlags.AnyOrUnknown - || substitute === typeVariable) - { - return typeVariable; - } - const id = `${getTypeId(typeVariable)}>${getTypeId(substitute)}`; - const cached = substitutionTypes.get(id); - if (cached) { - return cached; - } - const result = - createType(TypeFlags.Substitution); - result.typeVariable = typeVariable; - result.substitute = substitute; - substitutionTypes.set(id, result); - return result; - } - - function isUnaryTupleTypeNode(node: TypeNode) { - return node.kind === SyntaxKind.TupleType - && ( node).elementTypes.length === 1; - } - - function getImpliedConstraint( - typeVariable: TypeVariable, - checkNode: TypeNode, - extendsNode: TypeNode - ): Type | undefined { - return isUnaryTupleTypeNode(checkNode) - && isUnaryTupleTypeNode(extendsNode) - ? getImpliedConstraint( - typeVariable, - ( checkNode).elementTypes[0], - ( extendsNode).elementTypes[0] - ) - : getActualTypeVariable(getTypeFromTypeNode(checkNode)) - === typeVariable - ? getTypeFromTypeNode(extendsNode) - : undefined; - } - - function getConstrainedTypeVariable( - typeVariable: TypeVariable, - node: Node - ) { - let constraints: Type[] | undefined; - while (node && !isStatement(node) - && node.kind !== SyntaxKind.JSDocComment) - { - const parent = node.parent; - if (parent.kind === SyntaxKind.ConditionalType - && node === ( parent).trueType) - { - const constraint = getImpliedConstraint( - typeVariable, - ( parent).checkType, - ( parent).extendsType - ); - if (constraint) { - constraints = append(constraints, constraint); - } - } - node = parent; - } - return constraints - ? getSubstitutionType( - typeVariable, - getIntersectionType(append(constraints, typeVariable)) - ) - : typeVariable; - } - - function isJSDocTypeReference(node: Node): node is TypeReferenceNode { - return !!(node.flags & NodeFlags.JSDoc) - && (node.kind === SyntaxKind.TypeReference - || node.kind === SyntaxKind.ImportType); - } - - function checkNoTypeArguments( - node: NodeWithTypeArguments, - symbol?: Symbol - ) { - if (node.typeArguments) { - error( - node, - Diagnostics.Type_0_is_not_generic, - symbol - ? symbolToString(symbol) - : ( node).typeName - ? declarationNameToString(( node) - .typeName) - : anon - ); - return false; - } - return true; - } - - function getIntendedTypeFromJSDocTypeReference(node: - TypeReferenceNode): Type | undefined - { - if (isIdentifier(node.typeName)) { - const typeArgs = node.typeArguments; - switch (node.typeName.escapedText) { - case 'String': - checkNoTypeArguments(node); - return stringType; - case 'Number': - checkNoTypeArguments(node); - return numberType; - case 'Boolean': - checkNoTypeArguments(node); - return booleanType; - case 'Void': - checkNoTypeArguments(node); - return voidType; - case 'Undefined': - checkNoTypeArguments(node); - return undefinedType; - case 'Null': - checkNoTypeArguments(node); - return nullType; - case 'Function': - case 'function': - checkNoTypeArguments(node); - return globalFunctionType; - case 'array': - return (!typeArgs || !typeArgs.length) - && !noImplicitAny - ? anyArrayType - : undefined; - case 'promise': - return (!typeArgs || !typeArgs.length) - && !noImplicitAny - ? createPromiseType(anyType) - : undefined; - case 'Object': - if (typeArgs && typeArgs.length === 2) { - if (isJSDocIndexSignature(node)) { - const indexed = - getTypeFromTypeNode(typeArgs[0]); - const target = - getTypeFromTypeNode(typeArgs[1]); - const index = createIndexInfo( - target, /*isReadonly*/ - false - ); - return createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - indexed === stringType ? index : undefined, - indexed === numberType ? index : undefined - ); - } - return anyType; - } - checkNoTypeArguments(node); - return !noImplicitAny ? anyType : undefined; - } - } - } - - function getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) { - const type = getTypeFromTypeNode(node.type); - return strictNullChecks - ? getNullableType(type, TypeFlags.Null) - : type; - } - - function getTypeFromTypeReference(node: TypeReferenceType): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - let symbol: Symbol | undefined; - let type: Type | undefined; - const meaning = SymbolFlags.Type; - if (isJSDocTypeReference(node)) { - type = getIntendedTypeFromJSDocTypeReference(node); - if (!type) { - symbol = resolveTypeReferenceName( - getTypeReferenceName(node), - meaning, /*ignoreErrors*/ - true - ); - if (symbol === unknownSymbol) { - symbol = resolveTypeReferenceName( - getTypeReferenceName(node), - meaning | SymbolFlags.Value - ); - } else { - resolveTypeReferenceName( - getTypeReferenceName(node), - meaning - ); // Resolve again to mark errors, if any - } - type = getTypeReferenceType(node, symbol); - } - } - if (!type) { - symbol = resolveTypeReferenceName( - getTypeReferenceName(node), - meaning - ); - type = getTypeReferenceType(node, symbol); - } - // Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the - // type reference in checkTypeReferenceNode. - links.resolvedSymbol = symbol; - links.resolvedType = type; - } - return links.resolvedType; - } - - function typeArgumentsFromTypeReferenceNode(node: - NodeWithTypeArguments): Type[] | undefined - { - return map(node.typeArguments, getTypeFromTypeNode); - } - - function getTypeFromTypeQueryNode(node: TypeQueryNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - // TypeScript 1.0 spec (April 2014): 3.6.3 - // The expression is processed as an identifier expression (section 4.3) - // or property access expression(section 4.10), - // the widened type(section 3.9) of which becomes the result. - links - .resolvedType = getRegularTypeOfLiteralType(getWidenedType(checkExpression(node - .exprName))); - } - return links.resolvedType; - } - - function getTypeOfGlobalSymbol( - symbol: Symbol | undefined, - arity: number - ): ObjectType { - function getTypeDeclaration(symbol: Symbol): Declaration - | undefined - { - const declarations = symbol.declarations; - for (const declaration of declarations) { - switch (declaration.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - return declaration; - } - } - } - - if (!symbol) { - return arity ? emptyGenericType : emptyObjectType; - } - const type = getDeclaredTypeOfSymbol(symbol); - if (!(type.flags & TypeFlags.Object)) { - error( - getTypeDeclaration(symbol), - Diagnostics - .Global_type_0_must_be_a_class_or_interface_type, - symbolName(symbol) - ); - return arity ? emptyGenericType : emptyObjectType; - } - if (length(( type).typeParameters) !== arity) { - error( - getTypeDeclaration(symbol), - Diagnostics.Global_type_0_must_have_1_type_parameter_s, - symbolName(symbol), - arity - ); - return arity ? emptyGenericType : emptyObjectType; - } - return type; - } - - function getGlobalValueSymbol( - name: __String, - reportErrors: boolean - ): Symbol | undefined { - return getGlobalSymbol( - name, - SymbolFlags.Value, - reportErrors - ? Diagnostics.Cannot_find_global_value_0 - : undefined - ); - } - - function getGlobalTypeSymbol( - name: __String, - reportErrors: boolean - ): Symbol | undefined { - return getGlobalSymbol( - name, - SymbolFlags.Type, - reportErrors - ? Diagnostics.Cannot_find_global_type_0 - : undefined - ); - } - - function getGlobalSymbol( - name: __String, - meaning: SymbolFlags, - diagnostic: DiagnosticMessage | undefined - ): Symbol | undefined { - // Don't track references for global symbols anyway, so value if `isReference` is arbitrary - return resolveName( - undefined, - name, - meaning, - diagnostic, - name, /*isUse*/ - false - ); - } - - function getGlobalType( - name: __String, - arity: 0, - reportErrors: boolean - ): ObjectType; - function getGlobalType( - name: __String, - arity: number, - reportErrors: boolean - ): GenericType; - function getGlobalType( - name: __String, - arity: number, - reportErrors: boolean - ): ObjectType | undefined { - const symbol = getGlobalTypeSymbol(name, reportErrors); - return symbol || reportErrors - ? getTypeOfGlobalSymbol(symbol, arity) - : undefined; - } - - function getGlobalTypedPropertyDescriptorType() { - return deferredGlobalTypedPropertyDescriptorType - || (deferredGlobalTypedPropertyDescriptorType = getGlobalType( - 'TypedPropertyDescriptor' as __String, /*arity*/ - 1, /*reportErrors*/ - true - )) || emptyGenericType; - } - - function getGlobalTemplateStringsArrayType() { - return deferredGlobalTemplateStringsArrayType - || (deferredGlobalTemplateStringsArrayType = getGlobalType( - 'TemplateStringsArray' as __String, /*arity*/ - 0, /*reportErrors*/ - true - )) || emptyObjectType; - } - - function getGlobalImportMetaType() { - return deferredGlobalImportMetaType - || (deferredGlobalImportMetaType = getGlobalType( - 'ImportMeta' as __String, /*arity*/ - 0, /*reportErrors*/ - true - )) || emptyObjectType; - } - - function getGlobalESSymbolConstructorSymbol(reportErrors: boolean) { - return deferredGlobalESSymbolConstructorSymbol - || (deferredGlobalESSymbolConstructorSymbol = getGlobalValueSymbol( - 'Symbol' as __String, - reportErrors - )); - } - - function getGlobalESSymbolType(reportErrors: boolean) { - return deferredGlobalESSymbolType - || (deferredGlobalESSymbolType = getGlobalType( - 'Symbol' as __String, /*arity*/ - 0, - reportErrors - )) || emptyObjectType; - } - - function getGlobalPromiseType(reportErrors: boolean) { - return deferredGlobalPromiseType - || (deferredGlobalPromiseType = getGlobalType( - 'Promise' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalPromiseLikeType(reportErrors: boolean) { - return deferredGlobalPromiseLikeType - || (deferredGlobalPromiseLikeType = getGlobalType( - 'PromiseLike' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalPromiseConstructorSymbol(reportErrors: - boolean): Symbol | undefined - { - return deferredGlobalPromiseConstructorSymbol - || (deferredGlobalPromiseConstructorSymbol = getGlobalValueSymbol( - 'Promise' as __String, - reportErrors - )); - } - - function getGlobalPromiseConstructorLikeType(reportErrors: boolean) { - return deferredGlobalPromiseConstructorLikeType - || (deferredGlobalPromiseConstructorLikeType = getGlobalType( - 'PromiseConstructorLike' as __String, /*arity*/ - 0, - reportErrors - )) || emptyObjectType; - } - - function getGlobalAsyncIterableType(reportErrors: boolean) { - return deferredGlobalAsyncIterableType - || (deferredGlobalAsyncIterableType = getGlobalType( - 'AsyncIterable' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalAsyncIteratorType(reportErrors: boolean) { - return deferredGlobalAsyncIteratorType - || (deferredGlobalAsyncIteratorType = getGlobalType( - 'AsyncIterator' as __String, /*arity*/ - 3, - reportErrors - )) || emptyGenericType; - } - - function getGlobalAsyncIterableIteratorType(reportErrors: boolean) { - return deferredGlobalAsyncIterableIteratorType - || (deferredGlobalAsyncIterableIteratorType = getGlobalType( - 'AsyncIterableIterator' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalAsyncGeneratorType(reportErrors: boolean) { - return deferredGlobalAsyncGeneratorType - || (deferredGlobalAsyncGeneratorType = getGlobalType( - 'AsyncGenerator' as __String, /*arity*/ - 3, - reportErrors - )) || emptyGenericType; - } - - function getGlobalIterableType(reportErrors: boolean) { - return deferredGlobalIterableType - || (deferredGlobalIterableType = getGlobalType( - 'Iterable' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalIteratorType(reportErrors: boolean) { - return deferredGlobalIteratorType - || (deferredGlobalIteratorType = getGlobalType( - 'Iterator' as __String, /*arity*/ - 3, - reportErrors - )) || emptyGenericType; - } - - function getGlobalIterableIteratorType(reportErrors: boolean) { - return deferredGlobalIterableIteratorType - || (deferredGlobalIterableIteratorType = getGlobalType( - 'IterableIterator' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalGeneratorType(reportErrors: boolean) { - return deferredGlobalGeneratorType - || (deferredGlobalGeneratorType = getGlobalType( - 'Generator' as __String, /*arity*/ - 3, - reportErrors - )) || emptyGenericType; - } - - function getGlobalIteratorYieldResultType(reportErrors: boolean) { - return deferredGlobalIteratorYieldResultType - || (deferredGlobalIteratorYieldResultType = getGlobalType( - 'IteratorYieldResult' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalIteratorReturnResultType(reportErrors: boolean) { - return deferredGlobalIteratorReturnResultType - || (deferredGlobalIteratorReturnResultType = getGlobalType( - 'IteratorReturnResult' as __String, /*arity*/ - 1, - reportErrors - )) || emptyGenericType; - } - - function getGlobalTypeOrUndefined( - name: __String, - arity = 0 - ): ObjectType | undefined { - const symbol = getGlobalSymbol( - name, - SymbolFlags.Type, /*diagnostic*/ - undefined - ); - return symbol - && getTypeOfGlobalSymbol(symbol, arity); - } - - function getGlobalExtractSymbol(): Symbol { - return deferredGlobalExtractSymbol - || (deferredGlobalExtractSymbol = getGlobalSymbol( - 'Extract' as __String, - SymbolFlags.TypeAlias, - Diagnostics.Cannot_find_global_type_0 - )!); // TODO: GH#18217 - } - - function getGlobalOmitSymbol(): Symbol { - return deferredGlobalOmitSymbol - || (deferredGlobalOmitSymbol = getGlobalSymbol( - 'Omit' as __String, - SymbolFlags.TypeAlias, - Diagnostics.Cannot_find_global_type_0 - )!); // TODO: GH#18217 - } - - function getGlobalBigIntType(reportErrors: boolean) { - return deferredGlobalBigIntType - || (deferredGlobalBigIntType = getGlobalType( - 'BigInt' as __String, /*arity*/ - 0, - reportErrors - )) || emptyObjectType; - } - - /** - * Instantiates a global type that is generic with some element type, and returns that instantiation. - */ - function createTypeFromGenericGlobalType( - genericGlobalType: GenericType, - typeArguments: readonly Type[] - ): ObjectType { - return genericGlobalType !== emptyGenericType - ? createTypeReference(genericGlobalType, typeArguments) - : emptyObjectType; - } - - function createTypedPropertyDescriptorType(propertyType: Type): Type { - return createTypeFromGenericGlobalType( - getGlobalTypedPropertyDescriptorType(), - [propertyType] - ); - } - - function createIterableType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType( - getGlobalIterableType(/*reportErrors*/ true), - [iteratedType] - ); - } - - function createArrayType( - elementType: Type, - readonly?: boolean - ): ObjectType { - return createTypeFromGenericGlobalType( - readonly ? globalReadonlyArrayType : globalArrayType, - [elementType] - ); - } - - function getArrayOrTupleTargetType(node: ArrayTypeNode - | TupleTypeNode): GenericType - { - const readonly = isReadonlyTypeOperator(node.parent); - if (node.kind === SyntaxKind.ArrayType - || node.elementTypes.length === 1 - && node.elementTypes[0].kind === SyntaxKind.RestType) - { - return readonly ? globalReadonlyArrayType : globalArrayType; - } - const lastElement = lastOrUndefined(node.elementTypes); - const restElement = - lastElement && lastElement.kind === SyntaxKind.RestType - ? lastElement - : undefined; - const minLength = - findLastIndex( - node.elementTypes, - n => n.kind !== SyntaxKind.OptionalType - && n !== restElement - ) + 1; - return getTupleTypeOfArity( - node.elementTypes.length, - minLength, - !!restElement, - readonly, /*associatedNames*/ - undefined - ); - } - - // Return true when the given node is transitively contained in type constructs that eagerly - // resolve their constituent types. We include SyntaxKind.TypeReference because type arguments - // of type aliases are eagerly resolved. - function isAliasedType(node: Node): boolean { - const parent = node.parent; - switch (parent.kind) { - case SyntaxKind.ParenthesizedType: - case SyntaxKind.TypeReference: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - case SyntaxKind.IndexedAccessType: - case SyntaxKind.ConditionalType: - case SyntaxKind.TypeOperator: - return isAliasedType(parent); - case SyntaxKind.TypeAliasDeclaration: - return true; - } - return false; - } - - function getTypeFromArrayOrTupleTypeNode(node: ArrayTypeNode - | TupleTypeNode): Type - { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const target = getArrayOrTupleTargetType(node); - if (target === emptyGenericType) { - links.resolvedType = emptyObjectType; - } else if (isAliasedType(node)) { - links - .resolvedType = node.kind === SyntaxKind.TupleType - && node.elementTypes.length === 0 - ? target - : createDeferredTypeReference( - target, - node, /*mapper*/ - undefined - ); - } else { - const elementTypes = node.kind === SyntaxKind.ArrayType - ? [getTypeFromTypeNode(node.elementType)] - : map(node.elementTypes, getTypeFromTypeNode); - links.resolvedType = createTypeReference( - target, - elementTypes - ); - } - } - return links.resolvedType; - } - - function isReadonlyTypeOperator(node: Node) { - return isTypeOperatorNode(node) - && node.operator === SyntaxKind.ReadonlyKeyword; - } - - // We represent tuple types as type references to synthesized generic interface types created by - // this function. The types are of the form: - // - // interface Tuple extends Array { 0: T0, 1: T1, 2: T2, ... } - // - // Note that the generic type created by this function has no symbol associated with it. The same - // is true for each of the synthesized type parameters. - function createTupleTypeOfArity( - arity: number, - minLength: number, - hasRestElement: boolean, - readonly: boolean, - associatedNames: __String[] | undefined - ): TupleType { - let typeParameters: TypeParameter[] | undefined; - const properties: Symbol[] = []; - const maxLength = hasRestElement ? arity - 1 : arity; - if (arity) { - typeParameters = new Array(arity); - for (let i = 0; i < arity; i++) { - const typeParameter = typeParameters - [i] = createTypeParameter(); - if (i < maxLength) { - const property = createSymbol( - SymbolFlags.Property | (i >= minLength - ? SymbolFlags.Optional - : 0), - '' + i as __String, - readonly ? CheckFlags.Readonly : 0 - ); - property.type = typeParameter; - properties.push(property); - } - } - } - const literalTypes = []; - for (let i = minLength; i <= maxLength; i++) { - literalTypes.push(getLiteralType(i)); - } - const lengthSymbol = createSymbol( - SymbolFlags.Property, - 'length' as __String - ); - lengthSymbol.type = hasRestElement - ? numberType - : getUnionType(literalTypes); - properties.push(lengthSymbol); - const type = createObjectType(ObjectFlags - .Tuple | ObjectFlags.Reference); - type.typeParameters = typeParameters; - type.outerTypeParameters = undefined; - type.localTypeParameters = typeParameters; - type.instantiations = createMap(); - type.instantiations.set( - getTypeListId(type.typeParameters), - type - ); - type.target = type; - type.resolvedTypeArguments = type.typeParameters; - type.thisType = createTypeParameter(); - type.thisType.isThisType = true; - type.thisType.constraint = type; - type.declaredProperties = properties; - type.declaredCallSignatures = emptyArray; - type.declaredConstructSignatures = emptyArray; - type.declaredStringIndexInfo = undefined; - type.declaredNumberIndexInfo = undefined; - type.minLength = minLength; - type.hasRestElement = hasRestElement; - type.readonly = readonly; - type.associatedNames = associatedNames; - return type; - } - - function getTupleTypeOfArity( - arity: number, - minLength: number, - hasRestElement: boolean, - readonly: boolean, - associatedNames?: __String[] - ): GenericType { - const key = arity + (hasRestElement ? '+' : ',') + minLength - + (readonly ? 'R' : '') - + (associatedNames && associatedNames.length - ? ',' + associatedNames.join(',') - : ''); - let type = tupleTypes.get(key); - if (!type) { - tupleTypes.set( - key, - type = createTupleTypeOfArity( - arity, - minLength, - hasRestElement, - readonly, - associatedNames - ) - ); - } - return type; - } - - function createTupleType( - elementTypes: readonly Type[], - minLength = elementTypes.length, - hasRestElement = false, - readonly = false, - associatedNames?: __String[] - ) { - const arity = elementTypes.length; - if (arity === 1 && hasRestElement) { - return createArrayType(elementTypes[0], readonly); - } - const tupleType = getTupleTypeOfArity( - arity, - minLength, - arity > 0 && hasRestElement, - readonly, - associatedNames - ); - return elementTypes.length - ? createTypeReference(tupleType, elementTypes) - : tupleType; - } - - function sliceTupleType(type: TupleTypeReference, index: number) { - const tuple = type.target; - if (tuple.hasRestElement) { - // don't slice off rest element - index = Math.min(index, getTypeReferenceArity(type) - 1); - } - return createTupleType( - getTypeArguments(type).slice(index), - Math.max(0, tuple.minLength - index), - tuple.hasRestElement, - tuple.readonly, - tuple.associatedNames && tuple.associatedNames.slice(index) - ); - } - - function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type { - const type = getTypeFromTypeNode(node.type); - return strictNullChecks ? getOptionalType(type) : type; - } - - function getTypeId(type: Type) { - return type.id; - } - - function containsType(types: readonly Type[], type: Type): boolean { - return binarySearch(types, type, getTypeId, compareValues) >= 0; - } - - function insertType(types: Type[], type: Type): boolean { - const index = binarySearch(types, type, getTypeId, compareValues); - if (index < 0) { - types.splice(~index, 0, type); - return true; - } - return false; - } - - function addTypeToUnion( - typeSet: Type[], - includes: TypeFlags, - type: Type - ) { - const flags = type.flags; - if (flags & TypeFlags.Union) { - return addTypesToUnion( - typeSet, - includes, - ( type).types - ); - } - // We ignore 'never' types in unions - if (!(flags & TypeFlags.Never)) { - includes |= flags & TypeFlags.IncludesMask; - if (flags - & TypeFlags.StructuredOrInstantiable) - includes |= TypeFlags.IncludesStructuredOrInstantiable; - if (type === wildcardType) { - includes |= TypeFlags.IncludesWildcard; - } - if (!strictNullChecks && flags & TypeFlags.Nullable) { - if (!(getObjectFlags(type) - & ObjectFlags.ContainsWideningType)) - { - includes |= TypeFlags.IncludesNonWideningType; - } - } else { - const len = typeSet.length; - const index = len && type.id > typeSet[len - 1].id - ? ~len - : binarySearch( - typeSet, - type, - getTypeId, - compareValues - ); - if (index < 0) { - typeSet.splice(~index, 0, type); - } - } - } - return includes; - } - - // Add the given types to the given type set. Order is preserved, duplicates are removed, - // and nested types of the given kind are flattened into the set. - function addTypesToUnion( - typeSet: Type[], - includes: TypeFlags, - types: readonly Type[] - ): TypeFlags { - for (const type of types) { - includes = addTypeToUnion(typeSet, includes, type); - } - return includes; - } - - function isSetOfLiteralsFromSameEnum(types: readonly Type[]): boolean { - const first = types[0]; - if (first.flags & TypeFlags.EnumLiteral) { - const firstEnum = getParentOfSymbol(first.symbol); - for (let i = 1; i < types.length; i++) { - const other = types[i]; - if (!(other.flags & TypeFlags.EnumLiteral) - || (firstEnum !== getParentOfSymbol(other.symbol))) - { - return false; - } - } - return true; - } - - return false; - } - - function removeSubtypes( - types: Type[], - primitivesOnly: boolean - ): boolean { - const len = types.length; - if (len === 0 || isSetOfLiteralsFromSameEnum(types)) { - return true; - } - let i = len; - let count = 0; - while (i > 0) { - i--; - const source = types[i]; - for (const target of types) { - if (source !== target) { - if (count === 100000) { - // After 100000 subtype checks we estimate the remaining amount of work by assuming the - // same ratio of checks per element. If the estimated number of remaining type checks is - // greater than an upper limit we deem the union type too complex to represent. The - // upper limit is 25M for unions of primitives only, and 1M otherwise. This for example - // caps union types at 5000 unique literal types and 1000 unique object types. - const estimatedCount = (count / (len - i)) * len; - if (estimatedCount - > (primitivesOnly ? 25000000 : 1000000)) - { - error( - currentNode, - Diagnostics - .Expression_produces_a_union_type_that_is_too_complex_to_represent - ); - return false; - } - } - count++; - if (isTypeRelatedTo( - source, - target, - strictSubtypeRelation - ) && ( - !(getObjectFlags(getTargetType(source)) - & ObjectFlags.Class) - || !(getObjectFlags(getTargetType(target)) - & ObjectFlags.Class) - || isTypeDerivedFrom(source, target) - )) { - orderedRemoveItemAt(types, i); - break; - } - } - } - } - return true; - } - - function removeRedundantLiteralTypes( - types: Type[], - includes: TypeFlags - ) { - let i = types.length; - while (i > 0) { - i--; - const t = types[i]; - const remove = t.flags & TypeFlags.StringLiteral - && includes & TypeFlags.String - || t.flags & TypeFlags.NumberLiteral - && includes & TypeFlags.Number - || t.flags & TypeFlags.BigIntLiteral - && includes & TypeFlags.BigInt - || t.flags & TypeFlags.UniqueESSymbol - && includes & TypeFlags.ESSymbol - || isFreshLiteralType(t) - && containsType(types, ( t).regularType); - if (remove) { - orderedRemoveItemAt(types, i); - } - } - } - - // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction - // flag is specified we also reduce the constituent type set to only include types that aren't subtypes - // of other types. Subtype reduction is expensive for large union types and is possible only when union - // types are known not to circularly reference themselves (as is the case with union types created by - // expression constructs such as array literals and the || and ?: operators). Named types can - // circularly reference themselves and therefore cannot be subtype reduced during their declaration. - // For example, "type Item = string | (() => Item" is a named type that circularly references itself. - function getUnionType( - types: readonly Type[], - unionReduction: UnionReduction = UnionReduction.Literal, - aliasSymbol?: Symbol, - aliasTypeArguments?: readonly Type[] - ): Type { - if (types.length === 0) { - return neverType; - } - if (types.length === 1) { - return types[0]; - } - const typeSet: Type[] = []; - const includes = addTypesToUnion(typeSet, 0, types); - if (unionReduction !== UnionReduction.None) { - if (includes & TypeFlags.AnyOrUnknown) { - return includes & TypeFlags.Any - ? includes & TypeFlags.IncludesWildcard - ? wildcardType - : anyType - : unknownType; - } - switch (unionReduction) { - case UnionReduction.Literal: - if (includes - & (TypeFlags.Literal | TypeFlags.UniqueESSymbol)) - { - removeRedundantLiteralTypes(typeSet, includes); - } - break; - case UnionReduction.Subtype: - if (!removeSubtypes( - typeSet, - !(includes - & TypeFlags.IncludesStructuredOrInstantiable) - )) { - return errorType; - } - break; - } - if (typeSet.length === 0) { - return includes & TypeFlags.Null - ? includes & TypeFlags.IncludesNonWideningType - ? nullType - : nullWideningType - : includes & TypeFlags.Undefined - ? includes & TypeFlags.IncludesNonWideningType - ? undefinedType - : undefinedWideningType - : neverType; - } - } - return getUnionTypeFromSortedList( - typeSet, - includes & TypeFlags.NotPrimitiveUnion - ? 0 - : ObjectFlags.PrimitiveUnion, - aliasSymbol, - aliasTypeArguments - ); - } - - function getUnionTypePredicate(signatures: - readonly Signature[]): TypePredicate | undefined - { - let first: TypePredicate | undefined; - const types: Type[] = []; - for (const sig of signatures) { - const pred = getTypePredicateOfSignature(sig); - if (!pred || pred.kind === TypePredicateKind.AssertsThis - || pred.kind === TypePredicateKind.AssertsIdentifier) - { - continue; - } - - if (first) { - if (!typePredicateKindsMatch(first, pred)) { - // No common type predicate. - return undefined; - } - } else { - first = pred; - } - types.push(pred.type); - } - if (!first) { - // No union signatures had a type predicate. - return undefined; - } - const unionType = getUnionType(types); - return createTypePredicate( - first.kind, - first.parameterName, - first.parameterIndex, - unionType - ); - } - - function typePredicateKindsMatch( - a: TypePredicate, - b: TypePredicate - ): boolean { - return a.kind === b.kind && a.parameterIndex === b.parameterIndex; - } - - // This function assumes the constituent type list is sorted and deduplicated. - function getUnionTypeFromSortedList( - types: Type[], - objectFlags: ObjectFlags, - aliasSymbol?: Symbol, - aliasTypeArguments?: readonly Type[] - ): Type { - if (types.length === 0) { - return neverType; - } - if (types.length === 1) { - return types[0]; - } - const id = getTypeListId(types); - let type = unionTypes.get(id); - if (!type) { - type = createType(TypeFlags.Union); - unionTypes.set(id, type); - type - .objectFlags = objectFlags - | getPropagatingFlagsOfTypes( - types, /*excludeKinds*/ - TypeFlags.Nullable - ); - type.types = types; - /* - Note: This is the alias symbol (or lack thereof) that we see when we first encounter this union type. - For aliases of identical unions, eg `type T = A | B; type U = A | B`, the symbol of the first alias encountered is the aliasSymbol. - (In the language service, the order may depend on the order in which a user takes actions, such as hovering over symbols.) - It's important that we create equivalent union types only once, so that's an unfortunate side effect. - */ - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; - } - return type; - } - - function getTypeFromUnionTypeNode(node: UnionTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const aliasSymbol = getAliasSymbolForTypeNode(node); - links.resolvedType = getUnionType( - map(node.types, getTypeFromTypeNode), - UnionReduction.Literal, - aliasSymbol, - getTypeArgumentsForAliasSymbol(aliasSymbol) - ); - } - return links.resolvedType; - } - - function addTypeToIntersection( - typeSet: Map, - includes: TypeFlags, - type: Type - ) { - const flags = type.flags; - if (flags & TypeFlags.Intersection) { - return addTypesToIntersection( - typeSet, - includes, - ( type).types - ); - } - if (isEmptyAnonymousObjectType(type)) { - if (!(includes & TypeFlags.IncludesEmptyObject)) { - includes |= TypeFlags.IncludesEmptyObject; - typeSet.set(type.id.toString(), type); - } - } else { - if (flags & TypeFlags.AnyOrUnknown) { - if (type === wildcardType) { - includes |= TypeFlags.IncludesWildcard; - } - } else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) - && !typeSet.has(type.id.toString())) - { - if (type.flags & TypeFlags.Unit - && includes & TypeFlags.Unit) - { - // We have seen two distinct unit types which means we should reduce to an - // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen. - includes |= TypeFlags.NonPrimitive; - } - typeSet.set(type.id.toString(), type); - } - includes |= flags & TypeFlags.IncludesMask; - } - return includes; - } - - // Add the given types to the given type set. Order is preserved, freshness is removed from literal - // types, duplicates are removed, and nested types of the given kind are flattened into the set. - function addTypesToIntersection( - typeSet: Map, - includes: TypeFlags, - types: readonly Type[] - ) { - for (const type of types) { - includes = addTypeToIntersection( - typeSet, - includes, - getRegularTypeOfLiteralType(type) - ); - } - return includes; - } - - function removeRedundantPrimitiveTypes( - types: Type[], - includes: TypeFlags - ) { - let i = types.length; - while (i > 0) { - i--; - const t = types[i]; - const remove = t.flags & TypeFlags.String - && includes & TypeFlags.StringLiteral - || t.flags & TypeFlags.Number - && includes & TypeFlags.NumberLiteral - || t.flags & TypeFlags.BigInt - && includes & TypeFlags.BigIntLiteral - || t.flags & TypeFlags.ESSymbol - && includes & TypeFlags.UniqueESSymbol; - if (remove) { - orderedRemoveItemAt(types, i); - } - } - } - - // Check that the given type has a match in every union. A given type is matched by - // an identical type, and a literal type is additionally matched by its corresponding - // primitive type. - function eachUnionContains(unionTypes: UnionType[], type: Type) { - for (const u of unionTypes) { - if (!containsType(u.types, type)) { - const primitive = type.flags & TypeFlags.StringLiteral - ? stringType - : type.flags & TypeFlags.NumberLiteral - ? numberType - : type.flags & TypeFlags.BigIntLiteral - ? bigintType - : type.flags & TypeFlags.UniqueESSymbol - ? esSymbolType - : undefined; - if (!primitive || !containsType(u.types, primitive)) { - return false; - } - } - } - return true; - } - - function extractIrreducible(types: Type[], flag: TypeFlags) { - if (every( - types, - t => !!(t.flags & TypeFlags.Union) - && some((t as UnionType).types, tt => !!(tt.flags & flag)) - )) { - for (let i = 0; i < types.length; i++) { - types[i] = filterType(types[i], t => !(t.flags & flag)); - } - return true; - } - return false; - } - - // If the given list of types contains more than one union of primitive types, replace the - // first with a union containing an intersection of those primitive types, then remove the - // other unions and return true. Otherwise, do nothing and return false. - function intersectUnionsOfPrimitiveTypes(types: Type[]) { - let unionTypes: UnionType[] | undefined; - const index = findIndex( - types, - t => !!(getObjectFlags(t) & ObjectFlags.PrimitiveUnion) - ); - if (index < 0) { - return false; - } - let i = index + 1; - // Remove all but the first union of primitive types and collect them in - // the unionTypes array. - while (i < types.length) { - const t = types[i]; - if (getObjectFlags(t) & ObjectFlags.PrimitiveUnion) { - (unionTypes || (unionTypes = [ types[index]])) - .push( t); - orderedRemoveItemAt(types, i); - } else { - i++; - } - } - // Return false if there was only one union of primitive types - if (!unionTypes) { - return false; - } - // We have more than one union of primitive types, now intersect them. For each - // type in each union we check if the type is matched in every union and if so - // we include it in the result. - const checked: Type[] = []; - const result: Type[] = []; - for (const u of unionTypes) { - for (const t of u.types) { - if (insertType(checked, t)) { - if (eachUnionContains(unionTypes, t)) { - insertType(result, t); - } - } - } - } - // Finally replace the first union with the result - types[index] = getUnionTypeFromSortedList( - result, - ObjectFlags.PrimitiveUnion - ); - return true; - } - - function createIntersectionType( - types: Type[], - aliasSymbol?: Symbol, - aliasTypeArguments?: readonly Type[] - ) { - const result = - createType(TypeFlags.Intersection); - result.objectFlags = getPropagatingFlagsOfTypes( - types, /*excludeKinds*/ - TypeFlags.Nullable - ); - result.types = types; - result - .aliasSymbol = aliasSymbol; // See comment in `getUnionTypeFromSortedList`. - result.aliasTypeArguments = aliasTypeArguments; - return result; - } - - // We normalize combinations of intersection and union types based on the distributive property of the '&' - // operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection - // types with union type constituents into equivalent union types with intersection type constituents and - // effectively ensure that union types are always at the top level in type representations. - // - // We do not perform structural deduplication on intersection types. Intersection types are created only by the & - // type operator and we can't reduce those because we want to support recursive intersection types. For example, - // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. - // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution - // for intersections of types with signatures can be deterministic. - function getIntersectionType( - types: readonly Type[], - aliasSymbol?: Symbol, - aliasTypeArguments?: readonly Type[] - ): Type { - const typeMembershipMap: Map = createMap(); - const includes = addTypesToIntersection( - typeMembershipMap, - 0, - types - ); - const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); - // An intersection type is considered empty if it contains - // the type never, or - // more than one unit type or, - // an object type and a nullable type (null or undefined), or - // a string-like type and a type known to be non-string-like, or - // a number-like type and a type known to be non-number-like, or - // a symbol-like type and a type known to be non-symbol-like, or - // a void-like type and a type known to be non-void-like, or - // a non-primitive type and a type known to be primitive. - if (includes & TypeFlags.Never - || strictNullChecks && includes & TypeFlags.Nullable - && includes - & (TypeFlags.Object | TypeFlags.NonPrimitive - | TypeFlags.IncludesEmptyObject) - || includes & TypeFlags.NonPrimitive - && includes - & (TypeFlags.DisjointDomains & ~TypeFlags.NonPrimitive) - || includes & TypeFlags.StringLike - && includes - & (TypeFlags.DisjointDomains & ~TypeFlags.StringLike) - || includes & TypeFlags.NumberLike - && includes - & (TypeFlags.DisjointDomains & ~TypeFlags.NumberLike) - || includes & TypeFlags.BigIntLike - && includes - & (TypeFlags.DisjointDomains & ~TypeFlags.BigIntLike) - || includes & TypeFlags.ESSymbolLike - && includes - & (TypeFlags.DisjointDomains & ~TypeFlags.ESSymbolLike) - || includes & TypeFlags.VoidLike - && includes - & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) - { - return neverType; - } - if (includes & TypeFlags.Any) { - return includes & TypeFlags.IncludesWildcard - ? wildcardType - : anyType; - } - if (!strictNullChecks && includes & TypeFlags.Nullable) { - return includes & TypeFlags.Undefined - ? undefinedType - : nullType; - } - if (includes & TypeFlags.String - && includes & TypeFlags.StringLiteral - || includes & TypeFlags.Number - && includes & TypeFlags.NumberLiteral - || includes & TypeFlags.BigInt - && includes & TypeFlags.BigIntLiteral - || includes & TypeFlags.ESSymbol - && includes & TypeFlags.UniqueESSymbol) - { - removeRedundantPrimitiveTypes(typeSet, includes); - } - if (includes & TypeFlags.IncludesEmptyObject - && includes & TypeFlags.Object) - { - orderedRemoveItemAt( - typeSet, - findIndex(typeSet, isEmptyAnonymousObjectType) - ); - } - if (typeSet.length === 0) { - return unknownType; - } - if (typeSet.length === 1) { - return typeSet[0]; - } - const id = getTypeListId(typeSet); - let result = intersectionTypes.get(id); - if (!result) { - if (includes & TypeFlags.Union) { - if (intersectUnionsOfPrimitiveTypes(typeSet)) { - // When the intersection creates a reduced set (which might mean that *all* union types have - // disappeared), we restart the operation to get a new set of combined flags. Once we have - // reduced we'll never reduce again, so this occurs at most once. - result = getIntersectionType( - typeSet, - aliasSymbol, - aliasTypeArguments - ); - } else if (extractIrreducible( - typeSet, - TypeFlags.Undefined - )) { - result = getUnionType( - [getIntersectionType(typeSet), undefinedType], - UnionReduction.Literal, - aliasSymbol, - aliasTypeArguments - ); - } else if (extractIrreducible(typeSet, TypeFlags.Null)) { - result = getUnionType( - [getIntersectionType(typeSet), nullType], - UnionReduction.Literal, - aliasSymbol, - aliasTypeArguments - ); - } else { - // We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of - // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. - // If the estimated size of the resulting union type exceeds 100000 constituents, report an error. - const size = reduceLeft( - typeSet, - (n, t) => n * (t.flags & TypeFlags.Union - ? ( t).types.length - : 1), - 1 - ); - if (size >= 100000) { - error( - currentNode, - Diagnostics - .Expression_produces_a_union_type_that_is_too_complex_to_represent - ); - return errorType; - } - const unionIndex = findIndex( - typeSet, - t => (t.flags & TypeFlags.Union) !== 0 - ); - const unionType = typeSet[unionIndex]; - result = getUnionType( - map( - unionType.types, - t => getIntersectionType(replaceElement( - typeSet, - unionIndex, - t - )) - ), - UnionReduction.Literal, - aliasSymbol, - aliasTypeArguments - ); - } - } else { - result = createIntersectionType( - typeSet, - aliasSymbol, - aliasTypeArguments - ); - } - intersectionTypes.set(id, result); - } - return result; - } - - function getTypeFromIntersectionTypeNode(node: - IntersectionTypeNode): Type - { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const aliasSymbol = getAliasSymbolForTypeNode(node); - links.resolvedType = getIntersectionType( - map(node.types, getTypeFromTypeNode), - aliasSymbol, - getTypeArgumentsForAliasSymbol(aliasSymbol) - ); - } - return links.resolvedType; - } - - function createIndexType( - type: InstantiableType | UnionOrIntersectionType, - stringsOnly: boolean - ) { - const result = createType(TypeFlags.Index); - result.type = type; - result.stringsOnly = stringsOnly; - return result; - } - - function getIndexTypeForGenericType( - type: InstantiableType | UnionOrIntersectionType, - stringsOnly: boolean - ) { - return stringsOnly - ? type.resolvedStringIndexType - || (type.resolvedStringIndexType = createIndexType( - type, /*stringsOnly*/ - true - )) - : type.resolvedIndexType - || (type.resolvedIndexType = createIndexType( - type, /*stringsOnly*/ - false - )); - } - - function getLiteralTypeFromPropertyName(name: PropertyName) { - if (isPrivateIdentifier(name)) { - return neverType; - } - return isIdentifier(name) - ? getLiteralType(unescapeLeadingUnderscores(name.escapedText)) - : getRegularTypeOfLiteralType(isComputedPropertyName(name) - ? checkComputedPropertyName(name) - : checkExpression(name)); - } - - function getBigIntLiteralType(node: BigIntLiteral): LiteralType { - return getLiteralType({ - negative: false, - base10Value: parsePseudoBigInt(node.text) - }); - } - - function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags) { - if (!(getDeclarationModifierFlagsFromSymbol(prop) - & ModifierFlags.NonPublicAccessibilityModifier)) - { - let type = getLateBoundSymbol(prop).nameType; - if (!type && !isKnownSymbol(prop)) { - if (prop.escapedName === InternalSymbolName.Default) { - type = getLiteralType('default'); - } else { - const name = prop.valueDeclaration - && getNameOfDeclaration(prop - .valueDeclaration) as PropertyName; - type = name && getLiteralTypeFromPropertyName(name) - || getLiteralType(symbolName(prop)); - } - } - if (type && type.flags & include) { - return type; - } - } - return neverType; - } - - function getLiteralTypeFromProperties(type: Type, include: TypeFlags) { - return getUnionType(map( - getPropertiesOfType(type), - p => getLiteralTypeFromProperty(p, include) - )); - } - - function getNonEnumNumberIndexInfo(type: Type) { - const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number); - return numberIndexInfo !== enumNumberIndexInfo - ? numberIndexInfo - : undefined; - } - - function getIndexType( - type: Type, - stringsOnly = keyofStringsOnly, - noIndexSignatures?: boolean - ): Type { - return type.flags & TypeFlags.Union - ? getIntersectionType(map( - ( type).types, - t => getIndexType(t, stringsOnly, noIndexSignatures) - )) - : type.flags & TypeFlags.Intersection - ? getUnionType(map( - ( type).types, - t => getIndexType(t, stringsOnly, noIndexSignatures) - )) - : maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) - ? getIndexTypeForGenericType( - type, - stringsOnly - ) - : getObjectFlags(type) & ObjectFlags.Mapped - ? filterType( - getConstraintTypeFromMappedType( type), - t => !(noIndexSignatures - && t.flags - & (TypeFlags.Any | TypeFlags.String)) - ) - : type === wildcardType - ? wildcardType - : type.flags & TypeFlags.Unknown - ? neverType - : type.flags - & (TypeFlags.Any | TypeFlags.Never) - ? keyofConstraintType - : stringsOnly - ? !noIndexSignatures - && getIndexInfoOfType( - type, - IndexKind.String - ) - ? stringType - : getLiteralTypeFromProperties( - type, - TypeFlags.StringLiteral - ) - : !noIndexSignatures - && getIndexInfoOfType( - type, - IndexKind.String - ) - ? getUnionType([stringType, - numberType, - getLiteralTypeFromProperties( - type, - TypeFlags - .UniqueESSymbol - )]) - : getNonEnumNumberIndexInfo(type) - ? getUnionType([numberType, - getLiteralTypeFromProperties( - type, - TypeFlags - .StringLiteral - | TypeFlags - .UniqueESSymbol - )]) - : getLiteralTypeFromProperties( - type, - TypeFlags - .StringOrNumberLiteralOrUnique - ); - } - - function getExtractStringType(type: Type) { - if (keyofStringsOnly) { - return type; - } - const extractTypeAlias = getGlobalExtractSymbol(); - return extractTypeAlias - ? getTypeAliasInstantiation( - extractTypeAlias, - [type, stringType] - ) - : stringType; - } - - function getIndexTypeOrString(type: Type): Type { - const indexType = getExtractStringType(getIndexType(type)); - return indexType.flags & TypeFlags.Never ? stringType : indexType; - } - - function getTypeFromTypeOperatorNode(node: TypeOperatorNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - switch (node.operator) { - case SyntaxKind.KeyOfKeyword: - links - .resolvedType = getIndexType(getTypeFromTypeNode(node - .type)); - break; - case SyntaxKind.UniqueKeyword: - links - .resolvedType = node.type.kind - === SyntaxKind.SymbolKeyword - ? getESSymbolLikeTypeForNode(walkUpParenthesizedTypes(node - .parent)) - : errorType; - break; - case SyntaxKind.ReadonlyKeyword: - links.resolvedType = getTypeFromTypeNode(node.type); - break; - default: - throw Debug.assertNever(node.operator); - } - } - return links.resolvedType; - } - - function createIndexedAccessType(objectType: Type, indexType: Type) { - const type = - createType(TypeFlags.IndexedAccess); - type.objectType = objectType; - type.indexType = indexType; - return type; - } - - /** - * Returns if a type is or consists of a JSLiteral object type - * In addition to objects which are directly literals, - * * unions where every element is a jsliteral - * * intersections where at least one element is a jsliteral - * * and instantiable types constrained to a jsliteral - * Should all count as literals and not print errors on access or assignment of possibly existing properties. - * This mirrors the behavior of the index signature propagation, to which this behaves similarly (but doesn't affect assignability or inference). - */ - function isJSLiteralType(type: Type): boolean { - if (noImplicitAny) { - return false; // Flag is meaningless under `noImplicitAny` mode - } - if (getObjectFlags(type) & ObjectFlags.JSLiteral) { - return true; - } - if (type.flags & TypeFlags.Union) { - return every((type as UnionType).types, isJSLiteralType); - } - if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, isJSLiteralType); - } - if (type.flags & TypeFlags.Instantiable) { - return isJSLiteralType(getResolvedBaseConstraint(type)); - } - return false; - } - - function getPropertyNameFromIndex( - indexType: Type, - accessNode: StringLiteral | Identifier | PrivateIdentifier - | ObjectBindingPattern | ArrayBindingPattern - | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode - | ElementAccessExpression | SyntheticExpression | undefined - ) { - const accessExpression = - accessNode - && accessNode.kind === SyntaxKind.ElementAccessExpression - ? accessNode - : undefined; - return isTypeUsableAsPropertyName(indexType) - ? getPropertyNameFromType(indexType) - : accessExpression - && checkThatExpressionIsProperSymbolReference( - accessExpression.argumentExpression, - indexType, /*reportError*/ - false - ) - ? getPropertyNameForKnownSymbolName(idText(( accessExpression - .argumentExpression).name)) - : accessNode && isPropertyName(accessNode) - ? // late bound names are handled in the first branch, so here we only need to handle normal names - getPropertyNameForPropertyNameNode(accessNode) - : undefined; - } - - function getPropertyTypeForIndexType( - originalObjectType: Type, - objectType: Type, - indexType: Type, - fullIndexType: Type, - suppressNoImplicitAnyError: boolean, - accessNode: ElementAccessExpression | IndexedAccessTypeNode - | PropertyName | BindingName | SyntheticExpression | undefined, - accessFlags: AccessFlags - ) { - const accessExpression = - accessNode - && accessNode.kind === SyntaxKind.ElementAccessExpression - ? accessNode - : undefined; - const propName = accessNode && isPrivateIdentifier(accessNode) - ? undefined - : getPropertyNameFromIndex(indexType, accessNode); - if (propName !== undefined) { - const prop = getPropertyOfType(objectType, propName); - if (prop) { - if (accessExpression) { - markPropertyAsReferenced( - prop, - accessExpression, /*isThisAccess*/ - accessExpression.expression.kind - === SyntaxKind.ThisKeyword - ); - if (isAssignmentToReadonlyEntity( - accessExpression, - prop, - getAssignmentTargetKind(accessExpression) - )) { - error( - accessExpression.argumentExpression, - Diagnostics - .Cannot_assign_to_0_because_it_is_a_read_only_property, - symbolToString(prop) - ); - return undefined; - } - if (accessFlags & AccessFlags.CacheSymbol) { - getNodeLinks(accessNode!).resolvedSymbol = prop; - } - } - const propType = getTypeOfSymbol(prop); - return accessExpression - && getAssignmentTargetKind(accessExpression) - !== AssignmentKind.Definite - ? getFlowTypeOfReference(accessExpression, propType) - : propType; - } - if (everyType(objectType, isTupleType) - && isNumericLiteralName(propName) && +propName >= 0) - { - if (accessNode - && everyType( - objectType, - t => !( t).target - .hasRestElement - ) && !(accessFlags & AccessFlags.NoTupleBoundsCheck)) - { - const indexNode = - getIndexNodeForAccessExpression(accessNode); - if (isTupleType(objectType)) { - error( - indexNode, - Diagnostics - .Tuple_type_0_of_length_1_has_no_element_at_index_2, - typeToString(objectType), - getTypeReferenceArity(objectType), - unescapeLeadingUnderscores(propName) - ); - } else { - error( - indexNode, - Diagnostics - .Property_0_does_not_exist_on_type_1, - unescapeLeadingUnderscores(propName), - typeToString(objectType) - ); - } - } - errorIfWritingToReadonlyIndex(getIndexInfoOfType( - objectType, - IndexKind.Number - )); - return mapType( - objectType, - t => getRestTypeOfTupleType( t) - || undefinedType - ); - } - } - if (!(indexType.flags & TypeFlags.Nullable) - && isTypeAssignableToKind( - indexType, - TypeFlags.StringLike | TypeFlags.NumberLike - | TypeFlags.ESSymbolLike - )) - { - if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) { - return objectType; - } - const stringIndexInfo = getIndexInfoOfType( - objectType, - IndexKind.String - ); - const indexInfo = - isTypeAssignableToKind(indexType, TypeFlags.NumberLike) - && getIndexInfoOfType(objectType, IndexKind.Number) - || stringIndexInfo; - if (indexInfo) { - if (accessFlags & AccessFlags.NoIndexSignatures - && indexInfo === stringIndexInfo) - { - if (accessExpression) { - error( - accessExpression, - Diagnostics - .Type_0_cannot_be_used_to_index_type_1, - typeToString(indexType), - typeToString(originalObjectType) - ); - } - return undefined; - } - if (accessNode - && !isTypeAssignableToKind( - indexType, - TypeFlags.String | TypeFlags.Number - )) - { - const indexNode = - getIndexNodeForAccessExpression(accessNode); - error( - indexNode, - Diagnostics.Type_0_cannot_be_used_as_an_index_type, - typeToString(indexType) - ); - return indexInfo.type; - } - errorIfWritingToReadonlyIndex(indexInfo); - return indexInfo.type; - } - if (indexType.flags & TypeFlags.Never) { - return neverType; - } - if (isJSLiteralType(objectType)) { - return anyType; - } - if (accessExpression && !isConstEnumObjectType(objectType)) { - if (objectType.symbol === globalThisSymbol - && propName !== undefined - && globalThisSymbol.exports!.has(propName) - && (globalThisSymbol.exports!.get(propName)!.flags - & SymbolFlags.BlockScoped)) - { - error( - accessExpression, - Diagnostics.Property_0_does_not_exist_on_type_1, - unescapeLeadingUnderscores(propName), - typeToString(objectType) - ); - } else if (noImplicitAny - && !compilerOptions.suppressImplicitAnyIndexErrors - && !suppressNoImplicitAnyError) - { - if (propName !== undefined - && typeHasStaticProperty(propName, objectType)) - { - error( - accessExpression, - Diagnostics - .Property_0_is_a_static_member_of_type_1, - propName as string, - typeToString(objectType) - ); - } else if (getIndexTypeOfType( - objectType, - IndexKind.Number - )) { - error( - accessExpression.argumentExpression, - Diagnostics - .Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number - ); - } else { - let suggestion: string | undefined; - if (propName !== undefined - && (suggestion = getSuggestionForNonexistentProperty( - propName as string, - objectType - ))) - { - if (suggestion !== undefined) { - error( - accessExpression.argumentExpression, - Diagnostics - .Property_0_does_not_exist_on_type_1_Did_you_mean_2, - propName as string, - typeToString(objectType), - suggestion - ); - } - } else { - const suggestion = - getSuggestionForNonexistentIndexSignature( - objectType, - accessExpression, - indexType - ); - if (suggestion !== undefined) { - error( - accessExpression, - Diagnostics - .Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature_Did_you_mean_to_call_1, - typeToString(objectType), - suggestion - ); - } else { - let errorInfo: DiagnosticMessageChain - | undefined; - if (indexType.flags - & TypeFlags.EnumLiteral) - { - errorInfo = chainDiagnosticMessages( - /* details */ undefined, - Diagnostics - .Property_0_does_not_exist_on_type_1, - '[' + typeToString(indexType) - + ']', - typeToString(objectType) - ); - } else if (indexType.flags - & TypeFlags.UniqueESSymbol) - { - const symbolName = - getFullyQualifiedName( - (indexType as UniqueESSymbolType) - .symbol, - accessExpression - ); - errorInfo = chainDiagnosticMessages( - /* details */ undefined, - Diagnostics - .Property_0_does_not_exist_on_type_1, - '[' + symbolName + ']', - typeToString(objectType) - ); - } else if (indexType.flags - & TypeFlags.StringLiteral) - { - errorInfo = chainDiagnosticMessages( - /* details */ undefined, - Diagnostics - .Property_0_does_not_exist_on_type_1, - (indexType as StringLiteralType) - .value, - typeToString(objectType) - ); - } else if (indexType.flags - & TypeFlags.NumberLiteral) - { - errorInfo = chainDiagnosticMessages( - /* details */ undefined, - Diagnostics - .Property_0_does_not_exist_on_type_1, - (indexType as NumberLiteralType) - .value, - typeToString(objectType) - ); - } else if (indexType.flags - & (TypeFlags.Number - | TypeFlags.String)) - { - errorInfo = chainDiagnosticMessages( - /* details */ undefined, - Diagnostics - .No_index_signature_with_a_parameter_of_type_0_was_found_on_type_1, - typeToString(indexType), - typeToString(objectType) - ); - } - - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics - .Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1, - typeToString(fullIndexType), - typeToString(objectType) - ); - diagnostics - .add(createDiagnosticForNodeFromMessageChain( - accessExpression, - errorInfo - )); - } - } - } - } - return undefined; - } - } - if (isJSLiteralType(objectType)) { - return anyType; - } - if (accessNode) { - const indexNode = getIndexNodeForAccessExpression(accessNode); - if (indexType.flags - & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) - { - error( - indexNode, - Diagnostics.Property_0_does_not_exist_on_type_1, - '' - + ( indexType).value, - typeToString(objectType) - ); - } else if (indexType.flags - & (TypeFlags.String | TypeFlags.Number)) - { - error( - indexNode, - Diagnostics - .Type_0_has_no_matching_index_signature_for_type_1, - typeToString(objectType), - typeToString(indexType) - ); - } else { - error( - indexNode, - Diagnostics.Type_0_cannot_be_used_as_an_index_type, - typeToString(indexType) - ); - } - } - if (isTypeAny(indexType)) { - return indexType; - } - return undefined; - - function errorIfWritingToReadonlyIndex(indexInfo: IndexInfo - | undefined): void - { - if (indexInfo && indexInfo.isReadonly && accessExpression - && (isAssignmentTarget(accessExpression) - || isDeleteTarget(accessExpression))) - { - error( - accessExpression, - Diagnostics - .Index_signature_in_type_0_only_permits_reading, - typeToString(objectType) - ); - } - } - } - - function getIndexNodeForAccessExpression(accessNode: - ElementAccessExpression | IndexedAccessTypeNode | PropertyName - | BindingName | SyntheticExpression) - { - return accessNode.kind === SyntaxKind.ElementAccessExpression - ? accessNode.argumentExpression - : accessNode.kind === SyntaxKind.IndexedAccessType - ? accessNode.indexType - : accessNode.kind === SyntaxKind.ComputedPropertyName - ? accessNode.expression - : accessNode; - } - - function isGenericObjectType(type: Type): boolean { - return maybeTypeOfKind( - type, - TypeFlags.InstantiableNonPrimitive - | TypeFlags.GenericMappedType - ); - } - - function isGenericIndexType(type: Type): boolean { - return maybeTypeOfKind( - type, - TypeFlags.InstantiableNonPrimitive | TypeFlags.Index - ); - } - - function isThisTypeParameter(type: Type): boolean { - return !!(type.flags & TypeFlags.TypeParameter - && ( type).isThisType); - } - - function getSimplifiedType(type: Type, writing: boolean): Type { - return type.flags & TypeFlags.IndexedAccess - ? getSimplifiedIndexedAccessType( - type, - writing - ) - : type.flags & TypeFlags.Conditional - ? getSimplifiedConditionalType( - type, - writing - ) - : type; - } - - function distributeIndexOverObjectType( - objectType: Type, - indexType: Type, - writing: boolean - ) { - // (T | U)[K] -> T[K] | U[K] (reading) - // (T | U)[K] -> T[K] & U[K] (writing) - // (T & U)[K] -> T[K] & U[K] - if (objectType.flags & TypeFlags.UnionOrIntersection) { - const types = map( - (objectType as UnionOrIntersectionType).types, - t => getSimplifiedType( - getIndexedAccessType(t, indexType), - writing - ) - ); - return objectType.flags & TypeFlags.Intersection || writing - ? getIntersectionType(types) - : getUnionType(types); - } - } - - function distributeObjectOverIndexType( - objectType: Type, - indexType: Type, - writing: boolean - ) { - // T[A | B] -> T[A] | T[B] (reading) - // T[A | B] -> T[A] & T[B] (writing) - if (indexType.flags & TypeFlags.Union) { - const types = map( - (indexType as UnionType).types, - t => getSimplifiedType( - getIndexedAccessType(objectType, t), - writing - ) - ); - return writing - ? getIntersectionType(types) - : getUnionType(types); - } - } - - // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return - // the type itself if no transformation is possible. The writing flag indicates that the type is - // the target of an assignment. - function getSimplifiedIndexedAccessType( - type: IndexedAccessType, - writing: boolean - ): Type { - const cache = writing - ? 'simplifiedForWriting' - : 'simplifiedForReading'; - if (type[cache]) { - return type[cache] === circularConstraintType - ? type - : type[cache]!; - } - type[cache] = circularConstraintType; - // We recursively simplify the object type as it may in turn be an indexed access type. For example, with - // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - const objectType = getSimplifiedType(type.objectType, writing); - const indexType = getSimplifiedType(type.indexType, writing); - // T[A | B] -> T[A] | T[B] (reading) - // T[A | B] -> T[A] & T[B] (writing) - const distributedOverIndex = distributeObjectOverIndexType( - objectType, - indexType, - writing - ); - if (distributedOverIndex) { - return type[cache] = distributedOverIndex; - } - // Only do the inner distributions if the index can no longer be instantiated to cause index distribution again - if (!(indexType.flags & TypeFlags.Instantiable)) { - // (T | U)[K] -> T[K] | U[K] (reading) - // (T | U)[K] -> T[K] & U[K] (writing) - // (T & U)[K] -> T[K] & U[K] - const distributedOverObject = distributeIndexOverObjectType( - objectType, - indexType, - writing - ); - if (distributedOverObject) { - return type[cache] = distributedOverObject; - } - } - // So ultimately (reading): - // ((A & B) | C)[K1 | K2] -> ((A & B) | C)[K1] | ((A & B) | C)[K2] -> (A & B)[K1] | C[K1] | (A & B)[K2] | C[K2] -> (A[K1] & B[K1]) | C[K1] | (A[K2] & B[K2]) | C[K2] - - // If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper - // that substitutes the index type for P. For example, for an index access { [P in K]: Box }[X], we - // construct the type Box. - if (isGenericMappedType(objectType)) { - return type[cache] = mapType( - substituteIndexedMappedType(objectType, type.indexType), - t => getSimplifiedType(t, writing) - ); - } - return type[cache] = type; - } - - function getSimplifiedConditionalType( - type: ConditionalType, - writing: boolean - ) { - const checkType = type.checkType; - const extendsType = type.extendsType; - const trueType = getTrueTypeFromConditionalType(type); - const falseType = getFalseTypeFromConditionalType(type); - // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. - if (falseType.flags & TypeFlags.Never - && getActualTypeVariable(trueType) - === getActualTypeVariable(checkType)) - { - if (checkType.flags & TypeFlags.Any - || isTypeAssignableTo( - getRestrictiveInstantiation(checkType), - getRestrictiveInstantiation(extendsType) - )) - { // Always true - return getSimplifiedType(trueType, writing); - } else if (isIntersectionEmpty( - checkType, - extendsType - )) { // Always false - return neverType; - } - } else if (trueType.flags & TypeFlags.Never - && getActualTypeVariable(falseType) - === getActualTypeVariable(checkType)) - { - if (!(checkType.flags & TypeFlags.Any) - && isTypeAssignableTo( - getRestrictiveInstantiation(checkType), - getRestrictiveInstantiation(extendsType) - )) - { // Always true - return neverType; - } else if (checkType.flags & TypeFlags.Any - || isIntersectionEmpty( - checkType, - extendsType - )) - { // Always false - return getSimplifiedType(falseType, writing); - } - } - return type; - } - - /** - * Invokes union simplification logic to determine if an intersection is considered empty as a union constituent - */ - function isIntersectionEmpty(type1: Type, type2: Type) { - return !!(getUnionType([intersectTypes(type1, type2), neverType]) - .flags & TypeFlags.Never); - } - - function substituteIndexedMappedType( - objectType: MappedType, - index: Type - ) { - const mapper = createTypeMapper( - [getTypeParameterFromMappedType(objectType)], - [index] - ); - const templateMapper = combineTypeMappers( - objectType.mapper, - mapper - ); - return instantiateType( - getTemplateTypeFromMappedType(objectType), - templateMapper - ); - } - - function getIndexedAccessType( - objectType: Type, - indexType: Type, - accessNode?: ElementAccessExpression | IndexedAccessTypeNode - | PropertyName | BindingName | SyntheticExpression - ): Type { - return getIndexedAccessTypeOrUndefined( - objectType, - indexType, - accessNode, - AccessFlags.None - ) || (accessNode ? errorType : unknownType); - } - - function getIndexedAccessTypeOrUndefined( - objectType: Type, - indexType: Type, - accessNode?: ElementAccessExpression | IndexedAccessTypeNode - | PropertyName | BindingName | SyntheticExpression, - accessFlags = AccessFlags.None - ): Type | undefined { - if (objectType === wildcardType || indexType === wildcardType) { - return wildcardType; - } - // If the object type has a string index signature and no other members we know that the result will - // always be the type of that index signature and we can simplify accordingly. - if (isStringIndexSignatureOnlyType(objectType) - && !(indexType.flags & TypeFlags.Nullable) - && isTypeAssignableToKind( - indexType, - TypeFlags.String | TypeFlags.Number - )) - { - indexType = stringType; - } - // If the index type is generic, or if the object type is generic and doesn't originate in an expression, - // we are performing a higher-order index access where we cannot meaningfully access the properties of the - // object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in - // an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]' - // has always been resolved eagerly using the constraint type of 'this' at the given location. - if (isGenericIndexType(indexType) - || !(accessNode - && accessNode.kind !== SyntaxKind.IndexedAccessType) - && isGenericObjectType(objectType)) - { - if (objectType.flags & TypeFlags.AnyOrUnknown) { - return objectType; - } - // Defer the operation by creating an indexed access type. - const id = objectType.id + ',' + indexType.id; - let type = indexedAccessTypes.get(id); - if (!type) { - indexedAccessTypes.set( - id, - type = createIndexedAccessType(objectType, indexType) - ); - } - return type; - } - // In the following we resolve T[K] to the type of the property in T selected by K. - // We treat boolean as different from other unions to improve errors; - // skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'. - const apparentObjectType = getApparentType(objectType); - if (indexType.flags & TypeFlags.Union - && !(indexType.flags & TypeFlags.Boolean)) - { - const propTypes: Type[] = []; - let wasMissingProp = false; - for (const t of ( indexType).types) { - const propType = getPropertyTypeForIndexType( - objectType, - apparentObjectType, - t, - indexType, - wasMissingProp, - accessNode, - accessFlags - ); - if (propType) { - propTypes.push(propType); - } else if (!accessNode) { - // If there's no error node, we can immeditely stop, since error reporting is off - return undefined; - } else { - // Otherwise we set a flag and return at the end of the loop so we still mark all errors - wasMissingProp = true; - } - } - if (wasMissingProp) { - return undefined; - } - return accessFlags & AccessFlags.Writing - ? getIntersectionType(propTypes) - : getUnionType(propTypes); - } - return getPropertyTypeForIndexType( - objectType, - apparentObjectType, - indexType, - indexType, /* supressNoImplicitAnyError */ - false, - accessNode, - accessFlags | AccessFlags.CacheSymbol - ); - } - - function getTypeFromIndexedAccessTypeNode(node: - IndexedAccessTypeNode) - { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const objectType = getTypeFromTypeNode(node.objectType); - const indexType = getTypeFromTypeNode(node.indexType); - const resolved = getIndexedAccessType( - objectType, - indexType, - node - ); - links.resolvedType = resolved.flags & TypeFlags.IndexedAccess - && ( resolved).objectType === objectType - && ( resolved).indexType === indexType - ? getConstrainedTypeVariable( - resolved, - node - ) - : resolved; - } - return links.resolvedType; - } - - function getTypeFromMappedTypeNode(node: MappedTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const type = createObjectType( - ObjectFlags.Mapped, - node.symbol - ); - type.declaration = node; - type.aliasSymbol = getAliasSymbolForTypeNode(node); - type - .aliasTypeArguments = getTypeArgumentsForAliasSymbol(type - .aliasSymbol); - links.resolvedType = type; - // Eagerly resolve the constraint type which forces an error if the constraint type circularly - // references itself through one or more type aliases. - getConstraintTypeFromMappedType(type); - } - return links.resolvedType; - } - - function getActualTypeVariable(type: Type): Type { - if (type.flags & TypeFlags.Substitution) { - return ( type).typeVariable; - } - if (type.flags & TypeFlags.IndexedAccess && ( - ( type).objectType.flags - & TypeFlags.Substitution - || ( type).indexType.flags - & TypeFlags.Substitution - )) { - return getIndexedAccessType( - getActualTypeVariable(( type) - .objectType), - getActualTypeVariable(( type).indexType) - ); - } - return type; - } - - function getConditionalType( - root: ConditionalRoot, - mapper: TypeMapper | undefined - ): Type { - const checkType = instantiateType(root.checkType, mapper); - const extendsType = instantiateType(root.extendsType, mapper); - if (checkType === wildcardType || extendsType === wildcardType) { - return wildcardType; - } - const checkTypeInstantiable = maybeTypeOfKind( - checkType, - TypeFlags.Instantiable | TypeFlags.GenericMappedType - ); - let combinedMapper: TypeMapper | undefined; - if (root.inferTypeParameters) { - const context = createInferenceContext( - root.inferTypeParameters, /*signature*/ - undefined, - InferenceFlags.None - ); - if (!checkTypeInstantiable) { - // We don't want inferences from constraints as they may cause us to eagerly resolve the - // conditional type instead of deferring resolution. Also, we always want strict function - // types rules (i.e. proper contravariance) for inferences. - inferTypes( - context.inferences, - checkType, - extendsType, - InferencePriority.NoConstraints - | InferencePriority.AlwaysStrict - ); - } - combinedMapper = combineTypeMappers(mapper, context.mapper); - } - // Instantiate the extends type including inferences for 'infer T' type parameters - const inferredExtendsType = combinedMapper - ? instantiateType(root.extendsType, combinedMapper) - : extendsType; - // We attempt to resolve the conditional type only when the check and extends types are non-generic - if (!checkTypeInstantiable - && !maybeTypeOfKind( - inferredExtendsType, - TypeFlags.Instantiable | TypeFlags.GenericMappedType - )) - { - if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { - return instantiateType( - root.trueType, - combinedMapper || mapper - ); - } - // Return union of trueType and falseType for 'any' since it matches anything - if (checkType.flags & TypeFlags.Any) { - return getUnionType([instantiateType( - root.trueType, - combinedMapper || mapper - ), instantiateType(root.falseType, mapper)]); - } - // Return falseType for a definitely false extends check. We check an instantiations of the two - // types with type parameters mapped to the wildcard type, the most permissive instantiations - // possible (the wildcard type is assignable to and from all types). If those are not related, - // then no instantiations will be and we can just return the false branch type. - if (!isTypeAssignableTo( - getPermissiveInstantiation(checkType), - getPermissiveInstantiation(inferredExtendsType) - )) { - return instantiateType(root.falseType, mapper); - } - // Return trueType for a definitely true extends check. We check instantiations of the two - // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter - // that has no constraint. This ensures that, for example, the type - // type Foo = T extends { x: string } ? string : number - // doesn't immediately resolve to 'string' instead of being deferred. - if (isTypeAssignableTo( - getRestrictiveInstantiation(checkType), - getRestrictiveInstantiation(inferredExtendsType) - )) { - return instantiateType( - root.trueType, - combinedMapper || mapper - ); - } - } - // Return a deferred type for a check that is neither definitely true nor definitely false - const erasedCheckType = getActualTypeVariable(checkType); - const result = createType(TypeFlags.Conditional); - result.root = root; - result.checkType = erasedCheckType; - result.extendsType = extendsType; - result.mapper = mapper; - result.combinedMapper = combinedMapper; - result.aliasSymbol = root.aliasSymbol; - result.aliasTypeArguments = instantiateTypes( - root.aliasTypeArguments, - mapper! - ); // TODO: GH#18217 - return result; - } - - function getTrueTypeFromConditionalType(type: ConditionalType) { - return type.resolvedTrueType - || (type.resolvedTrueType = instantiateType( - type.root.trueType, - type.mapper - )); - } - - function getFalseTypeFromConditionalType(type: ConditionalType) { - return type.resolvedFalseType - || (type.resolvedFalseType = instantiateType( - type.root.falseType, - type.mapper - )); - } - - function getInferredTrueTypeFromConditionalType(type: - ConditionalType) - { - return type.resolvedInferredTrueType - || (type.resolvedInferredTrueType = type.combinedMapper - ? instantiateType(type.root.trueType, type.combinedMapper) - : getTrueTypeFromConditionalType(type)); - } - - function getInferTypeParameters(node: - ConditionalTypeNode): TypeParameter[] | undefined - { - let result: TypeParameter[] | undefined; - if (node.locals) { - node.locals.forEach(symbol => { - if (symbol.flags & SymbolFlags.TypeParameter) { - result = append( - result, - getDeclaredTypeOfSymbol(symbol) - ); - } - }); - } - return result; - } - - function getTypeFromConditionalTypeNode(node: - ConditionalTypeNode): Type - { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const checkType = getTypeFromTypeNode(node.checkType); - const aliasSymbol = getAliasSymbolForTypeNode(node); - const aliasTypeArguments = - getTypeArgumentsForAliasSymbol(aliasSymbol); - const allOuterTypeParameters = getOuterTypeParameters( - node, /*includeThisTypes*/ - true - ); - const outerTypeParameters = aliasTypeArguments - ? allOuterTypeParameters - : filter( - allOuterTypeParameters, - tp => isTypeParameterPossiblyReferenced(tp, node) - ); - const root: ConditionalRoot = { - node, - checkType, - extendsType: getTypeFromTypeNode(node.extendsType), - trueType: getTypeFromTypeNode(node.trueType), - falseType: getTypeFromTypeNode(node.falseType), - isDistributive: - !!(checkType.flags & TypeFlags.TypeParameter), - inferTypeParameters: getInferTypeParameters(node), - outerTypeParameters, - instantiations: undefined, - aliasSymbol, - aliasTypeArguments - }; - links.resolvedType = getConditionalType( - root, /*mapper*/ - undefined - ); - if (outerTypeParameters) { - root.instantiations = createMap(); - root.instantiations.set( - getTypeListId(outerTypeParameters), - links.resolvedType - ); - } - } - return links.resolvedType; - } - - function getTypeFromInferTypeNode(node: InferTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - links - .resolvedType = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node - .typeParameter)); - } - return links.resolvedType; - } - - function getIdentifierChain(node: EntityName): Identifier[] { - if (isIdentifier(node)) { - return [node]; - } else { - return append(getIdentifierChain(node.left), node.right); - } - } - - function getTypeFromImportTypeNode(node: ImportTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - if (node.isTypeOf - && node - .typeArguments) - { // Only the non-typeof form can make use of type arguments - error( - node, - Diagnostics.Type_arguments_cannot_be_used_here - ); - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } - if (!isLiteralImportTypeNode(node)) { - error(node.argument, Diagnostics.String_literal_expected); - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } - const targetMeaning = node.isTypeOf - ? SymbolFlags.Value - : node.flags & NodeFlags.JSDoc - ? SymbolFlags.Value | SymbolFlags.Type - : SymbolFlags.Type; - // TODO: Future work: support unions/generics/whatever via a deferred import-type - const innerModuleSymbol = resolveExternalModuleName( - node, - node.argument.literal - ); - if (!innerModuleSymbol) { - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } - const moduleSymbol = resolveExternalModuleSymbol( - innerModuleSymbol, /*dontResolveAlias*/ - false - ); - if (!nodeIsMissing(node.qualifier)) { - const nameStack: Identifier[] = - getIdentifierChain(node.qualifier!); - let currentNamespace = moduleSymbol; - let current: Identifier | undefined; - while (current = nameStack.shift()) { - const meaning = nameStack.length - ? SymbolFlags.Namespace - : targetMeaning; - const next = getSymbol( - getExportsOfSymbol(getMergedSymbol(resolveSymbol(currentNamespace))), - current.escapedText, - meaning - ); - if (!next) { - error( - current, - Diagnostics - .Namespace_0_has_no_exported_member_1, - getFullyQualifiedName(currentNamespace), - declarationNameToString(current) - ); - return links.resolvedType = errorType; - } - getNodeLinks(current).resolvedSymbol = next; - getNodeLinks(current.parent).resolvedSymbol = next; - currentNamespace = next; - } - links.resolvedType = resolveImportSymbolType( - node, - links, - currentNamespace, - targetMeaning - ); - } else { - if (moduleSymbol.flags & targetMeaning) { - links.resolvedType = resolveImportSymbolType( - node, - links, - moduleSymbol, - targetMeaning - ); - } else { - const errorMessage = - targetMeaning === SymbolFlags.Value - ? Diagnostics - .Module_0_does_not_refer_to_a_value_but_is_used_as_a_value_here - : Diagnostics - .Module_0_does_not_refer_to_a_type_but_is_used_as_a_type_here_Did_you_mean_typeof_import_0; - - error(node, errorMessage, node.argument.literal.text); - - links.resolvedSymbol = unknownSymbol; - links.resolvedType = errorType; - } - } - } - return links.resolvedType; - } - - function resolveImportSymbolType( - node: ImportTypeNode, - links: NodeLinks, - symbol: Symbol, - meaning: SymbolFlags - ) { - const resolvedSymbol = resolveSymbol(symbol); - links.resolvedSymbol = resolvedSymbol; - if (meaning === SymbolFlags.Value) { - return getTypeOfSymbol(symbol); // intentionally doesn't use resolved symbol so type is cached as expected on the alias - } else { - return getTypeReferenceType( - node, - resolvedSymbol - ); // getTypeReferenceType doesn't handle aliases - it must get the resolved symbol - } - } - - function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: - TypeNode): Type - { - const links = getNodeLinks(node); - if (!links.resolvedType) { - // Deferred resolution of members is handled by resolveObjectTypeMembers - const aliasSymbol = getAliasSymbolForTypeNode(node); - if (getMembersOfSymbol(node.symbol).size === 0 - && !aliasSymbol) - { - links.resolvedType = emptyTypeLiteralType; - } else { - let type = createObjectType( - ObjectFlags.Anonymous, - node.symbol - ); - type.aliasSymbol = aliasSymbol; - type - .aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); - if (isJSDocTypeLiteral(node) && node.isArrayType) { - type = createArrayType(type); - } - links.resolvedType = type; - } - } - return links.resolvedType; - } - - function getAliasSymbolForTypeNode(node: TypeNode) { - let host = node.parent; - while (isParenthesizedTypeNode(host) || isTypeOperatorNode(host) - && host.operator === SyntaxKind.ReadonlyKeyword) - { - host = host.parent; - } - return isTypeAlias(host) ? getSymbolOfNode(host) : undefined; - } - - function getTypeArgumentsForAliasSymbol(symbol: Symbol | undefined) { - return symbol - ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) - : undefined; - } - - function isNonGenericObjectType(type: Type) { - return !!(type.flags & TypeFlags.Object) - && !isGenericMappedType(type); - } - - function isEmptyObjectTypeOrSpreadsIntoEmptyObject(type: Type) { - return isEmptyObjectType(type) - || !!(type.flags - & (TypeFlags.Null | TypeFlags.Undefined - | TypeFlags.BooleanLike | TypeFlags.NumberLike - | TypeFlags.BigIntLike | TypeFlags.StringLike - | TypeFlags.EnumLike | TypeFlags.NonPrimitive - | TypeFlags.Index)); - } - - function isSinglePropertyAnonymousObjectType(type: Type) { - return !!(type.flags & TypeFlags.Object) - && !!(getObjectFlags(type) & ObjectFlags.Anonymous) - && (length(getPropertiesOfType(type)) === 1 - || every( - getPropertiesOfType(type), - p => !!(p.flags & SymbolFlags.Optional) - )); - } - - function tryMergeUnionOfObjectTypeAndEmptyObject( - type: UnionType, - readonly: boolean - ): Type | undefined { - if (type.types.length === 2) { - const firstType = type.types[0]; - const secondType = type.types[1]; - if (every( - type.types, - isEmptyObjectTypeOrSpreadsIntoEmptyObject - )) { - return isEmptyObjectType(firstType) - ? firstType - : isEmptyObjectType(secondType) ? secondType - : emptyObjectType; - } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(firstType) - && isSinglePropertyAnonymousObjectType(secondType)) - { - return getAnonymousPartialType(secondType); - } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(secondType) - && isSinglePropertyAnonymousObjectType(firstType)) - { - return getAnonymousPartialType(firstType); - } - } - - function getAnonymousPartialType(type: Type) { - // gets the type as if it had been spread, but where everything in the spread is made optional - const members = createSymbolTable(); - for (const prop of getPropertiesOfType(type)) { - if (getDeclarationModifierFlagsFromSymbol(prop) - & (ModifierFlags.Private | ModifierFlags.Protected)) - { - // do nothing, skip privates - } else if (isSpreadableProperty(prop)) { - const isSetonlyAccessor = - prop.flags & SymbolFlags.SetAccessor - && !(prop.flags & SymbolFlags.GetAccessor); - const flags = - SymbolFlags.Property | SymbolFlags.Optional; - const result = createSymbol( - flags, - prop.escapedName, - readonly ? CheckFlags.Readonly : 0 - ); - result.type = isSetonlyAccessor - ? undefinedType - : getTypeOfSymbol(prop); - result.declarations = prop.declarations; - result.nameType = prop.nameType; - result.syntheticOrigin = prop; - members.set(prop.escapedName, result); - } - } - const spread = createAnonymousType( - type.symbol, - members, - emptyArray, - emptyArray, - getIndexInfoOfType(type, IndexKind.String), - getIndexInfoOfType(type, IndexKind.Number) - ); - spread - .objectFlags |= ObjectFlags.ObjectLiteral - | ObjectFlags.ContainsObjectOrArrayLiteral; - return spread; - } - } - - /** - * Since the source of spread types are object literals, which are not binary, - * this function should be called in a left folding style, with left = previous result of getSpreadType - * and right = the new element to be spread. - */ - function getSpreadType( - left: Type, - right: Type, - symbol: Symbol | undefined, - objectFlags: ObjectFlags, - readonly: boolean - ): Type { - if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) { - return anyType; - } - if (left.flags & TypeFlags.Unknown - || right.flags & TypeFlags.Unknown) - { - return unknownType; - } - if (left.flags & TypeFlags.Never) { - return right; - } - if (right.flags & TypeFlags.Never) { - return left; - } - if (left.flags & TypeFlags.Union) { - const merged = tryMergeUnionOfObjectTypeAndEmptyObject( - left as UnionType, - readonly - ); - if (merged) { - return getSpreadType( - merged, - right, - symbol, - objectFlags, - readonly - ); - } - return mapType( - left, - t => getSpreadType(t, right, symbol, objectFlags, readonly) - ); - } - if (right.flags & TypeFlags.Union) { - const merged = tryMergeUnionOfObjectTypeAndEmptyObject( - right as UnionType, - readonly - ); - if (merged) { - return getSpreadType( - left, - merged, - symbol, - objectFlags, - readonly - ); - } - return mapType( - right, - t => getSpreadType(left, t, symbol, objectFlags, readonly) - ); - } - if (right.flags - & (TypeFlags.BooleanLike | TypeFlags.NumberLike - | TypeFlags.BigIntLike | TypeFlags.StringLike - | TypeFlags.EnumLike | TypeFlags.NonPrimitive - | TypeFlags.Index)) - { - return left; - } - - if (isGenericObjectType(left) || isGenericObjectType(right)) { - if (isEmptyObjectType(left)) { - return right; - } - // When the left type is an intersection, we may need to merge the last constituent of the - // intersection with the right type. For example when the left type is 'T & { a: string }' - // and the right type is '{ b: string }' we produce 'T & { a: string, b: string }'. - if (left.flags & TypeFlags.Intersection) { - const types = ( left).types; - const lastLeft = types[types.length - 1]; - if (isNonGenericObjectType(lastLeft) - && isNonGenericObjectType(right)) - { - return getIntersectionType(concatenate( - types.slice(0, types.length - 1), - [getSpreadType( - lastLeft, - right, - symbol, - objectFlags, - readonly - )] - )); - } - } - return getIntersectionType([left, right]); - } - - const members = createSymbolTable(); - const skippedPrivateMembers = - createUnderscoreEscapedMap(); - let stringIndexInfo: IndexInfo | undefined; - let numberIndexInfo: IndexInfo | undefined; - if (left === emptyObjectType) { - // for the first spread element, left === emptyObjectType, so take the right's string indexer - stringIndexInfo = getIndexInfoOfType(right, IndexKind.String); - numberIndexInfo = getIndexInfoOfType(right, IndexKind.Number); - } else { - stringIndexInfo = unionSpreadIndexInfos( - getIndexInfoOfType(left, IndexKind.String), - getIndexInfoOfType(right, IndexKind.String) - ); - numberIndexInfo = unionSpreadIndexInfos( - getIndexInfoOfType(left, IndexKind.Number), - getIndexInfoOfType(right, IndexKind.Number) - ); - } - - for (const rightProp of getPropertiesOfType(right)) { - if (getDeclarationModifierFlagsFromSymbol(rightProp) - & (ModifierFlags.Private | ModifierFlags.Protected)) - { - skippedPrivateMembers.set(rightProp.escapedName, true); - } else if (isSpreadableProperty(rightProp)) { - members.set( - rightProp.escapedName, - getSpreadSymbol(rightProp, readonly) - ); - } - } - - for (const leftProp of getPropertiesOfType(left)) { - if (skippedPrivateMembers.has(leftProp.escapedName) - || !isSpreadableProperty(leftProp)) - { - continue; - } - if (members.has(leftProp.escapedName)) { - const rightProp = members.get(leftProp.escapedName)!; - const rightType = getTypeOfSymbol(rightProp); - if (rightProp.flags & SymbolFlags.Optional) { - const declarations = concatenate( - leftProp.declarations, - rightProp.declarations - ); - const flags = - SymbolFlags.Property - | (leftProp.flags & SymbolFlags.Optional); - const result = createSymbol( - flags, - leftProp.escapedName - ); - result - .type = getUnionType([getTypeOfSymbol(leftProp), - getTypeWithFacts( - rightType, - TypeFacts.NEUndefined - )]); - result.leftSpread = leftProp; - result.rightSpread = rightProp; - result.declarations = declarations; - result.nameType = leftProp.nameType; - members.set(leftProp.escapedName, result); - } - } else { - members.set( - leftProp.escapedName, - getSpreadSymbol(leftProp, readonly) - ); - } - } - - const spread = createAnonymousType( - symbol, - members, - emptyArray, - emptyArray, - getIndexInfoWithReadonly(stringIndexInfo, readonly), - getIndexInfoWithReadonly(numberIndexInfo, readonly) - ); - spread - .objectFlags |= ObjectFlags.ObjectLiteral - | ObjectFlags.ContainsObjectOrArrayLiteral - | ObjectFlags.ContainsSpread | objectFlags; - return spread; - } - - /** We approximate own properties as non-methods plus methods that are inside the object literal */ - function isSpreadableProperty(prop: Symbol): boolean { - return !(prop.flags - & (SymbolFlags.Method | SymbolFlags.GetAccessor - | SymbolFlags.SetAccessor)) - || !prop.declarations.some(decl => isClassLike(decl.parent)); - } - - function getSpreadSymbol(prop: Symbol, readonly: boolean) { - const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor - && !(prop.flags & SymbolFlags.GetAccessor); - if (!isSetonlyAccessor && readonly === isReadonlySymbol(prop)) { - return prop; - } - const flags = - SymbolFlags.Property | (prop.flags & SymbolFlags.Optional); - const result = createSymbol( - flags, - prop.escapedName, - readonly ? CheckFlags.Readonly : 0 - ); - result.type = isSetonlyAccessor - ? undefinedType - : getTypeOfSymbol(prop); - result.declarations = prop.declarations; - result.nameType = prop.nameType; - result.syntheticOrigin = prop; - return result; - } - - function getIndexInfoWithReadonly( - info: IndexInfo | undefined, - readonly: boolean - ) { - return info && info.isReadonly !== readonly - ? createIndexInfo(info.type, readonly, info.declaration) - : info; - } - - function createLiteralType( - flags: TypeFlags, - value: string | number | PseudoBigInt, - symbol: Symbol | undefined - ) { - const type = createType(flags); - type.symbol = symbol!; - type.value = value; - return type; - } - - function getFreshTypeOfLiteralType(type: Type): Type { - if (type.flags & TypeFlags.Literal) { - if (!( type).freshType) { - const freshType = createLiteralType( - type.flags, - ( type).value, - ( type).symbol - ); - freshType.regularType = type; - freshType.freshType = freshType; - ( type).freshType = freshType; - } - return ( type).freshType; - } - return type; - } - - function getRegularTypeOfLiteralType(type: Type): Type { - return type.flags & TypeFlags.Literal - ? ( type).regularType - : type.flags & TypeFlags.Union - ? getUnionType(sameMap( - ( type).types, - getRegularTypeOfLiteralType - )) - : type; - } - - function isFreshLiteralType(type: Type) { - return !!(type.flags & TypeFlags.Literal) - && ( type).freshType === type; - } - - function getLiteralType( - value: string | number | PseudoBigInt, - enumId?: number, - symbol?: Symbol - ) { - // We store all literal types in a single map with keys of the form '#NNN' and '@SSS', - // where NNN is the text representation of a numeric literal and SSS are the characters - // of a string literal. For literal enum members we use 'EEE#NNN' and 'EEE@SSS', where - // EEE is a unique id for the containing enum type. - const qualifier = typeof value === 'number' - ? '#' - : typeof value === 'string' ? '@' : 'n'; - const key = (enumId ? enumId : '') + qualifier - + (typeof value === 'object' - ? pseudoBigIntToString(value) - : value); - let type = literalTypes.get(key); - if (!type) { - const flags = (typeof value === 'number' - ? TypeFlags.NumberLiteral - : typeof value === 'string' - ? TypeFlags.StringLiteral - : TypeFlags.BigIntLiteral) - | (enumId ? TypeFlags.EnumLiteral : 0); - literalTypes.set( - key, - type = createLiteralType(flags, value, symbol) - ); - type.regularType = type; - } - return type; - } - - function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - links - .resolvedType = getRegularTypeOfLiteralType(checkExpression(node - .literal)); - } - return links.resolvedType; - } - - function createUniqueESSymbolType(symbol: Symbol) { - const type = - createType(TypeFlags.UniqueESSymbol); - type.symbol = symbol; - type.escapedName = `__@${type.symbol.escapedName}@${ - getSymbolId(type.symbol)}` as __String; - return type; - } - - function getESSymbolLikeTypeForNode(node: Node) { - if (isValidESSymbolDeclaration(node)) { - const symbol = getSymbolOfNode(node); - const links = getSymbolLinks(symbol); - return links.uniqueESSymbolType - || (links - .uniqueESSymbolType = createUniqueESSymbolType(symbol)); - } - return esSymbolType; - } - - function getThisType(node: Node): Type { - const container = getThisContainer( - node, /*includeArrowFunctions*/ - false - ); - const parent = container && container.parent; - if (parent - && (isClassLike(parent) - || parent.kind === SyntaxKind.InterfaceDeclaration)) - { - if (!hasModifier(container, ModifierFlags.Static) - && (!isConstructorDeclaration(container) - || isNodeDescendantOf(node, container.body))) - { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent as ClassLikeDeclaration - | InterfaceDeclaration)).thisType!; - } - } - - // inside x.prototype = { ... } - if (parent && isObjectLiteralExpression(parent) - && isBinaryExpression(parent.parent) - && getAssignmentDeclarationKind(parent.parent) - === AssignmentDeclarationKind.Prototype) - { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent - .parent.left)!.parent!).thisType!; - } - // /** @return {this} */ - // x.prototype.m = function() { ... } - const host = node.flags & NodeFlags.JSDoc - ? getHostSignatureFromJSDoc(node) - : undefined; - if (host && isFunctionExpression(host) - && isBinaryExpression(host.parent) - && getAssignmentDeclarationKind(host.parent) - === AssignmentDeclarationKind.PrototypeProperty) - { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(host - .parent.left)!.parent!).thisType!; - } - // inside constructor function C() { ... } - if (isJSConstructor(container) - && isNodeDescendantOf(node, container.body)) - { - return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(container)) - .thisType!; - } - error( - node, - Diagnostics - .A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface - ); - return errorType; - } - - function getTypeFromThisTypeNode(node: ThisExpression - | ThisTypeNode): Type - { - const links = getNodeLinks(node); - if (!links.resolvedType) { - links.resolvedType = getThisType(node); - } - return links.resolvedType; - } - - function getTypeFromTypeNode(node: TypeNode): Type { - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - return anyType; - case SyntaxKind.UnknownKeyword: - return unknownType; - case SyntaxKind.StringKeyword: - return stringType; - case SyntaxKind.NumberKeyword: - return numberType; - case SyntaxKind.BigIntKeyword: - return bigintType; - case SyntaxKind.BooleanKeyword: - return booleanType; - case SyntaxKind.SymbolKeyword: - return esSymbolType; - case SyntaxKind.VoidKeyword: - return voidType; - case SyntaxKind.UndefinedKeyword: - return undefinedType; - case SyntaxKind.NullKeyword: - return nullType; - case SyntaxKind.NeverKeyword: - return neverType; - case SyntaxKind.ObjectKeyword: - return node.flags & NodeFlags.JavaScriptFile - && !noImplicitAny - ? anyType - : nonPrimitiveType; - case SyntaxKind.ThisType: - case SyntaxKind.ThisKeyword: - return getTypeFromThisTypeNode(node as ThisExpression - | ThisTypeNode); - case SyntaxKind.LiteralType: - return getTypeFromLiteralTypeNode( node); - case SyntaxKind.TypeReference: - return getTypeFromTypeReference( node); - case SyntaxKind.TypePredicate: - return ( node).assertsModifier - ? voidType - : booleanType; - case SyntaxKind.ExpressionWithTypeArguments: - return getTypeFromTypeReference( node); - case SyntaxKind.TypeQuery: - return getTypeFromTypeQueryNode( node); - case SyntaxKind.ArrayType: - case SyntaxKind.TupleType: - return getTypeFromArrayOrTupleTypeNode( node); - case SyntaxKind.OptionalType: - return getTypeFromOptionalTypeNode( node); - case SyntaxKind.UnionType: - return getTypeFromUnionTypeNode( node); - case SyntaxKind.IntersectionType: - return getTypeFromIntersectionTypeNode( node); - case SyntaxKind.JSDocNullableType: - return getTypeFromJSDocNullableTypeNode( node); - case SyntaxKind.JSDocOptionalType: - return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType) - .type)); - case SyntaxKind.ParenthesizedType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocTypeExpression: - return getTypeFromTypeNode(( node) - .type); - case SyntaxKind.RestType: - return getElementTypeOfArrayType(getTypeFromTypeNode(( node) - .type)) || errorType; - case SyntaxKind.JSDocVariadicType: - return getTypeFromJSDocVariadicType(node as JSDocVariadicType); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.JSDocSignature: - return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); - case SyntaxKind.TypeOperator: - return getTypeFromTypeOperatorNode( node); - case SyntaxKind.IndexedAccessType: - return getTypeFromIndexedAccessTypeNode( node); - case SyntaxKind.MappedType: - return getTypeFromMappedTypeNode( node); - case SyntaxKind.ConditionalType: - return getTypeFromConditionalTypeNode( node); - case SyntaxKind.InferType: - return getTypeFromInferTypeNode( node); - case SyntaxKind.ImportType: - return getTypeFromImportTypeNode( node); - // This function assumes that an identifier or qualified name is a type expression - // Callers should first ensure this by calling isTypeNode - case SyntaxKind.Identifier: - case SyntaxKind.QualifiedName: - const symbol = getSymbolAtLocation(node); - return symbol - ? getDeclaredTypeOfSymbol(symbol) - : errorType; - default: - return errorType; - } - } - - function instantiateList( - items: readonly T[], - mapper: TypeMapper, - instantiator: (item: T, mapper: TypeMapper) => T - ): readonly T[]; - function instantiateList( - items: readonly T[] | undefined, - mapper: TypeMapper, - instantiator: (item: T, mapper: TypeMapper) => T - ): readonly T[] | undefined; - function instantiateList( - items: readonly T[] | undefined, - mapper: TypeMapper, - instantiator: (item: T, mapper: TypeMapper) => T - ): readonly T[] | undefined { - if (items && items.length) { - for (let i = 0; i < items.length; i++) { - const item = items[i]; - const mapped = instantiator(item, mapper); - if (item !== mapped) { - const result = i === 0 ? [] : items.slice(0, i); - result.push(mapped); - for (i++; i < items.length; i++) { - result.push(instantiator(items[i], mapper)); - } - return result; - } - } - } - return items; - } - - function instantiateTypes( - types: readonly Type[], - mapper: TypeMapper - ): readonly Type[]; - function instantiateTypes( - types: readonly Type[] | undefined, - mapper: TypeMapper - ): readonly Type[] | undefined; - function instantiateTypes( - types: readonly Type[] | undefined, - mapper: TypeMapper - ): readonly Type[] | undefined { - return instantiateList(types, mapper, instantiateType); - } - - function instantiateSignatures( - signatures: readonly Signature[], - mapper: TypeMapper - ): readonly Signature[] { - return instantiateList( - signatures, - mapper, - instantiateSignature - ); - } - - function makeUnaryTypeMapper(source: Type, target: Type) { - return (t: Type) => t === source ? target : t; - } - - function makeBinaryTypeMapper( - source1: Type, - target1: Type, - source2: Type, - target2: Type - ) { - return (t: Type) => t === source1 - ? target1 - : t === source2 ? target2 : t; - } - - function makeArrayTypeMapper( - sources: readonly Type[], - targets: readonly Type[] | undefined - ) { - return (t: Type) => { - for (let i = 0; i < sources.length; i++) { - if (t === sources[i]) { - return targets ? targets[i] : anyType; - } - } - return t; - }; - } - - function createTypeMapper( - sources: readonly TypeParameter[], - targets: readonly Type[] | undefined - ): TypeMapper { - Debug - .assert(targets === undefined - || sources.length === targets.length); - return sources.length === 1 - ? makeUnaryTypeMapper( - sources[0], - targets ? targets[0] : anyType - ) - : sources.length === 2 - ? makeBinaryTypeMapper( - sources[0], - targets ? targets[0] : anyType, - sources[1], - targets ? targets[1] : anyType - ) - : makeArrayTypeMapper(sources, targets); - } - - function createTypeEraser(sources: - readonly TypeParameter[]): TypeMapper - { - return createTypeMapper(sources, /*targets*/ undefined); - } - - /** - * Maps forward-references to later types parameters to the empty object type. - * This is used during inference when instantiating type parameter defaults. - */ - function createBackreferenceMapper( - context: InferenceContext, - index: number - ): TypeMapper { - return t => findIndex( - context.inferences, - info => info.typeParameter === t - ) >= index - ? unknownType - : t; - } - - function combineTypeMappers( - mapper1: TypeMapper | undefined, - mapper2: TypeMapper - ): TypeMapper; - function combineTypeMappers( - mapper1: TypeMapper, - mapper2: TypeMapper | undefined - ): TypeMapper; - function combineTypeMappers( - mapper1: TypeMapper, - mapper2: TypeMapper - ): TypeMapper { - if (!mapper1) return mapper2; - if (!mapper2) return mapper1; - return t => instantiateType(mapper1(t), mapper2); - } - - function createReplacementMapper( - source: Type, - target: Type, - baseMapper: TypeMapper - ): TypeMapper { - return t => t === source ? target : baseMapper(t); - } - - function permissiveMapper(type: Type) { - return type.flags & TypeFlags.TypeParameter ? wildcardType : type; - } - - function getRestrictiveTypeParameter(tp: TypeParameter) { - return tp.constraint === unknownType - ? tp - : tp.restrictiveInstantiation || ( - tp - .restrictiveInstantiation = createTypeParameter(tp - .symbol), - (tp.restrictiveInstantiation as TypeParameter) - .constraint = unknownType, - tp.restrictiveInstantiation - ); - } - - function restrictiveMapper(type: Type) { - return type.flags & TypeFlags.TypeParameter - ? getRestrictiveTypeParameter( type) - : type; - } - - function cloneTypeParameter(typeParameter: - TypeParameter): TypeParameter - { - const result = createTypeParameter(typeParameter.symbol); - result.target = typeParameter; - return result; - } - - function instantiateTypePredicate( - predicate: TypePredicate, - mapper: TypeMapper - ): TypePredicate { - return createTypePredicate( - predicate.kind, - predicate.parameterName, - predicate.parameterIndex, - instantiateType(predicate.type, mapper) - ); - } - - function instantiateSignature( - signature: Signature, - mapper: TypeMapper, - eraseTypeParameters?: boolean - ): Signature { - let freshTypeParameters: TypeParameter[] | undefined; - if (signature.typeParameters && !eraseTypeParameters) { - // First create a fresh set of type parameters, then include a mapping from the old to the - // new type parameters in the mapper function. Finally store this mapper in the new type - // parameters such that we can use it when instantiating constraints. - freshTypeParameters = map( - signature.typeParameters, - cloneTypeParameter - ); - mapper = combineTypeMappers( - createTypeMapper( - signature.typeParameters, - freshTypeParameters - ), - mapper - ); - for (const tp of freshTypeParameters) { - tp.mapper = mapper; - } - } - // Don't compute resolvedReturnType and resolvedTypePredicate now, - // because using `mapper` now could trigger inferences to become fixed. (See `createInferenceContext`.) - // See GH#17600. - const result = createSignature( - signature.declaration, - freshTypeParameters, - signature.thisParameter - && instantiateSymbol(signature.thisParameter, mapper), - instantiateList( - signature.parameters, - mapper, - instantiateSymbol - ), - /*resolvedReturnType*/ undefined, - /*resolvedTypePredicate*/ undefined, - signature.minArgumentCount, - signature.flags & SignatureFlags.PropagatingFlags - ); - result.target = signature; - result.mapper = mapper; - return result; - } - - function instantiateSymbol( - symbol: Symbol, - mapper: TypeMapper - ): Symbol { - const links = getSymbolLinks(symbol); - if (links.type - && !maybeTypeOfKind( - links.type, - TypeFlags.Object | TypeFlags.Instantiable - )) - { - // If the type of the symbol is already resolved, and if that type could not possibly - // be affected by instantiation, simply return the symbol itself. - return symbol; - } - if (getCheckFlags(symbol) & CheckFlags.Instantiated) { - // If symbol being instantiated is itself a instantiation, fetch the original target and combine the - // type mappers. This ensures that original type identities are properly preserved and that aliases - // always reference a non-aliases. - symbol = links.target!; - mapper = combineTypeMappers(links.mapper, mapper); - } - // Keep the flags from the symbol we're instantiating. Mark that is instantiated, and - // also transient so that we can just store data on it directly. - const result = createSymbol( - symbol.flags, - symbol.escapedName, - CheckFlags.Instantiated - | getCheckFlags(symbol) - & (CheckFlags.Readonly | CheckFlags.Late - | CheckFlags.OptionalParameter - | CheckFlags.RestParameter) - ); - result.declarations = symbol.declarations; - result.parent = symbol.parent; - result.target = symbol; - result.mapper = mapper; - if (symbol.valueDeclaration) { - result.valueDeclaration = symbol.valueDeclaration; - } - if (symbol.nameType) { - result.nameType = symbol.nameType; - } - return result; - } - - function getObjectTypeInstantiation( - type: AnonymousType | DeferredTypeReference, - mapper: TypeMapper - ) { - const target = type.objectFlags & ObjectFlags.Instantiated - ? type.target! - : type; - const node = type.objectFlags & ObjectFlags.Reference - ? ( type).node! - : type.symbol.declarations[0]; - const links = getNodeLinks(node); - let typeParameters = links.outerTypeParameters; - if (!typeParameters) { - // The first time an anonymous type is instantiated we compute and store a list of the type - // parameters that are in scope (and therefore potentially referenced). For type literals that - // aren't the right hand side of a generic type alias declaration we optimize by reducing the - // set of type parameters to those that are possibly referenced in the literal. - let declaration = node; - if (isInJSFile(declaration)) { - const paramTag = findAncestor( - declaration, - isJSDocParameterTag - ); - if (paramTag) { - const paramSymbol = - getParameterSymbolFromJSDoc(paramTag); - if (paramSymbol) { - declaration = paramSymbol.valueDeclaration; - } - } - } - let outerTypeParameters = getOuterTypeParameters( - declaration, /*includeThisTypes*/ - true - ); - if (isJSConstructor(declaration)) { - const templateTagParameters = - getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters); - outerTypeParameters = addRange( - outerTypeParameters, - templateTagParameters - ); - } - typeParameters = outerTypeParameters || emptyArray; - typeParameters = (target.objectFlags & ObjectFlags.Reference - || target.symbol.flags & SymbolFlags.TypeLiteral) - && !target.aliasTypeArguments - ? filter( - typeParameters, - tp => isTypeParameterPossiblyReferenced( - tp, - declaration - ) - ) - : typeParameters; - links.outerTypeParameters = typeParameters; - if (typeParameters.length) { - links.instantiations = createMap(); - links.instantiations.set( - getTypeListId(typeParameters), - target - ); - } - } - if (typeParameters.length) { - // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the - // mapper to the type parameters to produce the effective list of type arguments, and compute the - // instantiation cache key from the type IDs of the type arguments. - const typeArguments = map( - typeParameters, - combineTypeMappers(type.mapper, mapper) - ); - const id = getTypeListId(typeArguments); - let result = links.instantiations!.get(id); - if (!result) { - const newMapper = createTypeMapper( - typeParameters, - typeArguments - ); - result = target.objectFlags & ObjectFlags.Reference - ? createDeferredTypeReference( - ( type).target, - ( type).node, - newMapper - ) - : target.objectFlags & ObjectFlags.Mapped - ? instantiateMappedType( - target, - newMapper - ) - : instantiateAnonymousType(target, newMapper); - links.instantiations!.set(id, result); - } - return result; - } - return type; - } - - function maybeTypeParameterReference(node: Node) { - return !(node.kind === SyntaxKind.QualifiedName - || node.parent.kind === SyntaxKind.TypeReference - && ( node.parent).typeArguments - && node === ( node.parent).typeName - || node.parent.kind === SyntaxKind.ImportType - && (node.parent as ImportTypeNode).typeArguments - && node === (node.parent as ImportTypeNode).qualifier); - } - - function isTypeParameterPossiblyReferenced( - tp: TypeParameter, - node: Node - ) { - // If the type parameter doesn't have exactly one declaration, if there are invening statement blocks - // between the node and the type parameter declaration, if the node contains actual references to the - // type parameter, or if the node contains type queries, we consider the type parameter possibly referenced. - if (tp.symbol && tp.symbol.declarations - && tp.symbol.declarations.length === 1) - { - const container = tp.symbol.declarations[0].parent; - for (let n = node; n !== container; n = n.parent) { - if (!n || n.kind === SyntaxKind.Block - || n.kind === SyntaxKind.ConditionalType - && forEachChild( - ( n).extendsType, - containsReference - )) - { - return true; - } - } - return !!forEachChild(node, containsReference); - } - return true; - function containsReference(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.ThisType: - return !!tp.isThisType; - case SyntaxKind.Identifier: - return !tp.isThisType && isPartOfTypeNode(node) - && maybeTypeParameterReference(node) - && getTypeFromTypeNode( node) === tp; - case SyntaxKind.TypeQuery: - return true; - } - return !!forEachChild(node, containsReference); - } - } - - function getHomomorphicTypeVariable(type: MappedType) { - const constraintType = getConstraintTypeFromMappedType(type); - if (constraintType.flags & TypeFlags.Index) { - const typeVariable = - getActualTypeVariable(( constraintType).type); - if (typeVariable.flags & TypeFlags.TypeParameter) { - return typeVariable; - } - } - return undefined; - } - - function instantiateMappedType( - type: MappedType, - mapper: TypeMapper - ): Type { - // For a homomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping - // operation depends on T as follows: - // * If T is a primitive type no mapping is performed and the result is simply T. - // * If T is a union type we distribute the mapped type over the union. - // * If T is an array we map to an array where the element type has been transformed. - // * If T is a tuple we map to a tuple where the element types have been transformed. - // * Otherwise we map to an object type where the type of each property has been transformed. - // For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } | - // { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce - // { [P in keyof A]: X } | undefined. - const typeVariable = getHomomorphicTypeVariable(type); - if (typeVariable) { - const mappedTypeVariable = instantiateType( - typeVariable, - mapper - ); - if (typeVariable !== mappedTypeVariable) { - return mapType(mappedTypeVariable, t => { - if (t.flags - & (TypeFlags.AnyOrUnknown - | TypeFlags.InstantiableNonPrimitive - | TypeFlags.Object | TypeFlags.Intersection) - && t !== wildcardType && t !== errorType) - { - const replacementMapper = createReplacementMapper( - typeVariable, - t, - mapper - ); - return isArrayType(t) - ? instantiateMappedArrayType( - t, - type, - replacementMapper - ) - : isTupleType(t) - ? instantiateMappedTupleType( - t, - type, - replacementMapper - ) - : instantiateAnonymousType( - type, - replacementMapper - ); - } - return t; - }); - } - } - return instantiateAnonymousType(type, mapper); - } - - function getModifiedReadonlyState( - state: boolean, - modifiers: MappedTypeModifiers - ) { - return modifiers & MappedTypeModifiers.IncludeReadonly - ? true - : modifiers & MappedTypeModifiers.ExcludeReadonly ? false - : state; - } - - function instantiateMappedArrayType( - arrayType: Type, - mappedType: MappedType, - mapper: TypeMapper - ) { - const elementType = instantiateMappedTypeTemplate( - mappedType, - numberType, /*isOptional*/ - true, - mapper - ); - return elementType === errorType - ? errorType - : createArrayType( - elementType, - getModifiedReadonlyState( - isReadonlyArrayType(arrayType), - getMappedTypeModifiers(mappedType) - ) - ); - } - - function instantiateMappedTupleType( - tupleType: TupleTypeReference, - mappedType: MappedType, - mapper: TypeMapper - ) { - const minLength = tupleType.target.minLength; - const elementTypes = map( - getTypeArguments(tupleType), - (_, i) => instantiateMappedTypeTemplate( - mappedType, - getLiteralType('' + i), - i >= minLength, - mapper - ) - ); - const modifiers = getMappedTypeModifiers(mappedType); - const newMinLength = - modifiers & MappedTypeModifiers.IncludeOptional - ? 0 - : modifiers & MappedTypeModifiers.ExcludeOptional - ? getTypeReferenceArity(tupleType) - - (tupleType.target.hasRestElement ? 1 : 0) - : minLength; - const newReadonly = getModifiedReadonlyState( - tupleType.target.readonly, - modifiers - ); - return contains(elementTypes, errorType) - ? errorType - : createTupleType( - elementTypes, - newMinLength, - tupleType.target.hasRestElement, - newReadonly, - tupleType.target.associatedNames - ); - } - - function instantiateMappedTypeTemplate( - type: MappedType, - key: Type, - isOptional: boolean, - mapper: TypeMapper - ) { - const templateMapper = combineTypeMappers( - mapper, - createTypeMapper([getTypeParameterFromMappedType(type)], [key]) - ); - const propType = instantiateType( - getTemplateTypeFromMappedType( type.target - || type), - templateMapper - ); - const modifiers = getMappedTypeModifiers(type); - return strictNullChecks - && modifiers & MappedTypeModifiers.IncludeOptional - && !isTypeAssignableTo(undefinedType, propType) - ? getOptionalType(propType) - : strictNullChecks - && modifiers & MappedTypeModifiers.ExcludeOptional - && isOptional - ? getTypeWithFacts(propType, TypeFacts.NEUndefined) - : propType; - } - - function instantiateAnonymousType( - type: AnonymousType, - mapper: TypeMapper - ): AnonymousType { - const result = createObjectType( - type.objectFlags | ObjectFlags.Instantiated, - type.symbol - ); - if (type.objectFlags & ObjectFlags.Mapped) { - ( result).declaration = ( type) - .declaration; - // C.f. instantiateSignature - const origTypeParameter = - getTypeParameterFromMappedType( type); - const freshTypeParameter = - cloneTypeParameter(origTypeParameter); - ( result).typeParameter = freshTypeParameter; - mapper = combineTypeMappers( - makeUnaryTypeMapper(origTypeParameter, freshTypeParameter), - mapper - ); - freshTypeParameter.mapper = mapper; - } - result.target = type; - result.mapper = mapper; - result.aliasSymbol = type.aliasSymbol; - result.aliasTypeArguments = instantiateTypes( - type.aliasTypeArguments, - mapper - ); - return result; - } - - function getConditionalTypeInstantiation( - type: ConditionalType, - mapper: TypeMapper - ): Type { - const root = type.root; - if (root.outerTypeParameters) { - // We are instantiating a conditional type that has one or more type parameters in scope. Apply the - // mapper to the type parameters to produce the effective list of type arguments, and compute the - // instantiation cache key from the type IDs of the type arguments. - const typeArguments = map(root.outerTypeParameters, mapper); - const id = getTypeListId(typeArguments); - let result = root.instantiations!.get(id); - if (!result) { - const newMapper = createTypeMapper( - root.outerTypeParameters, - typeArguments - ); - result = instantiateConditionalType(root, newMapper); - root.instantiations!.set(id, result); - } - return result; - } - return type; - } - - function instantiateConditionalType( - root: ConditionalRoot, - mapper: TypeMapper - ): Type { - // Check if we have a conditional type where the check type is a naked type parameter. If so, - // the conditional type is distributive over union types and when T is instantiated to a union - // type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y). - if (root.isDistributive) { - const checkType = root.checkType; - const instantiatedType = mapper(checkType); - if (checkType !== instantiatedType - && instantiatedType.flags - & (TypeFlags.Union | TypeFlags.Never)) - { - return mapType( - instantiatedType, - t => getConditionalType( - root, - createReplacementMapper(checkType, t, mapper) - ) - ); - } - } - return getConditionalType(root, mapper); - } - - function instantiateType( - type: Type, - mapper: TypeMapper | undefined - ): Type; - function instantiateType( - type: Type | undefined, - mapper: TypeMapper | undefined - ): Type | undefined; - function instantiateType( - type: Type | undefined, - mapper: TypeMapper | undefined - ): Type | undefined { - if (!type || !mapper || mapper === identityMapper) { - return type; - } - if (instantiationDepth === 50 || instantiationCount >= 5000000) { - // We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing - // with a combination of infinite generic types that perpetually generate new type identities. We stop - // the recursion here by yielding the error type. - error( - currentNode, - Diagnostics - .Type_instantiation_is_excessively_deep_and_possibly_infinite - ); - return errorType; - } - instantiationCount++; - instantiationDepth++; - const result = instantiateTypeWorker(type, mapper); - instantiationDepth--; - return result; - } - - function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type { - const flags = type.flags; - if (flags & TypeFlags.TypeParameter) { - return mapper(type); - } - if (flags & TypeFlags.Object) { - const objectFlags = ( type).objectFlags; - if (objectFlags & ObjectFlags.Anonymous) { - // If the anonymous type originates in a declaration of a function, method, class, or - // interface, in an object type literal, or in an object literal expression, we may need - // to instantiate the type because it might reference a type parameter. - return couldContainTypeVariables(type) - ? getObjectTypeInstantiation( - type, - mapper - ) - : type; - } - if (objectFlags & ObjectFlags.Mapped) { - return getObjectTypeInstantiation( - type, - mapper - ); - } - if (objectFlags & ObjectFlags.Reference) { - if (( type).node) { - return getObjectTypeInstantiation( - type, - mapper - ); - } - const resolvedTypeArguments = ( type) - .resolvedTypeArguments; - const newTypeArguments = instantiateTypes( - resolvedTypeArguments, - mapper - ); - return newTypeArguments !== resolvedTypeArguments - ? createTypeReference( - ( type).target, - newTypeArguments - ) - : type; - } - return type; - } - if (flags & TypeFlags.Union && !(flags & TypeFlags.Primitive)) { - const types = ( type).types; - const newTypes = instantiateTypes(types, mapper); - return newTypes !== types - ? getUnionType( - newTypes, - UnionReduction.Literal, - type.aliasSymbol, - instantiateTypes(type.aliasTypeArguments, mapper) - ) - : type; - } - if (flags & TypeFlags.Intersection) { - const types = ( type).types; - const newTypes = instantiateTypes(types, mapper); - return newTypes !== types - ? getIntersectionType( - newTypes, - type.aliasSymbol, - instantiateTypes(type.aliasTypeArguments, mapper) - ) - : type; - } - if (flags & TypeFlags.Index) { - return getIndexType(instantiateType( - ( type).type, - mapper - )); - } - if (flags & TypeFlags.IndexedAccess) { - return getIndexedAccessType( - instantiateType( - ( type).objectType, - mapper - ), - instantiateType( - ( type).indexType, - mapper - ) - ); - } - if (flags & TypeFlags.Conditional) { - return getConditionalTypeInstantiation( - type, - combineTypeMappers(( type).mapper, mapper) - ); - } - if (flags & TypeFlags.Substitution) { - const maybeVariable = instantiateType( - ( type).typeVariable, - mapper - ); - if (maybeVariable.flags & TypeFlags.TypeVariable) { - return getSubstitutionType( - maybeVariable as TypeVariable, - instantiateType( - ( type).substitute, - mapper - ) - ); - } else { - const sub = instantiateType( - ( type).substitute, - mapper - ); - if (sub.flags & TypeFlags.AnyOrUnknown - || isTypeAssignableTo( - getRestrictiveInstantiation(maybeVariable), - getRestrictiveInstantiation(sub) - )) - { - return maybeVariable; - } - return sub; - } - } - return type; - } - - function getPermissiveInstantiation(type: Type) { - return type.flags - & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown - | TypeFlags.Never) - ? type - : type.permissiveInstantiation - || (type.permissiveInstantiation = instantiateType( - type, - permissiveMapper - )); - } - - function getRestrictiveInstantiation(type: Type) { - if (type.flags - & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown - | TypeFlags.Never)) - { - return type; - } - if (type.restrictiveInstantiation) { - return type.restrictiveInstantiation; - } - type.restrictiveInstantiation = instantiateType( - type, - restrictiveMapper - ); - // We set the following so we don't attempt to set the restrictive instance of a restrictive instance - // which is redundant - we'll produce new type identities, but all type params have already been mapped. - // This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint" - // assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters - // are constrained to `unknown` and produce tons of false positives/negatives! - type.restrictiveInstantiation.restrictiveInstantiation = type - .restrictiveInstantiation; - return type.restrictiveInstantiation; - } - - function instantiateIndexInfo( - info: IndexInfo | undefined, - mapper: TypeMapper - ): IndexInfo | undefined { - return info - && createIndexInfo( - instantiateType(info.type, mapper), - info.isReadonly, - info.declaration - ); - } - - // Returns true if the given expression contains (at any level of nesting) a function or arrow expression - // that is subject to contextual typing. - function isContextSensitive(node: Expression | MethodDeclaration - | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean - { - Debug - .assert(node.kind !== SyntaxKind.MethodDeclaration - || isObjectLiteralMethod(node)); - switch (node.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind - .FunctionDeclaration: // Function declarations can have context when annotated with a jsdoc @type - return isContextSensitiveFunctionLikeDeclaration( node); - case SyntaxKind.ObjectLiteralExpression: - return some( - ( node).properties, - isContextSensitive - ); - case SyntaxKind.ArrayLiteralExpression: - return some( - ( node).elements, - isContextSensitive - ); - case SyntaxKind.ConditionalExpression: - return isContextSensitive(( node) - .whenTrue) - || isContextSensitive(( node) - .whenFalse); - case SyntaxKind.BinaryExpression: - return (( node).operatorToken.kind - === SyntaxKind.BarBarToken - || ( node).operatorToken.kind - === SyntaxKind.QuestionQuestionToken) - && (isContextSensitive(( node).left) - || isContextSensitive(( node) - .right)); - case SyntaxKind.PropertyAssignment: - return isContextSensitive(( node) - .initializer); - case SyntaxKind.ParenthesizedExpression: - return isContextSensitive(( node) - .expression); - case SyntaxKind.JsxAttributes: - return some( - ( node).properties, - isContextSensitive - ) || isJsxOpeningElement(node.parent) - && some( - node.parent.parent.children, - isContextSensitive - ); - case SyntaxKind.JsxAttribute: { - // If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive. - const { initializer } = node as JsxAttribute; - return !!initializer && isContextSensitive(initializer); - } - case SyntaxKind.JsxExpression: { - // It is possible to that node.expression is undefined (e.g
) - const { expression } = node as JsxExpression; - return !!expression && isContextSensitive(expression); - } - } - - return false; - } - - function isContextSensitiveFunctionLikeDeclaration(node: - FunctionLikeDeclaration): boolean - { - if (isFunctionDeclaration(node) - && (!isInJSFile(node) - || !getTypeForDeclarationFromJSDocComment(node))) - { - return false; - } - // Functions with type parameters are not context sensitive. - if (node.typeParameters) { - return false; - } - // Functions with any parameters that lack type annotations are context sensitive. - if (some( - node.parameters, - p => !getEffectiveTypeAnnotationNode(p) - )) { - return true; - } - if (node.kind !== SyntaxKind.ArrowFunction) { - // If the first parameter is not an explicit 'this' parameter, then the function has - // an implicit 'this' parameter which is subject to contextual typing. - const parameter = firstOrUndefined(node.parameters); - if (!(parameter && parameterIsThisKeyword(parameter))) { - return true; - } - } - return hasContextSensitiveReturnExpression(node); - } - - function hasContextSensitiveReturnExpression(node: - FunctionLikeDeclaration) - { - // TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value. - return !!node.body && node.body.kind !== SyntaxKind.Block - && isContextSensitive(node.body); - } - - function isContextSensitiveFunctionOrObjectLiteralMethod(func: - Node - ): func is FunctionExpression | ArrowFunction | MethodDeclaration { - return (isInJSFile(func) && isFunctionDeclaration(func) - || isFunctionExpressionOrArrowFunction(func) - || isObjectLiteralMethod(func)) - && isContextSensitiveFunctionLikeDeclaration(func); - } - - function getTypeWithoutSignatures(type: Type): Type { - if (type.flags & TypeFlags.Object) { - const resolved = - resolveStructuredTypeMembers( type); - if (resolved.constructSignatures.length - || resolved.callSignatures.length) - { - const result = createObjectType( - ObjectFlags.Anonymous, - type.symbol - ); - result.members = resolved.members; - result.properties = resolved.properties; - result.callSignatures = emptyArray; - result.constructSignatures = emptyArray; - return result; - } - } else if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map( - ( type).types, - getTypeWithoutSignatures - )); - } - return type; - } - - // TYPE CHECKING - - function isTypeIdenticalTo(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, identityRelation); - } - - function compareTypesIdentical(source: Type, target: Type): Ternary { - return isTypeRelatedTo(source, target, identityRelation) - ? Ternary.True - : Ternary.False; - } - - function compareTypesAssignable(source: Type, target: Type): Ternary { - return isTypeRelatedTo(source, target, assignableRelation) - ? Ternary.True - : Ternary.False; - } - - function compareTypesSubtypeOf(source: Type, target: Type): Ternary { - return isTypeRelatedTo(source, target, subtypeRelation) - ? Ternary.True - : Ternary.False; - } - - function isTypeSubtypeOf(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, subtypeRelation); - } - - function isTypeAssignableTo(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, assignableRelation); - } - - // An object type S is considered to be derived from an object type T if - // S is a union type and every constituent of S is derived from T, - // T is a union type and S is derived from at least one constituent of T, or - // S is a type variable with a base constraint that is derived from T, - // T is one of the global types Object and Function and S is a subtype of T, or - // T occurs directly or indirectly in an 'extends' clause of S. - // Note that this check ignores type parameters and only considers the - // inheritance hierarchy. - function isTypeDerivedFrom(source: Type, target: Type): boolean { - return source.flags & TypeFlags.Union - ? every( - ( source).types, - t => isTypeDerivedFrom(t, target) - ) - : target.flags & TypeFlags.Union - ? some( - ( target).types, - t => isTypeDerivedFrom(source, t) - ) - : source.flags & TypeFlags.InstantiableNonPrimitive - ? isTypeDerivedFrom( - getBaseConstraintOfType(source) || unknownType, - target - ) - : target === globalObjectType - ? !!(source.flags - & (TypeFlags.Object | TypeFlags.NonPrimitive)) - : target === globalFunctionType - ? !!(source.flags & TypeFlags.Object) - && isFunctionObjectType(source as ObjectType) - : hasBaseType(source, getTargetType(target)); - } - - /** - * This is *not* a bi-directional relationship. - * If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'. - * - * A type S is comparable to a type T if some (but not necessarily all) of the possible values of S are also possible values of T. - * It is used to check following cases: - * - the types of the left and right sides of equality/inequality operators (`===`, `!==`, `==`, `!=`). - * - the types of `case` clause expressions and their respective `switch` expressions. - * - the type of an expression in a type assertion with the type being asserted. - */ - function isTypeComparableTo(source: Type, target: Type): boolean { - return isTypeRelatedTo(source, target, comparableRelation); - } - - function areTypesComparable(type1: Type, type2: Type): boolean { - return isTypeComparableTo(type1, type2) - || isTypeComparableTo(type2, type1); - } - - function checkTypeAssignableTo( - source: Type, - target: Type, - errorNode: Node | undefined, - headMessage?: DiagnosticMessage, - containingMessageChain?: () => DiagnosticMessageChain | undefined, - errorOutputObject?: { errors?: Diagnostic[]; } - ): boolean { - return checkTypeRelatedTo( - source, - target, - assignableRelation, - errorNode, - headMessage, - containingMessageChain, - errorOutputObject - ); - } - - /** - * Like `checkTypeAssignableTo`, but if it would issue an error, instead performs structural comparisons of the types using the given expression node to - * attempt to issue more specific errors on, for example, specific object literal properties or tuple members. - */ - function checkTypeAssignableToAndOptionallyElaborate( - source: Type, - target: Type, - errorNode: Node | undefined, - expr: Expression | undefined, - headMessage?: DiagnosticMessage, - containingMessageChain?: () => DiagnosticMessageChain | undefined - ): boolean { - return checkTypeRelatedToAndOptionallyElaborate( - source, - target, - assignableRelation, - errorNode, - expr, - headMessage, - containingMessageChain, /*errorOutputContainer*/ - undefined - ); - } - - function checkTypeRelatedToAndOptionallyElaborate( - source: Type, - target: Type, - relation: Map, - errorNode: Node | undefined, - expr: Expression | undefined, - headMessage: DiagnosticMessage | undefined, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ): boolean { - if (isTypeRelatedTo(source, target, relation)) return true; - if (!errorNode - || !elaborateError( - expr, - source, - target, - relation, - headMessage, - containingMessageChain, - errorOutputContainer - )) - { - return checkTypeRelatedTo( - source, - target, - relation, - errorNode, - headMessage, - containingMessageChain, - errorOutputContainer - ); - } - return false; - } - - function isOrHasGenericConditional(type: Type): boolean { - return !!(type.flags & TypeFlags.Conditional - || (type.flags & TypeFlags.Intersection - && some( - (type as IntersectionType).types, - isOrHasGenericConditional - ))); - } - - function elaborateError( - node: Expression | undefined, - source: Type, - target: Type, - relation: Map, - headMessage: DiagnosticMessage | undefined, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ): boolean { - if (!node || isOrHasGenericConditional(target)) return false; - if (!checkTypeRelatedTo( - source, - target, - relation, /*errorNode*/ - undefined - ) - && elaborateDidYouMeanToCallOrConstruct( - node, - source, - target, - relation, - headMessage, - containingMessageChain, - errorOutputContainer - )) - { - return true; - } - switch (node.kind) { - case SyntaxKind.JsxExpression: - case SyntaxKind.ParenthesizedExpression: - return elaborateError( - (node as ParenthesizedExpression | JsxExpression) - .expression, - source, - target, - relation, - headMessage, - containingMessageChain, - errorOutputContainer - ); - case SyntaxKind.BinaryExpression: - switch ((node as BinaryExpression).operatorToken.kind) { - case SyntaxKind.EqualsToken: - case SyntaxKind.CommaToken: - return elaborateError( - (node as BinaryExpression).right, - source, - target, - relation, - headMessage, - containingMessageChain, - errorOutputContainer - ); - } - break; - case SyntaxKind.ObjectLiteralExpression: - return elaborateObjectLiteral( - node as ObjectLiteralExpression, - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - case SyntaxKind.ArrayLiteralExpression: - return elaborateArrayLiteral( - node as ArrayLiteralExpression, - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - case SyntaxKind.JsxAttributes: - return elaborateJsxComponents( - node as JsxAttributes, - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - case SyntaxKind.ArrowFunction: - return elaborateArrowFunction( - node as ArrowFunction, - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - } - return false; - } - - function elaborateDidYouMeanToCallOrConstruct( - node: Expression, - source: Type, - target: Type, - relation: Map, - headMessage: DiagnosticMessage | undefined, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ): boolean { - const callSignatures = getSignaturesOfType( - source, - SignatureKind.Call - ); - const constructSignatures = getSignaturesOfType( - source, - SignatureKind.Construct - ); - for (const signatures of [constructSignatures, callSignatures]) { - if (some(signatures, s => { - const returnType = getReturnTypeOfSignature(s); - return !(returnType.flags - & (TypeFlags.Any | TypeFlags.Never)) - && checkTypeRelatedTo( - returnType, - target, - relation, /*errorNode*/ - undefined - ); - })) { - const resultObj: { errors?: Diagnostic[]; } = - errorOutputContainer || {}; - checkTypeAssignableTo( - source, - target, - node, - headMessage, - containingMessageChain, - resultObj - ); - const diagnostic = resultObj.errors! - [resultObj.errors!.length - 1]; - addRelatedInfo(diagnostic, createDiagnosticForNode( - node, - signatures === constructSignatures - ? Diagnostics - .Did_you_mean_to_use_new_with_this_expression - : Diagnostics.Did_you_mean_to_call_this_expression - )); - return true; - } - } - return false; - } - - function elaborateArrowFunction( - node: ArrowFunction, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ): boolean { - // Don't elaborate blocks - if (isBlock(node.body)) { - return false; - } - // Or functions with annotated parameter types - if (some(node.parameters, ts.hasType)) { - return false; - } - const sourceSig = getSingleCallSignature(source); - if (!sourceSig) { - return false; - } - const targetSignatures = getSignaturesOfType( - target, - SignatureKind.Call - ); - if (!length(targetSignatures)) { - return false; - } - const returnExpression = node.body; - const sourceReturn = getReturnTypeOfSignature(sourceSig); - const targetReturn = - getUnionType(map(targetSignatures, getReturnTypeOfSignature)); - if (!checkTypeRelatedTo( - sourceReturn, - targetReturn, - relation, /*errorNode*/ - undefined - )) { - const elaborated = returnExpression - && elaborateError( - returnExpression, - sourceReturn, - targetReturn, - relation, /*headMessage*/ - undefined, - containingMessageChain, - errorOutputContainer - ); - if (elaborated) { - return elaborated; - } - const resultObj: { errors?: Diagnostic[]; } = - errorOutputContainer || {}; - checkTypeRelatedTo( - sourceReturn, - targetReturn, - relation, - returnExpression, /*message*/ - undefined, - containingMessageChain, - resultObj - ); - if (resultObj.errors) { - if (target.symbol && length(target.symbol.declarations)) { - addRelatedInfo( - resultObj.errors[resultObj.errors.length - 1], - createDiagnosticForNode( - target.symbol.declarations[0], - Diagnostics - .The_expected_type_comes_from_the_return_type_of_this_signature - ) - ); - } - return true; - } - } - return false; - } - - function getBestMatchIndexedAccessTypeOrUndefined( - source: Type, - target: Type, - nameType: Type - ) { - const idx = getIndexedAccessTypeOrUndefined(target, nameType); - if (idx) { - return idx; - } - if (target.flags & TypeFlags.Union) { - const best = getBestMatchingType(source, target as UnionType); - if (best) { - return getIndexedAccessTypeOrUndefined(best, nameType); - } - } - } - - type ElaborationIterator = IterableIterator<{ errorNode: Node; - innerExpression: Expression | undefined; nameType: Type; - errorMessage?: DiagnosticMessage | undefined; }>; - /** - * For every element returned from the iterator, checks that element to issue an error on a property of that element's type - * If that element would issue an error, we first attempt to dive into that element's inner expression and issue a more specific error by recuring into `elaborateError` - * Otherwise, we issue an error on _every_ element which fail the assignability check - */ - function elaborateElementwise( - iterator: ElaborationIterator, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ) { - // Assignability failure - check each prop individually, and if that fails, fall back on the bad error span - let reportedError = false; - for (let status = iterator.next(); !status.done; status = iterator - .next()) - { - const { errorNode: prop, innerExpression: next, nameType, - errorMessage } = status.value; - const targetPropType = - getBestMatchIndexedAccessTypeOrUndefined( - source, - target, - nameType - ); - if (!targetPropType - || targetPropType.flags & TypeFlags.IndexedAccess) - { - continue; // Don't elaborate on indexes on generic variables - } - const sourcePropType = getIndexedAccessTypeOrUndefined( - source, - nameType - ); - if (sourcePropType - && !checkTypeRelatedTo( - sourcePropType, - targetPropType, - relation, /*errorNode*/ - undefined - )) - { - const elaborated = next - && elaborateError( - next, - sourcePropType, - targetPropType, - relation, /*headMessage*/ - undefined, - containingMessageChain, - errorOutputContainer - ); - if (elaborated) { - reportedError = true; - } else { - // Issue error on the prop itself, since the prop couldn't elaborate the error - const resultObj: { errors?: Diagnostic[]; } = - errorOutputContainer || {}; - // Use the expression type, if available - const specificSource = next - ? checkExpressionForMutableLocation( - next, - CheckMode.Normal, - sourcePropType - ) - : sourcePropType; - const result = checkTypeRelatedTo( - specificSource, - targetPropType, - relation, - prop, - errorMessage, - containingMessageChain, - resultObj - ); - if (result && specificSource !== sourcePropType) { - // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType - checkTypeRelatedTo( - sourcePropType, - targetPropType, - relation, - prop, - errorMessage, - containingMessageChain, - resultObj - ); - } - if (resultObj.errors) { - const reportedDiag = resultObj.errors - [resultObj.errors.length - 1]; - const propertyName = - isTypeUsableAsPropertyName(nameType) - ? getPropertyNameFromType(nameType) - : undefined; - const targetProp = propertyName !== undefined - ? getPropertyOfType(target, propertyName) - : undefined; - - let issuedElaboration = false; - if (!targetProp) { - const indexInfo = - isTypeAssignableToKind( - nameType, - TypeFlags.NumberLike - ) - && getIndexInfoOfType( - target, - IndexKind.Number - ) - || getIndexInfoOfType( - target, - IndexKind.String - ) - || undefined; - if (indexInfo && indexInfo.declaration - && !getSourceFileOfNode(indexInfo - .declaration).hasNoDefaultLib) - { - issuedElaboration = true; - addRelatedInfo( - reportedDiag, - createDiagnosticForNode( - indexInfo.declaration, - Diagnostics - .The_expected_type_comes_from_this_index_signature - ) - ); - } - } - - if (!issuedElaboration - && (targetProp - && length(targetProp.declarations) - || target.symbol - && length(target.symbol.declarations))) - { - const targetNode = - targetProp - && length(targetProp.declarations) - ? targetProp.declarations[0] - : target.symbol.declarations[0]; - if (!getSourceFileOfNode(targetNode) - .hasNoDefaultLib) - { - addRelatedInfo( - reportedDiag, - createDiagnosticForNode( - targetNode, - Diagnostics - .The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1, - propertyName - && !(nameType.flags - & TypeFlags.UniqueESSymbol) - ? unescapeLeadingUnderscores(propertyName) - : typeToString(nameType), - typeToString(target) - ) - ); - } - } - } - reportedError = true; - } - } - } - return reportedError; - } - - function* generateJsxAttributes(node: - JsxAttributes): ElaborationIterator - { - if (!length(node.properties)) return; - for (const prop of node.properties) { - if (isJsxSpreadAttribute(prop)) continue; - yield { errorNode: prop.name, - innerExpression: prop.initializer, - nameType: getLiteralType(idText(prop.name)) }; - } - } - - function* generateJsxChildren( - node: JsxElement, - getInvalidTextDiagnostic: () => DiagnosticMessage - ): ElaborationIterator { - if (!length(node.children)) return; - let memberOffset = 0; - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - const nameType = getLiteralType(i - memberOffset); - const elem = getElaborationElementForJsxChild( - child, - nameType, - getInvalidTextDiagnostic - ); - if (elem) { - yield elem; - } else { - memberOffset++; - } - } - } - - function getElaborationElementForJsxChild( - child: JsxChild, - nameType: LiteralType, - getInvalidTextDiagnostic: () => DiagnosticMessage - ) { - switch (child.kind) { - case SyntaxKind.JsxExpression: - // child is of the type of the expression - return { errorNode: child, - innerExpression: child.expression, nameType }; - case SyntaxKind.JsxText: - if (child.containsOnlyTriviaWhiteSpaces) { - break; // Whitespace only jsx text isn't real jsx text - } - // child is a string - return { errorNode: child, innerExpression: undefined, - nameType, errorMessage: getInvalidTextDiagnostic() }; - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxFragment: - // child is of type JSX.Element - return { errorNode: child, innerExpression: child, - nameType }; - default: - return Debug.assertNever(child, 'Found invalid jsx child'); - } - } - - function getSemanticJsxChildren(children: NodeArray) { - return filter( - children, - i => !isJsxText(i) || !i.containsOnlyTriviaWhiteSpaces - ); - } - - function elaborateJsxComponents( - node: JsxAttributes, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ) { - let result = elaborateElementwise( - generateJsxAttributes(node), - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - let invalidTextDiagnostic: DiagnosticMessage | undefined; - if (isJsxOpeningElement(node.parent) - && isJsxElement(node.parent.parent)) - { - const containingElement = node.parent.parent; - const childPropName = - getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - const childrenPropName = childPropName === undefined - ? 'children' - : unescapeLeadingUnderscores(childPropName); - const childrenNameType = getLiteralType(childrenPropName); - const childrenTargetType = getIndexedAccessType( - target, - childrenNameType - ); - const validChildren = - getSemanticJsxChildren(containingElement.children); - if (!length(validChildren)) { - return result; - } - const moreThanOneRealChildren = length(validChildren) > 1; - const arrayLikeTargetParts = filterType( - childrenTargetType, - isArrayOrTupleLikeType - ); - const nonArrayLikeTargetParts = filterType( - childrenTargetType, - t => !isArrayOrTupleLikeType(t) - ); - if (moreThanOneRealChildren) { - if (arrayLikeTargetParts !== neverType) { - const realSource = - createTupleType(checkJsxChildren( - containingElement, - CheckMode.Normal - )); - const children = generateJsxChildren( - containingElement, - getInvalidTextualChildDiagnostic - ); - result = elaborateElementwise( - children, - realSource, - arrayLikeTargetParts, - relation, - containingMessageChain, - errorOutputContainer - ) || result; - } else if (!isTypeRelatedTo( - getIndexedAccessType(source, childrenNameType), - childrenTargetType, - relation - )) { - // arity mismatch - result = true; - const diag = error( - containingElement.openingElement.tagName, - Diagnostics - .This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, - childrenPropName, - typeToString(childrenTargetType) - ); - if (errorOutputContainer - && errorOutputContainer.skipLogging) - { - (errorOutputContainer.errors - || (errorOutputContainer.errors = [])) - .push(diag); - } - } - } else { - if (nonArrayLikeTargetParts !== neverType) { - const child = validChildren[0]; - const elem = getElaborationElementForJsxChild( - child, - childrenNameType, - getInvalidTextualChildDiagnostic - ); - if (elem) { - result = elaborateElementwise( - (function*() { - yield elem; - })(), - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ) || result; - } - } else if (!isTypeRelatedTo( - getIndexedAccessType(source, childrenNameType), - childrenTargetType, - relation - )) { - // arity mismatch - result = true; - const diag = error( - containingElement.openingElement.tagName, - Diagnostics - .This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, - childrenPropName, - typeToString(childrenTargetType) - ); - if (errorOutputContainer - && errorOutputContainer.skipLogging) - { - (errorOutputContainer.errors - || (errorOutputContainer.errors = [])) - .push(diag); - } - } - } - } - return result; - - function getInvalidTextualChildDiagnostic() { - if (!invalidTextDiagnostic) { - const tagNameText = getTextOfNode(node.parent.tagName); - const childPropName = - getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - const childrenPropName = childPropName === undefined - ? 'children' - : unescapeLeadingUnderscores(childPropName); - const childrenTargetType = getIndexedAccessType( - target, - getLiteralType(childrenPropName) - ); - const diagnostic = Diagnostics - ._0_components_don_t_accept_text_as_child_elements_Text_in_JSX_has_the_type_string_but_the_expected_type_of_1_is_2; - invalidTextDiagnostic = { ...diagnostic, - key: '!!ALREADY FORMATTED!!', - message: formatMessage( - /*_dummy*/ undefined, - diagnostic, - tagNameText, - childrenPropName, - typeToString(childrenTargetType) - ) }; - } - return invalidTextDiagnostic; - } - } - - function* generateLimitedTupleElements( - node: ArrayLiteralExpression, - target: Type - ): ElaborationIterator { - const len = length(node.elements); - if (!len) return; - for (let i = 0; i < len; i++) { - // Skip elements which do not exist in the target - a length error on the tuple overall is likely better than an error on a mismatched index signature - if (isTupleLikeType(target) - && !getPropertyOfType(target, ('' + i) as __String)) - { - continue; - } - const elem = node.elements[i]; - if (isOmittedExpression(elem)) continue; - const nameType = getLiteralType(i); - yield { errorNode: elem, innerExpression: elem, nameType }; - } - } - - function elaborateArrayLiteral( - node: ArrayLiteralExpression, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ) { - if (target.flags & TypeFlags.Primitive) return false; - if (isTupleLikeType(source)) { - return elaborateElementwise( - generateLimitedTupleElements(node, target), - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - } - // recreate a tuple from the elements, if possible - const tupleizedType = checkArrayLiteral( - node, - CheckMode.Contextual, /*forceTuple*/ - true - ); - if (isTupleLikeType(tupleizedType)) { - return elaborateElementwise( - generateLimitedTupleElements(node, target), - tupleizedType, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - } - return false; - } - - function* generateObjectLiteralElements(node: - ObjectLiteralExpression): ElaborationIterator - { - if (!length(node.properties)) return; - for (const prop of node.properties) { - if (isSpreadAssignment(prop)) continue; - const type = getLiteralTypeFromProperty( - getSymbolOfNode(prop), - TypeFlags.StringOrNumberLiteralOrUnique - ); - if (!type || (type.flags & TypeFlags.Never)) { - continue; - } - switch (prop.kind) { - case SyntaxKind.SetAccessor: - case SyntaxKind.GetAccessor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.ShorthandPropertyAssignment: - yield { errorNode: prop.name, - innerExpression: undefined, nameType: type }; - break; - case SyntaxKind.PropertyAssignment: - yield { errorNode: prop.name, - innerExpression: prop.initializer, nameType: type, - errorMessage: isComputedNonLiteralName(prop.name) - ? Diagnostics - .Type_of_computed_property_s_value_is_0_which_is_not_assignable_to_type_1 - : undefined }; - break; - default: - Debug.assertNever(prop); - } - } - } - - function elaborateObjectLiteral( - node: ObjectLiteralExpression, - source: Type, - target: Type, - relation: Map, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } | undefined - ) { - if (target.flags & TypeFlags.Primitive) return false; - return elaborateElementwise( - generateObjectLiteralElements(node), - source, - target, - relation, - containingMessageChain, - errorOutputContainer - ); - } - - /** - * This is *not* a bi-directional relationship. - * If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'. - */ - function checkTypeComparableTo( - source: Type, - target: Type, - errorNode: Node, - headMessage?: DiagnosticMessage, - containingMessageChain?: () => DiagnosticMessageChain | undefined - ): boolean { - return checkTypeRelatedTo( - source, - target, - comparableRelation, - errorNode, - headMessage, - containingMessageChain - ); - } - - function isSignatureAssignableTo( - source: Signature, - target: Signature, - ignoreReturnTypes: boolean - ): boolean { - return compareSignaturesRelated( - source, - target, - ignoreReturnTypes - ? SignatureCheckMode.IgnoreReturnTypes - : 0, /*reportErrors*/ - false, - /*errorReporter*/ undefined, /*errorReporter*/ - undefined, - compareTypesAssignable, /*reportUnreliableMarkers*/ - undefined - ) !== Ternary.False; - } - - type ErrorReporter = ( - message: DiagnosticMessage, - arg0?: string, - arg1?: string - ) => void; - - /** - * Returns true if `s` is `(...args: any[]) => any` or `(this: any, ...args: any[]) => any` - */ - function isAnySignature(s: Signature) { - return !s.typeParameters - && (!s.thisParameter - || isTypeAny(getTypeOfParameter(s.thisParameter))) - && s.parameters.length === 1 - && signatureHasRestParameter(s) - && (getTypeOfParameter(s.parameters[0]) === anyArrayType - || isTypeAny(getTypeOfParameter(s.parameters[0]))) - && isTypeAny(getReturnTypeOfSignature(s)); - } - - /** - * See signatureRelatedTo, compareSignaturesIdentical - */ - function compareSignaturesRelated( - source: Signature, - target: Signature, - checkMode: SignatureCheckMode, - reportErrors: boolean, - errorReporter: ErrorReporter | undefined, - incompatibleErrorReporter: ((source: Type, target: Type) => void) - | undefined, - compareTypes: TypeComparer, - reportUnreliableMarkers: TypeMapper | undefined - ): Ternary { - // TODO (drosen): De-duplicate code between related functions. - if (source === target) { - return Ternary.True; - } - - if (isAnySignature(target)) { - return Ternary.True; - } - - const targetCount = getParameterCount(target); - const sourceHasMoreParameters = !hasEffectiveRestParameter(target) - && (checkMode & SignatureCheckMode.StrictArity - ? hasEffectiveRestParameter(source) - || getParameterCount(source) > targetCount - : getMinArgumentCount(source) > targetCount); - if (sourceHasMoreParameters) { - return Ternary.False; - } - - if (source.typeParameters - && source.typeParameters !== target.typeParameters) - { - target = getCanonicalSignature(target); - source = instantiateSignatureInContextOf( - source, - target, /*inferenceContext*/ - undefined, - compareTypes - ); - } - - const sourceCount = getParameterCount(source); - const sourceRestType = getNonArrayRestType(source); - const targetRestType = getNonArrayRestType(target); - if (sourceRestType || targetRestType) { - void instantiateType( - sourceRestType || targetRestType, - reportUnreliableMarkers - ); - } - if (sourceRestType && targetRestType - && sourceCount !== targetCount) - { - // We're not able to relate misaligned complex rest parameters - return Ternary.False; - } - - const kind = target.declaration - ? target.declaration.kind - : SyntaxKind.Unknown; - const strictVariance = !(checkMode & SignatureCheckMode.Callback) - && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration - && kind !== SyntaxKind.MethodSignature - && kind !== SyntaxKind.Constructor; - let result = Ternary.True; - - const sourceThisType = getThisTypeOfSignature(source); - if (sourceThisType && sourceThisType !== voidType) { - const targetThisType = getThisTypeOfSignature(target); - if (targetThisType) { - // void sources are assignable to anything. - const related = !strictVariance - && compareTypes( - sourceThisType, - targetThisType, /*reportErrors*/ - false - ) - || compareTypes( - targetThisType, - sourceThisType, - reportErrors - ); - if (!related) { - if (reportErrors) { - errorReporter!(Diagnostics - .The_this_types_of_each_signature_are_incompatible); - } - return Ternary.False; - } - result &= related; - } - } - - const paramCount = sourceRestType || targetRestType - ? Math.min(sourceCount, targetCount) - : Math.max(sourceCount, targetCount); - const restIndex = sourceRestType || targetRestType - ? paramCount - 1 - : -1; - - for (let i = 0; i < paramCount; i++) { - const sourceType = i === restIndex - ? getRestTypeAtPosition(source, i) - : getTypeAtPosition(source, i); - const targetType = i === restIndex - ? getRestTypeAtPosition(target, i) - : getTypeAtPosition(target, i); - // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter - // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, - // they naturally relate only contra-variantly). However, if the source and target parameters both have - // function types with a single call signature, we know we are relating two callback parameters. In - // that case it is sufficient to only relate the parameters of the signatures co-variantly because, - // similar to return values, callback parameters are output positions. This means that a Promise, - // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) - // with respect to T. - const sourceSig = checkMode & SignatureCheckMode.Callback - ? undefined - : getSingleCallSignature(getNonNullableType(sourceType)); - const targetSig = checkMode & SignatureCheckMode.Callback - ? undefined - : getSingleCallSignature(getNonNullableType(targetType)); - const callbacks = sourceSig && targetSig - && !getTypePredicateOfSignature(sourceSig) - && !getTypePredicateOfSignature(targetSig) - && (getFalsyFlags(sourceType) & TypeFlags.Nullable) - === (getFalsyFlags(targetType) & TypeFlags.Nullable); - let related = callbacks - ? compareSignaturesRelated( - targetSig!, - sourceSig!, - (checkMode & SignatureCheckMode.StrictArity) - | (strictVariance - ? SignatureCheckMode.StrictCallback - : SignatureCheckMode.BivariantCallback), - reportErrors, - errorReporter, - incompatibleErrorReporter, - compareTypes, - reportUnreliableMarkers - ) - : !(checkMode & SignatureCheckMode.Callback) - && !strictVariance && compareTypes( - sourceType, - targetType, /*reportErrors*/ - false - ) - || compareTypes(targetType, sourceType, reportErrors); - // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void - if (related && checkMode & SignatureCheckMode.StrictArity - && i >= getMinArgumentCount(source) - && i < getMinArgumentCount(target) - && compareTypes( - sourceType, - targetType, /*reportErrors*/ - false - )) - { - related = Ternary.False; - } - if (!related) { - if (reportErrors) { - errorReporter!( - Diagnostics - .Types_of_parameters_0_and_1_are_incompatible, - unescapeLeadingUnderscores(getParameterNameAtPosition( - source, - i - )), - unescapeLeadingUnderscores(getParameterNameAtPosition( - target, - i - )) - ); - } - return Ternary.False; - } - result &= related; - } - - if (!(checkMode & SignatureCheckMode.IgnoreReturnTypes)) { - // If a signature resolution is already in-flight, skip issuing a circularity error - // here and just use the `any` type directly - const targetReturnType = - isResolvingReturnTypeOfSignature(target) - ? anyType - : target.declaration - && isJSConstructor(target.declaration) - ? getDeclaredTypeOfClassOrInterface(getMergedSymbol(target - .declaration.symbol)) - : getReturnTypeOfSignature(target); - if (targetReturnType === voidType) { - return result; - } - const sourceReturnType = - isResolvingReturnTypeOfSignature(source) - ? anyType - : source.declaration - && isJSConstructor(source.declaration) - ? getDeclaredTypeOfClassOrInterface(getMergedSymbol(source - .declaration.symbol)) - : getReturnTypeOfSignature(source); - - // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions - const targetTypePredicate = - getTypePredicateOfSignature(target); - if (targetTypePredicate) { - const sourceTypePredicate = - getTypePredicateOfSignature(source); - if (sourceTypePredicate) { - result &= compareTypePredicateRelatedTo( - sourceTypePredicate, - targetTypePredicate, - reportErrors, - errorReporter, - compareTypes - ); - } else if (isIdentifierTypePredicate(targetTypePredicate)) { - if (reportErrors) { - errorReporter!( - Diagnostics - .Signature_0_must_be_a_type_predicate, - signatureToString(source) - ); - } - return Ternary.False; - } - } else { - // When relating callback signatures, we still need to relate return types bi-variantly as otherwise - // the containing type wouldn't be co-variant. For example, interface Foo { add(cb: () => T): void } - // wouldn't be co-variant for T without this rule. - result &= checkMode & SignatureCheckMode.BivariantCallback - && compareTypes( - targetReturnType, - sourceReturnType, /*reportErrors*/ - false - ) - || compareTypes( - sourceReturnType, - targetReturnType, - reportErrors - ); - if (!result && reportErrors && incompatibleErrorReporter) { - incompatibleErrorReporter( - sourceReturnType, - targetReturnType - ); - } - } - } - - return result; - } - - function compareTypePredicateRelatedTo( - source: TypePredicate, - target: TypePredicate, - reportErrors: boolean, - errorReporter: ErrorReporter | undefined, - compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary - ): Ternary { - if (source.kind !== target.kind) { - if (reportErrors) { - errorReporter!(Diagnostics - .A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard); - errorReporter!( - Diagnostics.Type_predicate_0_is_not_assignable_to_1, - typePredicateToString(source), - typePredicateToString(target) - ); - } - return Ternary.False; - } - - if (source.kind === TypePredicateKind.Identifier - || source.kind === TypePredicateKind.AssertsIdentifier) - { - if (source.parameterIndex - !== (target as IdentifierTypePredicate).parameterIndex) - { - if (reportErrors) { - errorReporter!( - Diagnostics - .Parameter_0_is_not_in_the_same_position_as_parameter_1, - source.parameterName, - (target as IdentifierTypePredicate).parameterName - ); - errorReporter!( - Diagnostics - .Type_predicate_0_is_not_assignable_to_1, - typePredicateToString(source), - typePredicateToString(target) - ); - } - return Ternary.False; - } - } - - const related = source.type === target.type - ? Ternary.True - : source.type && target.type - ? compareTypes(source.type, target.type, reportErrors) - : Ternary.False; - if (related === Ternary.False && reportErrors) { - errorReporter!( - Diagnostics.Type_predicate_0_is_not_assignable_to_1, - typePredicateToString(source), - typePredicateToString(target) - ); - } - return related; - } - - function isImplementationCompatibleWithOverload( - implementation: Signature, - overload: Signature - ): boolean { - const erasedSource = getErasedSignature(implementation); - const erasedTarget = getErasedSignature(overload); - - // First see if the return types are compatible in either direction. - const sourceReturnType = getReturnTypeOfSignature(erasedSource); - const targetReturnType = getReturnTypeOfSignature(erasedTarget); - if (targetReturnType === voidType - || isTypeRelatedTo( - targetReturnType, - sourceReturnType, - assignableRelation - ) - || isTypeRelatedTo( - sourceReturnType, - targetReturnType, - assignableRelation - )) - { - return isSignatureAssignableTo( - erasedSource, - erasedTarget, /*ignoreReturnTypes*/ - true - ); - } - - return false; - } - - function isEmptyResolvedType(t: ResolvedType) { - return t.properties.length === 0 - && t.callSignatures.length === 0 - && t.constructSignatures.length === 0 - && !t.stringIndexInfo - && !t.numberIndexInfo; - } - - function isEmptyObjectType(type: Type): boolean { - return type.flags & TypeFlags.Object - ? !isGenericMappedType(type) - && isEmptyResolvedType(resolveStructuredTypeMembers( type)) - : type.flags & TypeFlags.NonPrimitive - ? true - : type.flags & TypeFlags.Union - ? some(( type).types, isEmptyObjectType) - : type.flags & TypeFlags.Intersection - ? every( - ( type).types, - isEmptyObjectType - ) - : false; - } - - function isEmptyAnonymousObjectType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.Anonymous) - && isEmptyObjectType(type); - } - - function isStringIndexSignatureOnlyType(type: Type): boolean { - return type.flags & TypeFlags.Object && !isGenericMappedType(type) - && getPropertiesOfType(type).length === 0 - && getIndexInfoOfType(type, IndexKind.String) - && !getIndexInfoOfType(type, IndexKind.Number) - || type.flags & TypeFlags.UnionOrIntersection - && every( - ( type).types, - isStringIndexSignatureOnlyType - ) - || false; - } - - function isEnumTypeRelatedTo( - sourceSymbol: Symbol, - targetSymbol: Symbol, - errorReporter?: ErrorReporter - ) { - if (sourceSymbol === targetSymbol) { - return true; - } - const id = getSymbolId(sourceSymbol) + ',' - + getSymbolId(targetSymbol); - const entry = enumRelation.get(id); - if (entry !== undefined - && !(!(entry & RelationComparisonResult.Reported) - && entry & RelationComparisonResult.Failed - && errorReporter)) - { - return !!(entry & RelationComparisonResult.Succeeded); - } - if (sourceSymbol.escapedName !== targetSymbol.escapedName - || !(sourceSymbol.flags & SymbolFlags.RegularEnum) - || !(targetSymbol.flags & SymbolFlags.RegularEnum)) - { - enumRelation.set( - id, - RelationComparisonResult.Failed - | RelationComparisonResult.Reported - ); - return false; - } - const targetEnumType = getTypeOfSymbol(targetSymbol); - for (const property - of getPropertiesOfType(getTypeOfSymbol(sourceSymbol))) - { - if (property.flags & SymbolFlags.EnumMember) { - const targetProperty = getPropertyOfType( - targetEnumType, - property.escapedName - ); - if (!targetProperty - || !(targetProperty.flags & SymbolFlags.EnumMember)) - { - if (errorReporter) { - errorReporter( - Diagnostics.Property_0_is_missing_in_type_1, - symbolName(property), - typeToString( - getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ - undefined, - TypeFormatFlags.UseFullyQualifiedType - ) - ); - enumRelation.set( - id, - RelationComparisonResult.Failed - | RelationComparisonResult.Reported - ); - } else { - enumRelation.set( - id, - RelationComparisonResult.Failed - ); - } - return false; - } - } - } - enumRelation.set(id, RelationComparisonResult.Succeeded); - return true; - } - - function isSimpleTypeRelatedTo( - source: Type, - target: Type, - relation: Map, - errorReporter?: ErrorReporter - ) { - const s = source.flags; - const t = target.flags; - if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never - || source === wildcardType) - { - return true; - } - if (t & TypeFlags.Never) return false; - if (s & TypeFlags.StringLike && t & TypeFlags.String) return true; - if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral - && t & TypeFlags.StringLiteral && !(t & TypeFlags.EnumLiteral) - && ( source).value - === ( target).value) - { - return true; - } - if (s & TypeFlags.NumberLike && t & TypeFlags.Number) return true; - if (s & TypeFlags.NumberLiteral && s & TypeFlags.EnumLiteral - && t & TypeFlags.NumberLiteral && !(t & TypeFlags.EnumLiteral) - && ( source).value - === ( target).value) - { - return true; - } - if (s & TypeFlags.BigIntLike && t & TypeFlags.BigInt) return true; - if (s & TypeFlags.BooleanLike - && t & TypeFlags.Boolean) - return true; - if (s & TypeFlags.ESSymbolLike - && t & TypeFlags.ESSymbol) - return true; - if (s & TypeFlags.Enum && t & TypeFlags.Enum - && isEnumTypeRelatedTo( - source.symbol, - target.symbol, - errorReporter - )) - { - return true; - } - if (s & TypeFlags.EnumLiteral && t & TypeFlags.EnumLiteral) { - if (s & TypeFlags.Union && t & TypeFlags.Union - && isEnumTypeRelatedTo( - source.symbol, - target.symbol, - errorReporter - )) - { - return true; - } - if (s & TypeFlags.Literal && t & TypeFlags.Literal - && ( source).value - === ( target).value - && isEnumTypeRelatedTo( - getParentOfSymbol(source.symbol)!, - getParentOfSymbol(target.symbol)!, - errorReporter - )) - { - return true; - } - } - if (s & TypeFlags.Undefined - && (!strictNullChecks - || t & (TypeFlags.Undefined | TypeFlags.Void))) - { - return true; - } - if (s & TypeFlags.Null - && (!strictNullChecks || t & TypeFlags.Null)) - { - return true; - } - if (s & TypeFlags.Object - && t & TypeFlags.NonPrimitive) - return true; - if (relation === assignableRelation - || relation === comparableRelation) - { - if (s & TypeFlags.Any) return true; - // Type number or any numeric literal type is assignable to any numeric enum type or any - // numeric enum literal type. This rule exists for backwards compatibility reasons because - // bit-flag enum types sometimes look like literal enum types with numeric literal values. - if (s & (TypeFlags.Number | TypeFlags.NumberLiteral) - && !(s & TypeFlags.EnumLiteral) && ( - t & TypeFlags.Enum || t & TypeFlags.NumberLiteral - && t & TypeFlags.EnumLiteral - )) - { - return true; - } - } - return false; - } - - function isTypeRelatedTo( - source: Type, - target: Type, - relation: Map - ) { - if (isFreshLiteralType(source)) { - source = ( source).regularType; - } - if (isFreshLiteralType(target)) { - target = ( target).regularType; - } - if (source === target - || relation === comparableRelation - && !(target.flags & TypeFlags.Never) - && isSimpleTypeRelatedTo(target, source, relation) - || relation !== identityRelation - && isSimpleTypeRelatedTo(source, target, relation)) - { - return true; - } - if (source.flags & TypeFlags.Object - && target.flags & TypeFlags.Object) - { - const related = relation - .get(getRelationKey( - source, - target, /*isIntersectionConstituent*/ - false, - relation - )); - if (related !== undefined) { - return !!(related & RelationComparisonResult.Succeeded); - } - } - if (source.flags & TypeFlags.StructuredOrInstantiable - || target.flags & TypeFlags.StructuredOrInstantiable) - { - return checkTypeRelatedTo( - source, - target, - relation, /*errorNode*/ - undefined - ); - } - return false; - } - - function isIgnoredJsxProperty(source: Type, sourceProp: Symbol) { - return getObjectFlags(source) & ObjectFlags.JsxAttributes - && !isUnhyphenatedJsxName(sourceProp.escapedName); - } - - function getNormalizedType(type: Type, writing: boolean): Type { - return isFreshLiteralType(type) - ? ( type).regularType - : getObjectFlags(type) & ObjectFlags.Reference - && ( type).node - ? createTypeReference( - ( type).target, - getTypeArguments( type) - ) - : type.flags & TypeFlags.Substitution - ? writing - ? ( type).typeVariable - : ( type).substitute - : type.flags & TypeFlags.Simplifiable - ? getSimplifiedType(type, writing) - : type; - } - - /** - * Checks if 'source' is related to 'target' (e.g.: is a assignable to). - * @param source The left-hand-side of the relation. - * @param target The right-hand-side of the relation. - * @param relation The relation considered. One of 'identityRelation', 'subtypeRelation', 'assignableRelation', or 'comparableRelation'. - * Used as both to determine which checks are performed and as a cache of previously computed results. - * @param errorNode The suggested node upon which all errors will be reported, if defined. This may or may not be the actual node used. - * @param headMessage If the error chain should be prepended by a head message, then headMessage will be used. - * @param containingMessageChain A chain of errors to prepend any new errors found. - * @param errorOutputContainer Return the diagnostic. Do not log if 'skipLogging' is truthy. - */ - function checkTypeRelatedTo( - source: Type, - target: Type, - relation: Map, - errorNode: Node | undefined, - headMessage?: DiagnosticMessage, - containingMessageChain?: () => DiagnosticMessageChain | undefined, - errorOutputContainer?: { errors?: Diagnostic[]; - skipLogging?: boolean; } - ): boolean { - let errorInfo: DiagnosticMessageChain | undefined; - let relatedInfo: [DiagnosticRelatedInformation, - ...DiagnosticRelatedInformation[]] | undefined; - let maybeKeys: string[]; - let sourceStack: Type[]; - let targetStack: Type[]; - let maybeCount = 0; - let depth = 0; - let expandingFlags = ExpandingFlags.None; - let overflow = false; - let overrideNextErrorInfo = - 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid - let lastSkippedInfo: [Type, Type] | undefined; - let incompatibleStack: [DiagnosticMessage, (string | number)?, - (string | number)?, (string | number)?, (string | number)?][] = - []; - - Debug.assert( - relation !== identityRelation || !errorNode, - 'no error reporting in identity checking' - ); - - const result = isRelatedTo( - source, - target, /*reportErrors*/ - !!errorNode, - headMessage - ); - if (incompatibleStack.length) { - reportIncompatibleStack(); - } - if (overflow) { - const diag = error( - errorNode, - Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, - typeToString(source), - typeToString(target) - ); - if (errorOutputContainer) { - (errorOutputContainer.errors - || (errorOutputContainer.errors = [])).push(diag); - } - } else if (errorInfo) { - if (containingMessageChain) { - const chain = containingMessageChain(); - if (chain) { - concatenateDiagnosticMessageChains(chain, errorInfo); - errorInfo = chain; - } - } - - let relatedInformation: DiagnosticRelatedInformation[] - | undefined; - // Check if we should issue an extra diagnostic to produce a quickfix for a slightly incorrect import statement - if (headMessage && errorNode && !result && source.symbol) { - const links = getSymbolLinks(source.symbol); - if (links.originatingImport - && !isImportCall(links.originatingImport)) - { - const helpfulRetry = checkTypeRelatedTo( - getTypeOfSymbol(links.target!), - target, - relation, /*errorNode*/ - undefined - ); - if (helpfulRetry) { - // Likely an incorrect import. Issue a helpful diagnostic to produce a quickfix to change the import - const diag = createDiagnosticForNode( - links.originatingImport, - Diagnostics - .Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead - ); - relatedInformation = append( - relatedInformation, - diag - ); // Cause the error to appear with the error that triggered it - } - } - } - const diag = createDiagnosticForNodeFromMessageChain( - errorNode!, - errorInfo, - relatedInformation - ); - if (relatedInfo) { - addRelatedInfo(diag, ...relatedInfo); - } - if (errorOutputContainer) { - (errorOutputContainer.errors - || (errorOutputContainer.errors = [])).push(diag); - } - if (!errorOutputContainer - || !errorOutputContainer.skipLogging) - { - diagnostics.add(diag); - } - } - if (errorNode && errorOutputContainer - && errorOutputContainer.skipLogging - && result === Ternary.False) - { - Debug.assert( - !!errorOutputContainer.errors, - 'missed opportunity to interact with error.' - ); - } - return result !== Ternary.False; - - function resetErrorInfo(saved: - ReturnType) - { - errorInfo = saved.errorInfo; - lastSkippedInfo = saved.lastSkippedInfo; - incompatibleStack = saved.incompatibleStack; - overrideNextErrorInfo = saved.overrideNextErrorInfo; - relatedInfo = saved.relatedInfo; - } - - function captureErrorCalculationState() { - return { - errorInfo, - lastSkippedInfo, - incompatibleStack: incompatibleStack.slice(), - overrideNextErrorInfo, - relatedInfo: !relatedInfo - ? undefined - : relatedInfo - .slice() as ([DiagnosticRelatedInformation, - ...DiagnosticRelatedInformation[]] | undefined) - }; - } - - function reportIncompatibleError( - message: DiagnosticMessage, - arg0?: string | number, - arg1?: string | number, - arg2?: string | number, - arg3?: string | number - ) { - overrideNextErrorInfo++; // Suppress the next relation error - lastSkippedInfo = undefined; // Reset skipped info cache - incompatibleStack.push([message, arg0, arg1, arg2, arg3]); - } - - function reportIncompatibleStack() { - const stack = incompatibleStack; - incompatibleStack = []; - const info = lastSkippedInfo; - lastSkippedInfo = undefined; - if (stack.length === 1) { - reportError(...stack[0]); - if (info) { - // Actually do the last relation error - reportRelationError( - /*headMessage*/ undefined, - ...info - ); - } - return; - } - // The first error will be the innermost, while the last will be the outermost - so by popping off the end, - // we can build from left to right - let path = ''; - const secondaryRootErrors: typeof incompatibleStack = []; - while (stack.length) { - const [msg, ...args] = stack.pop()!; - switch (msg.code) { - case Diagnostics.Types_of_property_0_are_incompatible - .code: { - // Parenthesize a `new` if there is one - if (path.indexOf('new ') === 0) { - path = `(${path})`; - } - const str = '' + args[0]; - // If leading, just print back the arg (irrespective of if it's a valid identifier) - if (path.length === 0) { - path = `${str}`; - } // Otherwise write a dotted name if possible - else if (isIdentifierText( - str, - compilerOptions.target - )) { - path = `${path}.${str}`; - } // Failing that, check if the name is already a computed name - else if (str[0] === '[' - && str[str.length - 1] === ']') - { - path = `${path}${str}`; - } // And finally write out a computed name as a last resort - else { - path = `${path}[${str}]`; - } - break; - } - case Diagnostics - .Call_signature_return_types_0_and_1_are_incompatible - .code: - case Diagnostics - .Construct_signature_return_types_0_and_1_are_incompatible - .code: - case Diagnostics - .Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code: - case Diagnostics - .Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code: { - if (path.length === 0) { - // Don't flatten signature compatability errors at the start of a chain - instead prefer - // to unify (the with no arguments bit is excessive for printback) and print them back - let mappedMsg = msg; - if (msg.code - === Diagnostics - .Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code) - { - mappedMsg = Diagnostics - .Call_signature_return_types_0_and_1_are_incompatible; - } else if (msg.code - === Diagnostics - .Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code) - { - mappedMsg = Diagnostics - .Construct_signature_return_types_0_and_1_are_incompatible; - } - secondaryRootErrors - .unshift([mappedMsg, args[0], args[1]]); - } else { - const prefix = - (msg.code - === Diagnostics - .Construct_signature_return_types_0_and_1_are_incompatible - .code - || msg.code - === Diagnostics - .Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code) - ? 'new ' - : ''; - const params = - (msg.code - === Diagnostics - .Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code - || msg.code - === Diagnostics - .Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1 - .code) - ? '' - : '...'; - path = `${prefix}${path}(${params})`; - } - break; - } - default: - return Debug - .fail(`Unhandled Diagnostic: ${msg.code}`); - } - } - if (path) { - reportError( - path[path.length - 1] === ')' - ? Diagnostics - .The_types_returned_by_0_are_incompatible_between_these_types - : Diagnostics - .The_types_of_0_are_incompatible_between_these_types, - path - ); - } else { - // Remove the innermost secondary error as it will duplicate the error already reported by `reportRelationError` on entry - secondaryRootErrors.shift(); - } - for (const [msg, ...args] of secondaryRootErrors) { - const originalValue = msg.elidedInCompatabilityPyramid; - msg - .elidedInCompatabilityPyramid = false; // Teporarily override elision to ensure error is reported - reportError(msg, ...args); - msg.elidedInCompatabilityPyramid = originalValue; - } - if (info) { - // Actually do the last relation error - reportRelationError(/*headMessage*/ undefined, ...info); - } - } - - function reportError( - message: DiagnosticMessage, - arg0?: string | number, - arg1?: string | number, - arg2?: string | number, - arg3?: string | number - ): void { - Debug.assert(!!errorNode); - if (incompatibleStack.length) reportIncompatibleStack(); - if (message.elidedInCompatabilityPyramid) return; - errorInfo = chainDiagnosticMessages( - errorInfo, - message, - arg0, - arg1, - arg2, - arg3 - ); - } - - function associateRelatedInfo(info: DiagnosticRelatedInformation) { - Debug.assert(!!errorInfo); - if (!relatedInfo) { - relatedInfo = [info]; - } else { - relatedInfo.push(info); - } - } - - function reportRelationError( - message: DiagnosticMessage | undefined, - source: Type, - target: Type - ) { - if (incompatibleStack.length) reportIncompatibleStack(); - const [sourceType, targetType] = getTypeNamesForErrorDisplay( - source, - target - ); - - if (target.flags & TypeFlags.TypeParameter - && target.immediateBaseConstraint !== undefined - && isTypeAssignableTo( - source, - target.immediateBaseConstraint - )) - { - reportError( - Diagnostics - ._0_is_assignable_to_the_constraint_of_type_1_but_1_could_be_instantiated_with_a_different_subtype_of_constraint_2, - sourceType, - targetType, - typeToString(target.immediateBaseConstraint) - ); - } - - if (!message) { - if (relation === comparableRelation) { - message = Diagnostics - .Type_0_is_not_comparable_to_type_1; - } else if (sourceType === targetType) { - message = Diagnostics - .Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated; - } else { - message = Diagnostics - .Type_0_is_not_assignable_to_type_1; - } - } - - reportError(message, sourceType, targetType); - } - - function tryElaborateErrorsForPrimitivesAndObjects( - source: Type, - target: Type - ) { - const sourceType = - symbolValueDeclarationIsContextSensitive(source.symbol) - ? typeToString(source, source.symbol.valueDeclaration) - : typeToString(source); - const targetType = - symbolValueDeclarationIsContextSensitive(target.symbol) - ? typeToString(target, target.symbol.valueDeclaration) - : typeToString(target); - - if ((globalStringType === source && stringType === target) - || (globalNumberType === source && numberType === target) - || (globalBooleanType === source && booleanType === target) - || (getGlobalESSymbolType(/*reportErrors*/ false) - === source && esSymbolType === target)) - { - reportError( - Diagnostics - ._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, - targetType, - sourceType - ); - } - } - - /** - * Try and elaborate array and tuple errors. Returns false - * if we have found an elaboration, or we should ignore - * any other elaborations when relating the `source` and - * `target` types. - */ - function tryElaborateArrayLikeErrors( - source: Type, - target: Type, - reportErrors: boolean - ): boolean { - /** - * The spec for elaboration is: - * - If the source is a readonly tuple and the target is a mutable array or tuple, elaborate on mutability and skip property elaborations. - * - If the source is a tuple then skip property elaborations if the target is an array or tuple. - * - If the source is a readonly array and the target is a mutable array or tuple, elaborate on mutability and skip property elaborations. - * - If the source an array then skip property elaborations if the target is a tuple. - */ - if (isTupleType(source)) { - if (source.target.readonly - && isMutableArrayOrTuple(target)) - { - if (reportErrors) { - reportError( - Diagnostics - .The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1, - typeToString(source), - typeToString(target) - ); - } - return false; - } - return isTupleType(target) || isArrayType(target); - } - if (isReadonlyArrayType(source) - && isMutableArrayOrTuple(target)) - { - if (reportErrors) { - reportError( - Diagnostics - .The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1, - typeToString(source), - typeToString(target) - ); - } - return false; - } - if (isTupleType(target)) { - return isArrayType(source); - } - return true; - } - - /** - * Compare two types and return - * * Ternary.True if they are related with no assumptions, - * * Ternary.Maybe if they are related with assumptions of other relationships, or - * * Ternary.False if they are not related. - */ - function isRelatedTo( - originalSource: Type, - originalTarget: Type, - reportErrors = false, - headMessage?: DiagnosticMessage, - isApparentIntersectionConstituent?: boolean - ): Ternary { - // Normalize the source and target types: Turn fresh literal types into regular literal types, - // turn deferred type references into regular type references, simplify indexed access and - // conditional types, and resolve substitution types to either the substitution (on the source - // side) or the type variable (on the target side). - let source = getNormalizedType( - originalSource, /*writing*/ - false - ); - let target = getNormalizedType( - originalTarget, /*writing*/ - true - ); - - // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. - // If so, reporting the `null` and `undefined` in the type is hardly useful. - // First, see if we're even relating an object type to a union. - // Then see if the target is stripped down to a single non-union type. - // Note - // * We actually want to remove null and undefined naively here (rather than using getNonNullableType), - // since we don't want to end up with a worse error like "`Foo` is not assignable to `NonNullable`" - // when dealing with generics. - // * We also don't deal with primitive source types, since we already halt elaboration below. - if (target.flags & TypeFlags.Union - && source.flags & TypeFlags.Object - && (target as UnionType).types.length <= 3 - && maybeTypeOfKind(target, TypeFlags.Nullable)) - { - const nullStrippedTarget = extractTypesOfKind( - target, - ~TypeFlags.Nullable - ); - if (!(nullStrippedTarget.flags - & (TypeFlags.Union | TypeFlags.Never))) - { - target = nullStrippedTarget; - } - } - - // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases - if (source === target) return Ternary.True; - - if (relation === identityRelation) { - return isIdenticalTo(source, target); - } - - if (relation === comparableRelation - && !(target.flags & TypeFlags.Never) - && isSimpleTypeRelatedTo(target, source, relation) - || isSimpleTypeRelatedTo( - source, - target, - relation, - reportErrors ? reportError : undefined - )) - { - return Ternary.True; - } - - const isComparingJsxAttributes = - !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - const isPerformingExcessPropertyChecks = - !isApparentIntersectionConstituent - && (isObjectLiteralType(source) - && getObjectFlags(source) - & ObjectFlags.FreshLiteral); - if (isPerformingExcessPropertyChecks) { - if (hasExcessProperties( - source, - target, - reportErrors - )) { - if (reportErrors) { - reportRelationError(headMessage, source, target); - } - return Ternary.False; - } - } - - const isPerformingCommonPropertyChecks = - relation !== comparableRelation - && !isApparentIntersectionConstituent - && source.flags - & (TypeFlags.Primitive | TypeFlags.Object - | TypeFlags.Intersection) - && source !== globalObjectType - && target.flags - & (TypeFlags.Object | TypeFlags.Intersection) - && isWeakType(target) - && (getPropertiesOfType(source).length > 0 - || typeHasCallOrConstructSignatures(source)); - if (isPerformingCommonPropertyChecks - && !hasCommonProperties( - source, - target, - isComparingJsxAttributes - )) - { - if (reportErrors) { - const calls = getSignaturesOfType( - source, - SignatureKind.Call - ); - const constructs = getSignaturesOfType( - source, - SignatureKind.Construct - ); - if (calls.length > 0 - && isRelatedTo( - getReturnTypeOfSignature(calls[0]), - target, /*reportErrors*/ - false - ) - || constructs.length > 0 - && isRelatedTo( - getReturnTypeOfSignature(constructs[0]), - target, /*reportErrors*/ - false - )) - { - reportError( - Diagnostics - .Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, - typeToString(source), - typeToString(target) - ); - } else { - reportError( - Diagnostics - .Type_0_has_no_properties_in_common_with_type_1, - typeToString(source), - typeToString(target) - ); - } - } - return Ternary.False; - } - - let result = Ternary.False; - const saveErrorInfo = captureErrorCalculationState(); - let isIntersectionConstituent = - !!isApparentIntersectionConstituent; - - // Note that these checks are specifically ordered to produce correct results. In particular, - // we need to deconstruct unions before intersections (because unions are always at the top), - // and we need to handle "each" relations before "some" relations for the same kind of type. - if (source.flags & TypeFlags.Union) { - result = relation === comparableRelation - ? someTypeRelatedToType( - source as UnionType, - target, - reportErrors - && !(source.flags & TypeFlags.Primitive) - ) - : eachTypeRelatedToType( - source as UnionType, - target, - reportErrors - && !(source.flags & TypeFlags.Primitive) - ); - } else { - if (target.flags & TypeFlags.Union) { - result = typeRelatedToSomeType( - getRegularTypeOfObjectLiteral(source), - target, - reportErrors - && !(source.flags & TypeFlags.Primitive) - && !(target.flags & TypeFlags.Primitive) - ); - } else if (target.flags & TypeFlags.Intersection) { - isIntersectionConstituent = true; // set here to affect the following trio of checks - result = typeRelatedToEachType( - getRegularTypeOfObjectLiteral(source), - target as IntersectionType, - reportErrors - ); - if (result - && (isPerformingExcessPropertyChecks - || isPerformingCommonPropertyChecks)) - { - // Validate against excess props using the original `source` - if (!propertiesRelatedTo( - source, - target, - reportErrors, /*excludedProperties*/ - undefined, /*isIntersectionConstituent*/ - false - )) { - return Ternary.False; - } - } - } else if (source.flags & TypeFlags.Intersection) { - // Check to see if any constituents of the intersection are immediately related to the target. - // - // Don't report errors though. Checking whether a constituent is related to the source is not actually - // useful and leads to some confusing error messages. Instead it is better to let the below checks - // take care of this, or to not elaborate at all. For instance, - // - // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. - // - // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection - // than to report that 'D' is not assignable to 'A' or 'B'. - // - // - For a primitive type or type parameter (such as 'number = A & B') there is no point in - // breaking the intersection apart. - result = someTypeRelatedToType( - source, - target, /*reportErrors*/ - false - ); - } - if (!result - && (source.flags & TypeFlags.StructuredOrInstantiable - || target.flags - & TypeFlags.StructuredOrInstantiable)) - { - if (result = recursiveTypeRelatedTo( - source, - target, - reportErrors, - isIntersectionConstituent - )) { - resetErrorInfo(saveErrorInfo); - } - } - } - if (!result - && source.flags - & (TypeFlags.Intersection | TypeFlags.TypeParameter)) - { - // The combined constraint of an intersection type is the intersection of the constraints of - // the constituents. When an intersection type contains instantiable types with union type - // constraints, there are situations where we need to examine the combined constraint. One is - // when the target is a union type. Another is when the intersection contains types belonging - // to one of the disjoint domains. For example, given type variables T and U, each with the - // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and - // we need to check this constraint against a union on the target side. Also, given a type - // variable V constrained to 'string | number', 'V & number' has a combined constraint of - // 'string & number | number & number' which reduces to just 'number'. - // This also handles type parameters, as a type parameter with a union constraint compared against a union - // needs to have its constraint hoisted into an intersection with said type parameter, this way - // the type param can be compared with itself in the target (with the influence of its constraint to match other parts) - // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)` - const constraint = getEffectiveConstraintOfIntersection( - source.flags & TypeFlags.Intersection - ? ( source).types - : [source], - !!(target.flags & TypeFlags.Union) - ); - if (constraint - && (source.flags & TypeFlags.Intersection - || target.flags & TypeFlags.Union)) - { - if (everyType( - constraint, - c => c !== source - )) { // Skip comparison if expansion contains the source itself - // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this - if (result = isRelatedTo( - constraint, - target, /*reportErrors*/ - false, /*headMessage*/ - undefined, - isIntersectionConstituent - )) { - resetErrorInfo(saveErrorInfo); - } - } - } - } - - if (!result && reportErrors) { - source = originalSource.aliasSymbol - ? originalSource - : source; - target = originalTarget.aliasSymbol - ? originalTarget - : target; - let maybeSuppress = overrideNextErrorInfo > 0; - if (maybeSuppress) { - overrideNextErrorInfo--; - } - if (source.flags & TypeFlags.Object - && target.flags & TypeFlags.Object) - { - const currentError = errorInfo; - tryElaborateArrayLikeErrors( - source, - target, - reportErrors - ); - if (errorInfo !== currentError) { - maybeSuppress = !!errorInfo; - } - } - if (source.flags & TypeFlags.Object - && target.flags & TypeFlags.Primitive) - { - tryElaborateErrorsForPrimitivesAndObjects( - source, - target - ); - } else if (source.symbol && source.flags & TypeFlags.Object - && globalObjectType === source) - { - reportError(Diagnostics - .The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead); - } else if (isComparingJsxAttributes - && target.flags & TypeFlags.Intersection) - { - const targetTypes = (target as IntersectionType).types; - const intrinsicAttributes = getJsxType( - JsxNames.IntrinsicAttributes, - errorNode - ); - const intrinsicClassAttributes = getJsxType( - JsxNames.IntrinsicClassAttributes, - errorNode - ); - if (intrinsicAttributes !== errorType - && intrinsicClassAttributes !== errorType - && (contains(targetTypes, intrinsicAttributes) - || contains( - targetTypes, - intrinsicClassAttributes - ))) - { - // do not report top error - return result; - } - } - if (!headMessage && maybeSuppress) { - lastSkippedInfo = [source, target]; - // Used by, eg, missing property checking to replace the top-level message with a more informative one - return result; - } - reportRelationError(headMessage, source, target); - } - return result; - } - - function isIdenticalTo(source: Type, target: Type): Ternary { - let result: Ternary; - const flags = source.flags & target.flags; - if (flags & TypeFlags.Object || flags & TypeFlags.IndexedAccess - || flags & TypeFlags.Conditional || flags & TypeFlags.Index - || flags & TypeFlags.Substitution) - { - return recursiveTypeRelatedTo( - source, - target, /*reportErrors*/ - false, /*isIntersectionConstituent*/ - false - ); - } - if (flags & (TypeFlags.Union | TypeFlags.Intersection)) { - if (result = eachTypeRelatedToSomeType( - source, - target - )) { - if (result &= eachTypeRelatedToSomeType( - target, - source - )) { - return result; - } - } - } - return Ternary.False; - } - - function getTypeOfPropertyInTypes(types: Type[], name: __String) { - const appendPropType = ( - propTypes: Type[] | undefined, - type: Type - ) => { - type = getApparentType(type); - const prop = type.flags & TypeFlags.UnionOrIntersection - ? getPropertyOfUnionOrIntersectionType( - type, - name - ) - : getPropertyOfObjectType(type, name); - const propType = prop && getTypeOfSymbol(prop) - || isNumericLiteralName(name) - && getIndexTypeOfType(type, IndexKind.Number) - || getIndexTypeOfType(type, IndexKind.String) - || undefinedType; - return append(propTypes, propType); - }; - return getUnionType(reduceLeft( - types, - appendPropType, /*initial*/ - undefined - ) || emptyArray); - } - - function hasExcessProperties( - source: FreshObjectLiteralType, - target: Type, - reportErrors: boolean - ): boolean { - if (!isExcessPropertyCheckTarget(target) || !noImplicitAny - && getObjectFlags(target) & ObjectFlags.JSLiteral) - { - return false; // Disable excess property checks on JS literals to simulate having an implicit "index signature" - but only outside of noImplicitAny - } - const isComparingJsxAttributes = - !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - if ((relation === assignableRelation - || relation === comparableRelation) - && (isTypeSubsetOf(globalObjectType, target) - || (!isComparingJsxAttributes - && isEmptyObjectType(target)))) - { - return false; - } - let reducedTarget = target; - let checkTypes: Type[] | undefined; - if (target.flags & TypeFlags.Union) { - reducedTarget = findMatchingDiscriminantType( - source, - target, - isRelatedTo - ) - || filterPrimitivesIfContainsNonPrimitive( target); - checkTypes = reducedTarget.flags & TypeFlags.Union - ? ( reducedTarget).types - : [reducedTarget]; - } - for (const prop of getPropertiesOfType(source)) { - if (shouldCheckAsExcessProperty(prop, source.symbol)) { - if (!isKnownProperty( - reducedTarget, - prop.escapedName, - isComparingJsxAttributes - )) { - if (reportErrors) { - // Report error in terms of object types in the target as those are the only ones - // we check in isKnownProperty. - const errorTarget = filterType( - reducedTarget, - isExcessPropertyCheckTarget - ); - // We know *exactly* where things went wrong when comparing the types. - // Use this property as the error node as this will be more helpful in - // reasoning about what went wrong. - if (!errorNode) return Debug.fail(); - if (isJsxAttributes(errorNode) - || isJsxOpeningLikeElement(errorNode) - || isJsxOpeningLikeElement(errorNode - .parent)) - { - // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. - // However, using an object-literal error message will be very confusing to the users so we give different a message. - // TODO: Spelling suggestions for excess jsx attributes (needs new diagnostic messages) - if (prop.valueDeclaration - && isJsxAttribute(prop - .valueDeclaration) - && getSourceFileOfNode(errorNode) - === getSourceFileOfNode(prop - .valueDeclaration.name)) - { - // Note that extraneous children (as in `extra`) don't pass this check, - // since `children` is a SyntaxKind.PropertySignature instead of a SyntaxKind.JsxAttribute. - errorNode = prop.valueDeclaration.name; - } - reportError( - Diagnostics - .Property_0_does_not_exist_on_type_1, - symbolToString(prop), - typeToString(errorTarget) - ); - } else { - // use the property's value declaration if the property is assigned inside the literal itself - const objectLiteralDeclaration = - source.symbol - && firstOrUndefined(source.symbol - .declarations); - let suggestion; - if (prop.valueDeclaration - && findAncestor( - prop.valueDeclaration, - d => d === objectLiteralDeclaration - ) - && getSourceFileOfNode(objectLiteralDeclaration) - === getSourceFileOfNode(errorNode)) - { - const propDeclaration = prop - .valueDeclaration as ObjectLiteralElementLike; - Debug.assertNode( - propDeclaration, - isObjectLiteralElementLike - ); - - errorNode = propDeclaration; - - const name = propDeclaration.name!; - if (isIdentifier(name)) { - suggestion = getSuggestionForNonexistentProperty( - name, - errorTarget - ); - } - } - if (suggestion !== undefined) { - reportError( - Diagnostics - .Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, - symbolToString(prop), - typeToString(errorTarget), - suggestion - ); - } else { - reportError( - Diagnostics - .Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - symbolToString(prop), - typeToString(errorTarget) - ); - } - } - } - return true; - } - if (checkTypes - && !isRelatedTo( - getTypeOfSymbol(prop), - getTypeOfPropertyInTypes( - checkTypes, - prop.escapedName - ), - reportErrors - )) - { - if (reportErrors) { - reportIncompatibleError( - Diagnostics - .Types_of_property_0_are_incompatible, - symbolToString(prop) - ); - } - return true; - } - } - } - return false; - } - - function shouldCheckAsExcessProperty( - prop: Symbol, - container: Symbol - ) { - return prop.valueDeclaration && container.valueDeclaration - && prop.valueDeclaration.parent - === container.valueDeclaration; - } - - function eachTypeRelatedToSomeType( - source: UnionOrIntersectionType, - target: UnionOrIntersectionType - ): Ternary { - let result = Ternary.True; - const sourceTypes = source.types; - for (const sourceType of sourceTypes) { - const related = typeRelatedToSomeType( - sourceType, - target, /*reportErrors*/ - false - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function typeRelatedToSomeType( - source: Type, - target: UnionOrIntersectionType, - reportErrors: boolean - ): Ternary { - const targetTypes = target.types; - if (target.flags & TypeFlags.Union - && containsType(targetTypes, source)) - { - return Ternary.True; - } - for (const type of targetTypes) { - const related = isRelatedTo( - source, - type, /*reportErrors*/ - false - ); - if (related) { - return related; - } - } - if (reportErrors) { - const bestMatchingType = getBestMatchingType( - source, - target, - isRelatedTo - ); - isRelatedTo( - source, - bestMatchingType - || targetTypes[targetTypes.length - - 1], /*reportErrors*/ - true - ); - } - return Ternary.False; - } - - function typeRelatedToEachType( - source: Type, - target: IntersectionType, - reportErrors: boolean - ): Ternary { - let result = Ternary.True; - const targetTypes = target.types; - for (const targetType of targetTypes) { - const related = isRelatedTo( - source, - targetType, - reportErrors, /*headMessage*/ - undefined, /*isIntersectionConstituent*/ - true - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function someTypeRelatedToType( - source: UnionOrIntersectionType, - target: Type, - reportErrors: boolean - ): Ternary { - const sourceTypes = source.types; - if (source.flags & TypeFlags.Union - && containsType(sourceTypes, target)) - { - return Ternary.True; - } - const len = sourceTypes.length; - for (let i = 0; i < len; i++) { - const related = isRelatedTo( - sourceTypes[i], - target, - reportErrors && i === len - 1 - ); - if (related) { - return related; - } - } - return Ternary.False; - } - - function eachTypeRelatedToType( - source: UnionOrIntersectionType, - target: Type, - reportErrors: boolean - ): Ternary { - let result = Ternary.True; - const sourceTypes = source.types; - for (const sourceType of sourceTypes) { - const related = isRelatedTo( - sourceType, - target, - reportErrors - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function typeArgumentsRelatedTo( - sources: readonly Type[] = emptyArray, - targets: readonly Type[] = emptyArray, - variances: readonly VarianceFlags[] = emptyArray, - reportErrors: boolean, - isIntersectionConstituent: boolean - ): Ternary { - if (sources.length !== targets.length - && relation === identityRelation) - { - return Ternary.False; - } - const length = sources.length <= targets.length - ? sources.length - : targets.length; - let result = Ternary.True; - for (let i = 0; i < length; i++) { - // When variance information isn't available we default to covariance. This happens - // in the process of computing variance information for recursive types and when - // comparing 'this' type arguments. - const varianceFlags = i < variances.length - ? variances[i] - : VarianceFlags.Covariant; - const variance = - varianceFlags & VarianceFlags.VarianceMask; - // We ignore arguments for independent type parameters (because they're never witnessed). - if (variance !== VarianceFlags.Independent) { - const s = sources[i]; - const t = targets[i]; - let related = Ternary.True; - if (varianceFlags & VarianceFlags.Unmeasurable) { - // Even an `Unmeasurable` variance works out without a structural check if the source and target are _identical_. - // We can't simply assume invariance, because `Unmeasurable` marks nonlinear relations, for example, a relation tained by - // the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be) - related = relation === identityRelation - ? isRelatedTo(s, t, /*reportErrors*/ false) - : compareTypesIdentical(s, t); - } else if (variance === VarianceFlags.Covariant) { - related = isRelatedTo( - s, - t, - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - ); - } else if (variance === VarianceFlags.Contravariant) { - related = isRelatedTo( - t, - s, - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - ); - } else if (variance === VarianceFlags.Bivariant) { - // In the bivariant case we first compare contravariantly without reporting - // errors. Then, if that doesn't succeed, we compare covariantly with error - // reporting. Thus, error elaboration will be based on the the covariant check, - // which is generally easier to reason about. - related = isRelatedTo( - t, - s, /*reportErrors*/ - false - ); - if (!related) { - related = isRelatedTo( - s, - t, - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - ); - } - } else { - // In the invariant case we first compare covariantly, and only when that - // succeeds do we proceed to compare contravariantly. Thus, error elaboration - // will typically be based on the covariant check. - related = isRelatedTo( - s, - t, - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - ); - if (related) { - related &= isRelatedTo( - t, - s, - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - ); - } - } - if (!related) { - return Ternary.False; - } - result &= related; - } - } - return result; - } - - // Determine if possibly recursive types are related. First, check if the result is already available in the global cache. - // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true. - // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are - // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion - // and issue an error. Otherwise, actually compare the structure of the two types. - function recursiveTypeRelatedTo( - source: Type, - target: Type, - reportErrors: boolean, - isIntersectionConstituent: boolean - ): Ternary { - if (overflow) { - return Ternary.False; - } - const id = getRelationKey( - source, - target, - isIntersectionConstituent, - relation - ); - const entry = relation.get(id); - if (entry !== undefined) { - if (reportErrors && entry & RelationComparisonResult.Failed - && !(entry & RelationComparisonResult.Reported)) - { - // We are elaborating errors and the cached result is an unreported failure. The result will be reported - // as a failure, and should be updated as a reported failure by the bottom of this function. - } else { - if (outofbandVarianceMarkerHandler) { - // We're in the middle of variance checking - integrate any unmeasurable/unreliable flags from this cached component - const saved = - entry & RelationComparisonResult.ReportsMask; - if (saved - & RelationComparisonResult.ReportsUnmeasurable) - { - instantiateType( - source, - reportUnmeasurableMarkers - ); - } - if (saved - & RelationComparisonResult.ReportsUnreliable) - { - instantiateType( - source, - reportUnreliableMarkers - ); - } - } - return entry & RelationComparisonResult.Succeeded - ? Ternary.True - : Ternary.False; - } - } - if (!maybeKeys) { - maybeKeys = []; - sourceStack = []; - targetStack = []; - } else { - for (let i = 0; i < maybeCount; i++) { - // If source and target are already being compared, consider them related with assumptions - if (id === maybeKeys[i]) { - return Ternary.Maybe; - } - } - if (depth === 100) { - overflow = true; - return Ternary.False; - } - } - const maybeStart = maybeCount; - maybeKeys[maybeCount] = id; - maybeCount++; - sourceStack[depth] = source; - targetStack[depth] = target; - depth++; - const saveExpandingFlags = expandingFlags; - if (!(expandingFlags & ExpandingFlags.Source) - && isDeeplyNestedType(source, sourceStack, depth)) - { - expandingFlags |= ExpandingFlags.Source; - } - if (!(expandingFlags & ExpandingFlags.Target) - && isDeeplyNestedType(target, targetStack, depth)) - { - expandingFlags |= ExpandingFlags.Target; - } - let originalHandler: typeof outofbandVarianceMarkerHandler; - let propagatingVarianceFlags: RelationComparisonResult = 0; - if (outofbandVarianceMarkerHandler) { - originalHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = onlyUnreliable => { - propagatingVarianceFlags |= onlyUnreliable - ? RelationComparisonResult.ReportsUnreliable - : RelationComparisonResult.ReportsUnmeasurable; - return originalHandler!(onlyUnreliable); - }; - } - const result = expandingFlags !== ExpandingFlags.Both - ? structuredTypeRelatedTo( - source, - target, - reportErrors, - isIntersectionConstituent - ) - : Ternary.Maybe; - if (outofbandVarianceMarkerHandler) { - outofbandVarianceMarkerHandler = originalHandler; - } - expandingFlags = saveExpandingFlags; - depth--; - if (result) { - if (result === Ternary.True || depth === 0) { - // If result is definitely true, record all maybe keys as having succeeded - for (let i = maybeStart; i < maybeCount; i++) { - relation.set( - maybeKeys[i], - RelationComparisonResult.Succeeded - | propagatingVarianceFlags - ); - } - maybeCount = maybeStart; - } - } else { - // A false result goes straight into global cache (when something is false under - // assumptions it will also be false without assumptions) - relation.set( - id, - (reportErrors ? RelationComparisonResult.Reported : 0) - | RelationComparisonResult.Failed - | propagatingVarianceFlags - ); - maybeCount = maybeStart; - } - return result; - } - - function structuredTypeRelatedTo( - source: Type, - target: Type, - reportErrors: boolean, - isIntersectionConstituent: boolean - ): Ternary { - const flags = source.flags & target.flags; - if (relation === identityRelation - && !(flags & TypeFlags.Object)) - { - if (flags & TypeFlags.Index) { - return isRelatedTo( - ( source).type, - ( target).type, /*reportErrors*/ - false - ); - } - let result = Ternary.False; - if (flags & TypeFlags.IndexedAccess) { - if (result = isRelatedTo( - ( source).objectType, - ( target) - .objectType, /*reportErrors*/ - false - )) { - if (result &= isRelatedTo( - ( source).indexType, - ( target) - .indexType, /*reportErrors*/ - false - )) { - return result; - } - } - } - if (flags & TypeFlags.Conditional) { - if (( source).root.isDistributive - === ( target).root.isDistributive) - { - if (result = isRelatedTo( - ( source).checkType, - ( target) - .checkType, /*reportErrors*/ - false - )) { - if (result &= isRelatedTo( - ( source).extendsType, - ( target) - .extendsType, /*reportErrors*/ - false - )) { - if (result &= isRelatedTo( - getTrueTypeFromConditionalType( source), - getTrueTypeFromConditionalType( target), /*reportErrors*/ - false - )) { - if (result &= isRelatedTo( - getFalseTypeFromConditionalType( source), - getFalseTypeFromConditionalType( target), /*reportErrors*/ - false - )) { - return result; - } - } - } - } - } - } - if (flags & TypeFlags.Substitution) { - return isRelatedTo( - ( source).substitute, - ( target) - .substitute, /*reportErrors*/ - false - ); - } - return Ternary.False; - } - - let result: Ternary; - let originalErrorInfo: DiagnosticMessageChain | undefined; - let varianceCheckFailed = false; - const saveErrorInfo = captureErrorCalculationState(); - - // We limit alias variance probing to only object and conditional types since their alias behavior - // is more predictable than other, interned types, which may or may not have an alias depending on - // the order in which things were checked. - if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) - && source.aliasSymbol - && source.aliasTypeArguments - && source.aliasSymbol === target.aliasSymbol - && !(source.aliasTypeArgumentsContainsMarker - || target.aliasTypeArgumentsContainsMarker)) - { - const variances = getAliasVariances(source.aliasSymbol); - const varianceResult = relateVariances( - source.aliasTypeArguments, - target.aliasTypeArguments, - variances, - isIntersectionConstituent - ); - if (varianceResult !== undefined) { - return varianceResult; - } - } - - if (target.flags & TypeFlags.TypeParameter) { - // A source type { [P in Q]: X } is related to a target type T if keyof T is related to Q and X is related to T[Q]. - if (getObjectFlags(source) & ObjectFlags.Mapped - && isRelatedTo( - getIndexType(target), - getConstraintTypeFromMappedType( source) - )) - { - if (!(getMappedTypeModifiers( source) - & MappedTypeModifiers.IncludeOptional)) - { - const templateType = - getTemplateTypeFromMappedType( source); - const indexedAccessType = getIndexedAccessType( - target, - getTypeParameterFromMappedType( source) - ); - if (result = isRelatedTo( - templateType, - indexedAccessType, - reportErrors - )) { - return result; - } - } - } - } else if (target.flags & TypeFlags.Index) { - // A keyof S is related to a keyof T if T is related to S. - if (source.flags & TypeFlags.Index) { - if (result = isRelatedTo( - ( target).type, - ( source).type, /*reportErrors*/ - false - )) { - return result; - } - } - // A type S is assignable to keyof T if S is assignable to keyof C, where C is the - // simplified form of T or, if T doesn't simplify, the constraint of T. - const constraint = - getSimplifiedTypeOrConstraint(( target) - .type); - if (constraint) { - // We require Ternary.True here such that circular constraints don't cause - // false positives. For example, given 'T extends { [K in keyof T]: string }', - // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when - // related to other types. - if (isRelatedTo( - source, - getIndexType( - constraint, - (target as IndexType).stringsOnly - ), - reportErrors - ) === Ternary.True) { - return Ternary.True; - } - } - } else if (target.flags & TypeFlags.IndexedAccess) { - // A type S is related to a type T[K] if S is related to C, where C is the base - // constraint of T[K] for writing. - if (relation !== identityRelation) { - const objectType = ( target) - .objectType; - const indexType = ( target) - .indexType; - const baseObjectType = - getBaseConstraintOfType(objectType) || objectType; - const baseIndexType = - getBaseConstraintOfType(indexType) || indexType; - if (!isGenericObjectType(baseObjectType) - && !isGenericIndexType(baseIndexType)) - { - const accessFlags = - AccessFlags.Writing - | (baseObjectType !== objectType - ? AccessFlags.NoIndexSignatures - : 0); - const constraint = getIndexedAccessTypeOrUndefined( - baseObjectType, - baseIndexType, /*accessNode*/ - undefined, - accessFlags - ); - if (constraint - && (result = isRelatedTo( - source, - constraint, - reportErrors - ))) - { - return result; - } - } - } - } else if (isGenericMappedType(target)) { - // A source type T is related to a target type { [P in X]: T[P] } - const template = getTemplateTypeFromMappedType(target); - const modifiers = getMappedTypeModifiers(target); - if (!(modifiers & MappedTypeModifiers.ExcludeOptional)) { - if (template.flags & TypeFlags.IndexedAccess - && ( template).objectType - === source - && ( template).indexType - === getTypeParameterFromMappedType(target)) - { - return Ternary.True; - } - if (!isGenericMappedType(source)) { - const targetConstraint = - getConstraintTypeFromMappedType(target); - const sourceKeys = getIndexType( - source, /*stringsOnly*/ - undefined, /*noIndexSignatures*/ - true - ); - const includeOptional = - modifiers - & MappedTypeModifiers.IncludeOptional; - const filteredByApplicability = includeOptional - ? intersectTypes(targetConstraint, sourceKeys) - : undefined; - // A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X. - // A source type T is related to a target type { [P in Q]?: X } if some constituent Q' of Q is related to keyof T and T[Q'] is related to X. - if (includeOptional - ? !(filteredByApplicability!.flags - & TypeFlags.Never) - : isRelatedTo(targetConstraint, sourceKeys)) - { - const typeParameter = - getTypeParameterFromMappedType(target); - const indexingType = filteredByApplicability - ? getIntersectionType([filteredByApplicability, - typeParameter]) - : typeParameter; - const indexedAccessType = getIndexedAccessType( - source, - indexingType - ); - const templateType = - getTemplateTypeFromMappedType(target); - if (result = isRelatedTo( - indexedAccessType, - templateType, - reportErrors - )) { - return result; - } - } - originalErrorInfo = errorInfo; - resetErrorInfo(saveErrorInfo); - } - } - } - - if (source.flags & TypeFlags.TypeVariable) { - if (source.flags & TypeFlags.IndexedAccess - && target.flags & TypeFlags.IndexedAccess) - { - // A type S[K] is related to a type T[J] if S is related to T and K is related to J. - if (result = isRelatedTo( - ( source).objectType, - ( target).objectType, - reportErrors - )) { - result &= isRelatedTo( - ( source).indexType, - ( target).indexType, - reportErrors - ); - } - if (result) { - resetErrorInfo(saveErrorInfo); - return result; - } - } else { - const constraint = - getConstraintOfType( source); - if (!constraint - || (source.flags & TypeFlags.TypeParameter - && constraint.flags & TypeFlags.Any)) - { - // A type variable with no constraint is not related to the non-primitive object type. - if (result = isRelatedTo( - emptyObjectType, - extractTypesOfKind( - target, - ~TypeFlags.NonPrimitive - ) - )) { - resetErrorInfo(saveErrorInfo); - return result; - } - } // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed - else if (result = isRelatedTo( - constraint, - target, /*reportErrors*/ - false, /*headMessage*/ - undefined, - isIntersectionConstituent - )) { - resetErrorInfo(saveErrorInfo); - return result; - } // slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example - else if (result = isRelatedTo( - getTypeWithThisArgument(constraint, source), - target, - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - )) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - } else if (source.flags & TypeFlags.Index) { - if (result = isRelatedTo( - keyofConstraintType, - target, - reportErrors - )) { - resetErrorInfo(saveErrorInfo); - return result; - } - } else if (source.flags & TypeFlags.Conditional) { - if (target.flags & TypeFlags.Conditional) { - // Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if - // one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2, - // and Y1 is related to Y2. - const sourceParams = (source as ConditionalType).root - .inferTypeParameters; - let sourceExtends = ( source) - .extendsType; - let mapper: TypeMapper | undefined; - if (sourceParams) { - // If the source has infer type parameters, we instantiate them in the context of the target - const ctx = createInferenceContext( - sourceParams, /*signature*/ - undefined, - InferenceFlags.None, - isRelatedTo - ); - inferTypes( - ctx.inferences, - ( target).extendsType, - sourceExtends, - InferencePriority.NoConstraints - | InferencePriority.AlwaysStrict - ); - sourceExtends = instantiateType( - sourceExtends, - ctx.mapper - ); - mapper = ctx.mapper; - } - if (isTypeIdenticalTo( - sourceExtends, - ( target).extendsType - ) - && (isRelatedTo( - ( source).checkType, - ( target).checkType - ) - || isRelatedTo( - ( target).checkType, - ( source).checkType - ))) - { - if (result = isRelatedTo( - instantiateType( - getTrueTypeFromConditionalType( source), - mapper - ), - getTrueTypeFromConditionalType( target), - reportErrors - )) { - result &= isRelatedTo( - getFalseTypeFromConditionalType( source), - getFalseTypeFromConditionalType( target), - reportErrors - ); - } - if (result) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - } else { - const distributiveConstraint = - getConstraintOfDistributiveConditionalType( source); - if (distributiveConstraint) { - if (result = isRelatedTo( - distributiveConstraint, - target, - reportErrors - )) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - const defaultConstraint = - getDefaultConstraintOfConditionalType( source); - if (defaultConstraint) { - if (result = isRelatedTo( - defaultConstraint, - target, - reportErrors - )) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - } - } else { - // An empty object type is related to any mapped type that includes a '?' modifier. - if (relation !== subtypeRelation - && relation !== strictSubtypeRelation - && isPartialMappedType(target) - && isEmptyObjectType(source)) - { - return Ternary.True; - } - if (isGenericMappedType(target)) { - if (isGenericMappedType(source)) { - if (result = mappedTypeRelatedTo( - source, - target, - reportErrors - )) { - resetErrorInfo(saveErrorInfo); - return result; - } - } - return Ternary.False; - } - const sourceIsPrimitive = - !!(source.flags & TypeFlags.Primitive); - if (relation !== identityRelation) { - source = getApparentType(source); - } else if (isGenericMappedType(source)) { - return Ternary.False; - } - if (getObjectFlags(source) & ObjectFlags.Reference - && getObjectFlags(target) & ObjectFlags.Reference - && ( source).target - === ( target).target - && !(getObjectFlags(source) & ObjectFlags.MarkerType - || getObjectFlags(target) - & ObjectFlags.MarkerType)) - { - // We have type references to the same generic type, and the type references are not marker - // type references (which are intended by be compared structurally). Obtain the variance - // information for the type parameters and relate the type arguments accordingly. - const variances = - getVariances(( source).target); - const varianceResult = relateVariances( - getTypeArguments( source), - getTypeArguments( target), - variances, - isIntersectionConstituent - ); - if (varianceResult !== undefined) { - return varianceResult; - } - } else if (isReadonlyArrayType(target) - ? isArrayType(source) || isTupleType(source) - : isArrayType(target) && isTupleType(source) - && !source.target.readonly) - { - if (relation !== identityRelation) { - return isRelatedTo( - getIndexTypeOfType(source, IndexKind.Number) - || anyType, - getIndexTypeOfType(target, IndexKind.Number) - || anyType, - reportErrors - ); - } else { - // By flags alone, we know that the `target` is a readonly array while the source is a normal array or tuple - // or `target` is an array and source is a tuple - in both cases the types cannot be identical, by construction - return Ternary.False; - } - } // Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})` - // and not `{} <- fresh({}) <- {[idx: string]: any}` - else if ((relation === subtypeRelation - || relation === strictSubtypeRelation) - && isEmptyObjectType(target) - && getObjectFlags(target) & ObjectFlags.FreshLiteral - && !isEmptyObjectType(source)) - { - return Ternary.False; - } - // Even if relationship doesn't hold for unions, intersections, or generic type references, - // it may hold in a structural comparison. - // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates - // to X. Failing both of those we want to check if the aggregation of A and B's members structurally - // relates to X. Thus, we include intersection types on the source side here. - if (source.flags - & (TypeFlags.Object | TypeFlags.Intersection) - && target.flags & TypeFlags.Object) - { - // Report structural errors only if we haven't reported any errors yet - const reportStructuralErrors = reportErrors - && errorInfo === saveErrorInfo.errorInfo - && !sourceIsPrimitive; - result = propertiesRelatedTo( - source, - target, - reportStructuralErrors, /*excludedProperties*/ - undefined, - isIntersectionConstituent - ); - if (result) { - result &= signaturesRelatedTo( - source, - target, - SignatureKind.Call, - reportStructuralErrors - ); - if (result) { - result &= signaturesRelatedTo( - source, - target, - SignatureKind.Construct, - reportStructuralErrors - ); - if (result) { - result &= indexTypesRelatedTo( - source, - target, - IndexKind.String, - sourceIsPrimitive, - reportStructuralErrors - ); - if (result) { - result &= indexTypesRelatedTo( - source, - target, - IndexKind.Number, - sourceIsPrimitive, - reportStructuralErrors - ); - } - } - } - } - if (varianceCheckFailed && result) { - errorInfo = originalErrorInfo || errorInfo - || saveErrorInfo - .errorInfo; // Use variance error (there is no structural one) and return false - } else if (result) { - return result; - } - } - // If S is an object type and T is a discriminated union, S may be related to T if - // there exists a constituent of T for every combination of the discriminants of S - // with respect to T. We do not report errors here, as we will use the existing - // error result from checking each constituent of the union. - if (source.flags - & (TypeFlags.Object | TypeFlags.Intersection) - && target.flags & TypeFlags.Union) - { - const objectOnlyTarget = extractTypesOfKind( - target, - TypeFlags.Object - ); - if (objectOnlyTarget.flags & TypeFlags.Union) { - const result = typeRelatedToDiscriminatedType( - source, - objectOnlyTarget as UnionType - ); - if (result) { - return result; - } - } - } - } - return Ternary.False; - - function relateVariances( - sourceTypeArguments: readonly Type[] | undefined, - targetTypeArguments: readonly Type[] | undefined, - variances: VarianceFlags[], - isIntersectionConstituent: boolean - ) { - if (result = typeArgumentsRelatedTo( - sourceTypeArguments, - targetTypeArguments, - variances, - reportErrors, - isIntersectionConstituent - )) { - return result; - } - if (some( - variances, - v => !!(v & VarianceFlags.AllowsStructuralFallback) - )) { - // If some type parameter was `Unmeasurable` or `Unreliable`, and we couldn't pass by assuming it was identical, then we - // have to allow a structural fallback check - // We elide the variance-based error elaborations, since those might not be too helpful, since we'll potentially - // be assuming identity of the type parameter. - originalErrorInfo = undefined; - resetErrorInfo(saveErrorInfo); - return undefined; - } - const allowStructuralFallback = targetTypeArguments - && hasCovariantVoidArgument( - targetTypeArguments, - variances - ); - varianceCheckFailed = !allowStructuralFallback; - // The type arguments did not relate appropriately, but it may be because we have no variance - // information (in which case typeArgumentsRelatedTo defaulted to covariance for all type - // arguments). It might also be the case that the target type has a 'void' type argument for - // a covariant type parameter that is only used in return positions within the generic type - // (in which case any type argument is permitted on the source side). In those cases we proceed - // with a structural comparison. Otherwise, we know for certain the instantiations aren't - // related and we can return here. - if (variances !== emptyArray && !allowStructuralFallback) { - // In some cases generic types that are covariant in regular type checking mode become - // invariant in --strictFunctionTypes mode because one or more type parameters are used in - // both co- and contravariant positions. In order to make it easier to diagnose *why* such - // types are invariant, if any of the type parameters are invariant we reset the reported - // errors and instead force a structural comparison (which will include elaborations that - // reveal the reason). - // We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`, - // we can return `False` early here to skip calculating the structural error message we don't need. - if (varianceCheckFailed - && !(reportErrors - && some( - variances, - v => (v & VarianceFlags.VarianceMask) - === VarianceFlags.Invariant - ))) - { - return Ternary.False; - } - // We remember the original error information so we can restore it in case the structural - // comparison unexpectedly succeeds. This can happen when the structural comparison result - // is a Ternary.Maybe for example caused by the recursion depth limiter. - originalErrorInfo = errorInfo; - resetErrorInfo(saveErrorInfo); - } - } - } - - function reportUnmeasurableMarkers(p: TypeParameter) { - if (outofbandVarianceMarkerHandler - && (p === markerSuperType || p === markerSubType - || p === markerOtherType)) - { - outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false); - } - return p; - } - - function reportUnreliableMarkers(p: TypeParameter) { - if (outofbandVarianceMarkerHandler - && (p === markerSuperType || p === markerSubType - || p === markerOtherType)) - { - outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true); - } - return p; - } - - // A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is - // related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice - // that S and T are contra-variant whereas X and Y are co-variant. - function mappedTypeRelatedTo( - source: MappedType, - target: MappedType, - reportErrors: boolean - ): Ternary { - const modifiersRelated = relation === comparableRelation - || (relation === identityRelation - ? getMappedTypeModifiers(source) - === getMappedTypeModifiers(target) - : getCombinedMappedTypeOptionality(source) - <= getCombinedMappedTypeOptionality(target)); - if (modifiersRelated) { - let result: Ternary; - const targetConstraint = - getConstraintTypeFromMappedType(target); - const sourceConstraint = instantiateType( - getConstraintTypeFromMappedType(source), - getCombinedMappedTypeOptionality(source) < 0 - ? reportUnmeasurableMarkers - : reportUnreliableMarkers - ); - if (result = isRelatedTo( - targetConstraint, - sourceConstraint, - reportErrors - )) { - const mapper = createTypeMapper( - [getTypeParameterFromMappedType(source)], - [getTypeParameterFromMappedType(target)] - ); - return result - & isRelatedTo( - instantiateType( - getTemplateTypeFromMappedType(source), - mapper - ), - getTemplateTypeFromMappedType(target), - reportErrors - ); - } - } - return Ternary.False; - } - - function typeRelatedToDiscriminatedType( - source: Type, - target: UnionType - ) { - // 1. Generate the combinations of discriminant properties & types 'source' can satisfy. - // a. If the number of combinations is above a set limit, the comparison is too complex. - // 2. Filter 'target' to the subset of types whose discriminants exist in the matrix. - // a. If 'target' does not satisfy all discriminants in the matrix, 'source' is not related. - // 3. For each type in the filtered 'target', determine if all non-discriminant properties of - // 'target' are related to a property in 'source'. - // - // NOTE: See ~/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts - // for examples. - - const sourceProperties = getPropertiesOfObjectType(source); - const sourcePropertiesFiltered = findDiscriminantProperties( - sourceProperties, - target - ); - if (!sourcePropertiesFiltered) return Ternary.False; - - // Though we could compute the number of combinations as we generate - // the matrix, this would incur additional memory overhead due to - // array allocations. To reduce this overhead, we first compute - // the number of combinations to ensure we will not surpass our - // fixed limit before incurring the cost of any allocations: - let numCombinations = 1; - for (const sourceProperty of sourcePropertiesFiltered) { - numCombinations *= countTypes(getTypeOfSymbol(sourceProperty)); - if (numCombinations > 25) { - // We've reached the complexity limit. - return Ternary.False; - } - } - - // Compute the set of types for each discriminant property. - const sourceDiscriminantTypes: Type[][] = - new Array(sourcePropertiesFiltered.length); - const excludedProperties = createUnderscoreEscapedMap(); - for (let i = 0; i < sourcePropertiesFiltered.length; i++) { - const sourceProperty = sourcePropertiesFiltered[i]; - const sourcePropertyType = getTypeOfSymbol(sourceProperty); - sourceDiscriminantTypes - [i] = sourcePropertyType.flags & TypeFlags.Union - ? (sourcePropertyType as UnionType).types - : [sourcePropertyType]; - excludedProperties.set(sourceProperty.escapedName, true); - } - - // Match each combination of the cartesian product of discriminant properties to one or more - // constituents of 'target'. If any combination does not have a match then 'source' is not relatable. - const discriminantCombinations = - cartesianProduct(sourceDiscriminantTypes); - const matchingTypes: Type[] = []; - for (const combination of discriminantCombinations) { - let hasMatch = false; - outer: - for (const type of target.types) { - for (let i = 0; i < sourcePropertiesFiltered.length; - i++) - { - const sourceProperty = sourcePropertiesFiltered[i]; - const targetProperty = getPropertyOfObjectType( - type, - sourceProperty.escapedName - ); - if (!targetProperty) continue outer; - if (sourceProperty === targetProperty) continue; - // We compare the source property to the target in the context of a single discriminant type. - const related = propertyRelatedTo( - source, - target, - sourceProperty, - targetProperty, - _ => combination[i], /*reportErrors*/ - false, /*isIntersectionConstituent*/ - false - ); - // If the target property could not be found, or if the properties were not related, - // then this constituent is not a match. - if (!related) { - continue outer; - } - } - pushIfUnique(matchingTypes, type, equateValues); - hasMatch = true; - } - if (!hasMatch) { - // We failed to match any type for this combination. - return Ternary.False; - } - } - - // Compare the remaining non-discriminant properties of each match. - let result = Ternary.True; - for (const type of matchingTypes) { - result &= propertiesRelatedTo( - source, - type, /*reportErrors*/ - false, - excludedProperties, /*isIntersectionConstituent*/ - false - ); - if (result) { - result &= signaturesRelatedTo( - source, - type, - SignatureKind.Call, /*reportStructuralErrors*/ - false - ); - if (result) { - result &= signaturesRelatedTo( - source, - type, - SignatureKind - .Construct, /*reportStructuralErrors*/ - false - ); - if (result) { - result &= indexTypesRelatedTo( - source, - type, - IndexKind.String, /*sourceIsPrimitive*/ - false, /*reportStructuralErrors*/ - false - ); - if (result) { - result &= indexTypesRelatedTo( - source, - type, - IndexKind.Number, /*sourceIsPrimitive*/ - false, /*reportStructuralErrors*/ - false - ); - } - } - } - } - if (!result) { - return result; - } - } - return result; - } - - function excludeProperties( - properties: Symbol[], - excludedProperties: UnderscoreEscapedMap | undefined - ) { - if (!excludedProperties - || properties.length === 0) - return properties; - let result: Symbol[] | undefined; - for (let i = 0; i < properties.length; i++) { - if (!excludedProperties.has(properties[i].escapedName)) { - if (result) { - result.push(properties[i]); - } - } else if (!result) { - result = properties.slice(0, i); - } - } - return result || properties; - } - - function isPropertySymbolTypeRelated( - sourceProp: Symbol, - targetProp: Symbol, - getTypeOfSourceProperty: (sym: Symbol) => Type, - reportErrors: boolean, - isIntersectionConstituent: boolean - ): Ternary { - const targetIsOptional = strictNullChecks - && !!(getCheckFlags(targetProp) & CheckFlags.Partial); - const source = getTypeOfSourceProperty(sourceProp); - if (getCheckFlags(targetProp) & CheckFlags.DeferredType - && !getSymbolLinks(targetProp).type) - { - // Rather than resolving (and normalizing) the type, relate constituent-by-constituent without performing normalization or seconadary passes - const links = getSymbolLinks(targetProp); - Debug.assertDefined(links.deferralParent); - Debug.assertDefined(links.deferralConstituents); - const unionParent = - !!(links.deferralParent!.flags & TypeFlags.Union); - let result = unionParent ? Ternary.False : Ternary.True; - const targetTypes = links.deferralConstituents!; - for (const targetType of targetTypes) { - const related = isRelatedTo( - source, - targetType, /*reportErrors*/ - false, /*headMessage*/ - undefined, /*isIntersectionConstituent*/ - !unionParent - ); - if (!unionParent) { - if (!related) { - // Can't assign to a target individually - have to fallback to assigning to the _whole_ intersection (which forces normalization) - return isRelatedTo( - source, - addOptionality( - getTypeOfSymbol(targetProp), - targetIsOptional - ), - reportErrors - ); - } - result &= related; - } else { - if (related) { - return related; - } - } - } - if (unionParent && !result && targetIsOptional) { - result = isRelatedTo(source, undefinedType); - } - if (unionParent && !result && reportErrors) { - // The easiest way to get the right errors here is to un-defer (which may be costly) - // If it turns out this is too costly too often, we can replicate the error handling logic within - // typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union - // type on which to hand discriminable properties, which we are expressly trying to avoid here) - return isRelatedTo( - source, - addOptionality( - getTypeOfSymbol(targetProp), - targetIsOptional - ), - reportErrors - ); - } - return result; - } else { - return isRelatedTo( - source, - addOptionality( - getTypeOfSymbol(targetProp), - targetIsOptional - ), - reportErrors, /*headMessage*/ - undefined, - isIntersectionConstituent - ); - } - } - - function propertyRelatedTo( - source: Type, - target: Type, - sourceProp: Symbol, - targetProp: Symbol, - getTypeOfSourceProperty: (sym: Symbol) => Type, - reportErrors: boolean, - isIntersectionConstituent: boolean - ): Ternary { - const sourcePropFlags = - getDeclarationModifierFlagsFromSymbol(sourceProp); - const targetPropFlags = - getDeclarationModifierFlagsFromSymbol(targetProp); - if (sourcePropFlags & ModifierFlags.Private - || targetPropFlags & ModifierFlags.Private) - { - const hasDifferingDeclarations = - sourceProp.valueDeclaration - !== targetProp.valueDeclaration; - if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate - && hasDifferingDeclarations) - { - if (reportErrors) { - reportError( - Diagnostics - .Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, - symbolToString(sourceProp), - typeToString(source) - ); - } - return Ternary.False; - } - if (hasDifferingDeclarations) { - if (reportErrors) { - if (sourcePropFlags & ModifierFlags.Private - && targetPropFlags & ModifierFlags.Private) - { - reportError( - Diagnostics - .Types_have_separate_declarations_of_a_private_property_0, - symbolToString(targetProp) - ); - } else { - reportError( - Diagnostics - .Property_0_is_private_in_type_1_but_not_in_type_2, - symbolToString(targetProp), - typeToString(sourcePropFlags - & ModifierFlags.Private - ? source - : target), - typeToString(sourcePropFlags - & ModifierFlags.Private - ? target - : source) - ); - } - } - return Ternary.False; - } - } else if (targetPropFlags & ModifierFlags.Protected) { - if (!isValidOverrideOf(sourceProp, targetProp)) { - if (reportErrors) { - reportError( - Diagnostics - .Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, - symbolToString(targetProp), - typeToString(getDeclaringClass(sourceProp) - || source), - typeToString(getDeclaringClass(targetProp) - || target) - ); - } - return Ternary.False; - } - } else if (sourcePropFlags & ModifierFlags.Protected) { - if (reportErrors) { - reportError( - Diagnostics - .Property_0_is_protected_in_type_1_but_public_in_type_2, - symbolToString(targetProp), - typeToString(source), - typeToString(target) - ); - } - return Ternary.False; - } - // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isPropertySymbolTypeRelated( - sourceProp, - targetProp, - getTypeOfSourceProperty, - reportErrors, - isIntersectionConstituent - ); - if (!related) { - if (reportErrors) { - reportIncompatibleError( - Diagnostics.Types_of_property_0_are_incompatible, - symbolToString(targetProp) - ); - } - return Ternary.False; - } - // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation - && sourceProp.flags & SymbolFlags.Optional - && !(targetProp.flags & SymbolFlags.Optional)) - { - // TypeScript 1.0 spec (April 2014): 3.8.3 - // S is a subtype of a type T, and T is a supertype of S if ... - // S' and T are object types and, for each member M in T.. - // M is a property and S' contains a property N where - // if M is a required property, N is also a required property - // (M - property in T) - // (N - property in S) - if (reportErrors) { - reportError( - Diagnostics - .Property_0_is_optional_in_type_1_but_required_in_type_2, - symbolToString(targetProp), - typeToString(source), - typeToString(target) - ); - } - return Ternary.False; - } - return related; - } - - function reportUnmatchedProperty( - source: Type, - target: Type, - unmatchedProperty: Symbol, - requireOptionalProperties: boolean - ) { - let shouldSkipElaboration = false; - // give specific error in case where private names have the same description - if ( - unmatchedProperty.valueDeclaration - && isNamedDeclaration(unmatchedProperty.valueDeclaration) - && isPrivateIdentifier(unmatchedProperty.valueDeclaration - .name) - && isClassDeclaration(source.symbol.valueDeclaration) - ) { - const privateIdentifierDescription = unmatchedProperty - .valueDeclaration.name.escapedText; - const symbolTableKey = getSymbolNameForPrivateIdentifier( - source.symbol, - privateIdentifierDescription - ); - if (symbolTableKey - && !!getPropertyOfType(source, symbolTableKey)) - { - const sourceName = source.symbol.valueDeclaration.name; - const targetName = - isClassDeclaration(target.symbol.valueDeclaration) - ? target.symbol.valueDeclaration.name - : undefined; - reportError( - Diagnostics - .Property_0_in_type_1_refers_to_a_different_member_that_cannot_be_accessed_from_within_type_2, - diagnosticName(privateIdentifierDescription), - diagnosticName(sourceName || anon), - diagnosticName(targetName || anon) - ); - return; - } - } - const props = - arrayFrom(getUnmatchedProperties( - source, - target, - requireOptionalProperties, /*matchDiscriminantProperties*/ - false - )); - if (!headMessage - || (headMessage.code - !== Diagnostics - .Class_0_incorrectly_implements_interface_1.code - && headMessage.code - !== Diagnostics - .Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass - .code)) - { - shouldSkipElaboration = true; // Retain top-level error for interface implementing issues, otherwise omit it - } - if (props.length === 1) { - const propName = symbolToString(unmatchedProperty); - reportError( - Diagnostics - .Property_0_is_missing_in_type_1_but_required_in_type_2, - propName, - ...getTypeNamesForErrorDisplay(source, target) - ); - if (length(unmatchedProperty.declarations)) { - associateRelatedInfo(createDiagnosticForNode( - unmatchedProperty.declarations[0], - Diagnostics._0_is_declared_here, - propName - )); - } - if (shouldSkipElaboration && errorInfo) { - overrideNextErrorInfo++; - } - } else if (tryElaborateArrayLikeErrors( - source, - target, /*reportErrors*/ - false - )) { - if (props.length - > 5) - { // arbitrary cutoff for too-long list form - reportError( - Diagnostics - .Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, - typeToString(source), - typeToString(target), - map(props.slice(0, 4), p => symbolToString(p)) - .join(', '), - props.length - 4 - ); - } else { - reportError( - Diagnostics - .Type_0_is_missing_the_following_properties_from_type_1_Colon_2, - typeToString(source), - typeToString(target), - map(props, p => symbolToString(p)).join(', ') - ); - } - if (shouldSkipElaboration && errorInfo) { - overrideNextErrorInfo++; - } - } - // No array like or unmatched property error - just issue top level error (errorInfo = undefined) - } - - function propertiesRelatedTo( - source: Type, - target: Type, - reportErrors: boolean, - excludedProperties: UnderscoreEscapedMap | undefined, - isIntersectionConstituent: boolean - ): Ternary { - if (relation === identityRelation) { - return propertiesIdenticalTo( - source, - target, - excludedProperties - ); - } - const requireOptionalProperties = - (relation === subtypeRelation - || relation === strictSubtypeRelation) - && !isObjectLiteralType(source) - && !isEmptyArrayLiteralType(source) - && !isTupleType(source); - const unmatchedProperty = getUnmatchedProperty( - source, - target, - requireOptionalProperties, /*matchDiscriminantProperties*/ - false - ); - if (unmatchedProperty) { - if (reportErrors) { - reportUnmatchedProperty( - source, - target, - unmatchedProperty, - requireOptionalProperties - ); - } - return Ternary.False; - } - if (isObjectLiteralType(target)) { - for (const sourceProp of excludeProperties( - getPropertiesOfType(source), - excludedProperties - )) { - if (!getPropertyOfObjectType( - target, - sourceProp.escapedName - )) { - const sourceType = getTypeOfSymbol(sourceProp); - if (!(sourceType === undefinedType - || sourceType === undefinedWideningType - || sourceType === optionalType)) - { - if (reportErrors) { - reportError( - Diagnostics - .Property_0_does_not_exist_on_type_1, - symbolToString(sourceProp), - typeToString(target) - ); - } - return Ternary.False; - } - } - } - } - let result = Ternary.True; - if (isTupleType(target)) { - const targetRestType = getRestTypeOfTupleType(target); - if (targetRestType) { - if (!isTupleType(source)) { - return Ternary.False; - } - const sourceRestType = getRestTypeOfTupleType(source); - if (sourceRestType - && !isRelatedTo( - sourceRestType, - targetRestType, - reportErrors - )) - { - if (reportErrors) { - reportError(Diagnostics - .Rest_signatures_are_incompatible); - } - return Ternary.False; - } - const targetCount = getTypeReferenceArity(target) - 1; - const sourceCount = getTypeReferenceArity(source) - - (sourceRestType ? 1 : 0); - const sourceTypeArguments = - getTypeArguments( source); - for (let i = targetCount; i < sourceCount; i++) { - const related = isRelatedTo( - sourceTypeArguments[i], - targetRestType, - reportErrors - ); - if (!related) { - if (reportErrors) { - reportError( - Diagnostics - .Property_0_is_incompatible_with_rest_element_type, - '' + i - ); - } - return Ternary.False; - } - result &= related; - } - } - } - // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ - // from the target union, across all members - const properties = getPropertiesOfType(target); - const numericNamesOnly = isTupleType(source) - && isTupleType(target); - for (const targetProp of excludeProperties( - properties, - excludedProperties - )) { - const name = targetProp.escapedName; - if (!(targetProp.flags & SymbolFlags.Prototype) - && (!numericNamesOnly || isNumericLiteralName(name) - || name === 'length')) - { - const sourceProp = getPropertyOfType(source, name); - if (sourceProp && sourceProp !== targetProp) { - const related = propertyRelatedTo( - source, - target, - sourceProp, - targetProp, - getTypeOfSymbol, - reportErrors, - isIntersectionConstituent - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - } - return result; - } - - function propertiesIdenticalTo( - source: Type, - target: Type, - excludedProperties: UnderscoreEscapedMap | undefined - ): Ternary { - if (!(source.flags & TypeFlags.Object - && target.flags & TypeFlags.Object)) - { - return Ternary.False; - } - const sourceProperties = excludeProperties( - getPropertiesOfObjectType(source), - excludedProperties - ); - const targetProperties = excludeProperties( - getPropertiesOfObjectType(target), - excludedProperties - ); - if (sourceProperties.length !== targetProperties.length) { - return Ternary.False; - } - let result = Ternary.True; - for (const sourceProp of sourceProperties) { - const targetProp = getPropertyOfObjectType( - target, - sourceProp.escapedName - ); - if (!targetProp) { - return Ternary.False; - } - const related = compareProperties( - sourceProp, - targetProp, - isRelatedTo - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function signaturesRelatedTo( - source: Type, - target: Type, - kind: SignatureKind, - reportErrors: boolean - ): Ternary { - if (relation === identityRelation) { - return signaturesIdenticalTo(source, target, kind); - } - if (target === anyFunctionType || source === anyFunctionType) { - return Ternary.True; - } - - const sourceIsJSConstructor = source.symbol - && isJSConstructor(source.symbol.valueDeclaration); - const targetIsJSConstructor = target.symbol - && isJSConstructor(target.symbol.valueDeclaration); - - const sourceSignatures = getSignaturesOfType( - source, - (sourceIsJSConstructor && kind === SignatureKind.Construct) - ? SignatureKind.Call - : kind - ); - const targetSignatures = getSignaturesOfType( - target, - (targetIsJSConstructor && kind === SignatureKind.Construct) - ? SignatureKind.Call - : kind - ); - - if (kind === SignatureKind.Construct && sourceSignatures.length - && targetSignatures.length) - { - if (isAbstractConstructorType(source) - && !isAbstractConstructorType(target)) - { - // An abstract constructor type is not assignable to a non-abstract constructor type - // as it would otherwise be possible to new an abstract class. Note that the assignability - // check we perform for an extends clause excludes construct signatures from the target, - // so this check never proceeds. - if (reportErrors) { - reportError(Diagnostics - .Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); - } - return Ternary.False; - } - if (!constructorVisibilitiesAreCompatible( - sourceSignatures[0], - targetSignatures[0], - reportErrors - )) { - return Ternary.False; - } - } - - let result = Ternary.True; - const saveErrorInfo = captureErrorCalculationState(); - const incompatibleReporter = kind === SignatureKind.Construct - ? reportIncompatibleConstructSignatureReturn - : reportIncompatibleCallSignatureReturn; - - if (getObjectFlags(source) & ObjectFlags.Instantiated - && getObjectFlags(target) & ObjectFlags.Instantiated - && source.symbol === target.symbol) - { - // We have instantiations of the same anonymous type (which typically will be the type of a - // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We erase type parameters - // as they are known to always be the same. - for (let i = 0; i < targetSignatures.length; i++) { - const related = signatureRelatedTo( - sourceSignatures[i], - targetSignatures[i], /*erase*/ - true, - reportErrors, - incompatibleReporter( - sourceSignatures[i], - targetSignatures[i] - ) - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - } else if (sourceSignatures.length === 1 - && targetSignatures.length === 1) - { - // For simple functions (functions with a single signature) we only erase type parameters for - // the comparable relation. Otherwise, if the source signature is generic, we instantiate it - // in the context of the target signature before checking the relationship. Ideally we'd do - // this regardless of the number of signatures, but the potential costs are prohibitive due - // to the quadratic nature of the logic below. - const eraseGenerics = relation === comparableRelation - || !!compilerOptions.noStrictGenericChecks; - result = signatureRelatedTo( - sourceSignatures[0], - targetSignatures[0], - eraseGenerics, - reportErrors, - incompatibleReporter( - sourceSignatures[0], - targetSignatures[0] - ) - ); - } else { - outer: - for (const t of targetSignatures) { - // Only elaborate errors from the first failure - let shouldElaborateErrors = reportErrors; - for (const s of sourceSignatures) { - const related = signatureRelatedTo( - s, - t, /*erase*/ - true, - shouldElaborateErrors, - incompatibleReporter(s, t) - ); - if (related) { - result &= related; - resetErrorInfo(saveErrorInfo); - continue outer; - } - shouldElaborateErrors = false; - } - - if (shouldElaborateErrors) { - reportError( - Diagnostics - .Type_0_provides_no_match_for_the_signature_1, - typeToString(source), - signatureToString( - t, /*enclosingDeclaration*/ - undefined, /*flags*/ - undefined, - kind - ) - ); - } - return Ternary.False; - } - } - return result; - } - - function reportIncompatibleCallSignatureReturn( - siga: Signature, - sigb: Signature - ) { - if (siga.parameters.length === 0 - && sigb.parameters.length === 0) - { - return ( - source: Type, - target: Type - ) => reportIncompatibleError( - Diagnostics - .Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, - typeToString(source), - typeToString(target) - ); - } - return (source: Type, target: Type) => reportIncompatibleError( - Diagnostics - .Call_signature_return_types_0_and_1_are_incompatible, - typeToString(source), - typeToString(target) - ); - } - - function reportIncompatibleConstructSignatureReturn( - siga: Signature, - sigb: Signature - ) { - if (siga.parameters.length === 0 - && sigb.parameters.length === 0) - { - return ( - source: Type, - target: Type - ) => reportIncompatibleError( - Diagnostics - .Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, - typeToString(source), - typeToString(target) - ); - } - return (source: Type, target: Type) => reportIncompatibleError( - Diagnostics - .Construct_signature_return_types_0_and_1_are_incompatible, - typeToString(source), - typeToString(target) - ); - } - - /** - * See signatureAssignableTo, compareSignaturesIdentical - */ - function signatureRelatedTo( - source: Signature, - target: Signature, - erase: boolean, - reportErrors: boolean, - incompatibleReporter: (source: Type, target: Type) => void - ): Ternary { - return compareSignaturesRelated( - erase ? getErasedSignature(source) : source, - erase ? getErasedSignature(target) : target, - relation === strictSubtypeRelation - ? SignatureCheckMode.StrictArity - : 0, - reportErrors, - reportError, - incompatibleReporter, - isRelatedTo, - reportUnreliableMarkers - ); - } - - function signaturesIdenticalTo( - source: Type, - target: Type, - kind: SignatureKind - ): Ternary { - const sourceSignatures = getSignaturesOfType(source, kind); - const targetSignatures = getSignaturesOfType(target, kind); - if (sourceSignatures.length !== targetSignatures.length) { - return Ternary.False; - } - let result = Ternary.True; - for (let i = 0; i < sourceSignatures.length; i++) { - const related = compareSignaturesIdentical( - sourceSignatures[i], - targetSignatures[i], /*partialMatch*/ - false, /*ignoreThisTypes*/ - false, /*ignoreReturnTypes*/ - false, - isRelatedTo - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function eachPropertyRelatedTo( - source: Type, - target: Type, - kind: IndexKind, - reportErrors: boolean - ): Ternary { - let result = Ternary.True; - for (const prop of getPropertiesOfObjectType(source)) { - if (isIgnoredJsxProperty(source, prop)) { - continue; - } - // Skip over symbol-named members - if (prop.nameType - && prop.nameType.flags & TypeFlags.UniqueESSymbol) - { - continue; - } - if (kind === IndexKind.String - || isNumericLiteralName(prop.escapedName)) - { - const related = isRelatedTo( - getTypeOfSymbol(prop), - target, - reportErrors - ); - if (!related) { - if (reportErrors) { - reportError( - Diagnostics - .Property_0_is_incompatible_with_index_signature, - symbolToString(prop) - ); - } - return Ternary.False; - } - result &= related; - } - } - return result; - } - - function indexInfoRelatedTo( - sourceInfo: IndexInfo, - targetInfo: IndexInfo, - reportErrors: boolean - ) { - const related = isRelatedTo( - sourceInfo.type, - targetInfo.type, - reportErrors - ); - if (!related && reportErrors) { - reportError(Diagnostics.Index_signatures_are_incompatible); - } - return related; - } - - function indexTypesRelatedTo( - source: Type, - target: Type, - kind: IndexKind, - sourceIsPrimitive: boolean, - reportErrors: boolean - ): Ternary { - if (relation === identityRelation) { - return indexTypesIdenticalTo(source, target, kind); - } - const targetInfo = getIndexInfoOfType(target, kind); - if (!targetInfo || targetInfo.type.flags & TypeFlags.Any - && !sourceIsPrimitive) - { - // Index signature of type any permits assignment from everything but primitives - return Ternary.True; - } - const sourceInfo = getIndexInfoOfType(source, kind) - || kind === IndexKind.Number - && getIndexInfoOfType(source, IndexKind.String); - if (sourceInfo) { - return indexInfoRelatedTo( - sourceInfo, - targetInfo, - reportErrors - ); - } - if (isGenericMappedType(source)) { - // A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U } - // if T is related to U. - return kind === IndexKind.String - ? isRelatedTo( - getTemplateTypeFromMappedType(source), - targetInfo.type, - reportErrors - ) - : Ternary.False; - } - if (isObjectTypeWithInferableIndex(source)) { - let related = Ternary.True; - if (kind === IndexKind.String) { - const sourceNumberInfo = getIndexInfoOfType( - source, - IndexKind.Number - ); - if (sourceNumberInfo) { - related = indexInfoRelatedTo( - sourceNumberInfo, - targetInfo, - reportErrors - ); - } - } - if (related) { - related &= eachPropertyRelatedTo( - source, - targetInfo.type, - kind, - reportErrors - ); - } - return related; - } - if (reportErrors) { - reportError( - Diagnostics.Index_signature_is_missing_in_type_0, - typeToString(source) - ); - } - return Ternary.False; - } - - function indexTypesIdenticalTo( - source: Type, - target: Type, - indexKind: IndexKind - ): Ternary { - const targetInfo = getIndexInfoOfType(target, indexKind); - const sourceInfo = getIndexInfoOfType(source, indexKind); - if (!sourceInfo && !targetInfo) { - return Ternary.True; - } - if (sourceInfo && targetInfo - && sourceInfo.isReadonly === targetInfo.isReadonly) - { - return isRelatedTo(sourceInfo.type, targetInfo.type); - } - return Ternary.False; - } - - function constructorVisibilitiesAreCompatible( - sourceSignature: Signature, - targetSignature: Signature, - reportErrors: boolean - ) { - if (!sourceSignature.declaration - || !targetSignature.declaration) - { - return true; - } - - const sourceAccessibility = getSelectedModifierFlags( - sourceSignature.declaration, - ModifierFlags.NonPublicAccessibilityModifier - ); - const targetAccessibility = getSelectedModifierFlags( - targetSignature.declaration, - ModifierFlags.NonPublicAccessibilityModifier - ); - - // A public, protected and private signature is assignable to a private signature. - if (targetAccessibility === ModifierFlags.Private) { - return true; - } - - // A public and protected signature is assignable to a protected signature. - if (targetAccessibility === ModifierFlags.Protected - && sourceAccessibility !== ModifierFlags.Private) - { - return true; - } - - // Only a public signature is assignable to public signature. - if (targetAccessibility !== ModifierFlags.Protected - && !sourceAccessibility) - { - return true; - } - - if (reportErrors) { - reportError( - Diagnostics - .Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, - visibilityToString(sourceAccessibility), - visibilityToString(targetAccessibility) - ); - } - - return false; - } - } - - function getBestMatchingType( - source: Type, - target: UnionOrIntersectionType, - isRelatedTo = compareTypesAssignable - ) { - return findMatchingDiscriminantType(source, target, isRelatedTo) - || findMatchingTypeReferenceOrTypeAliasReference( - source, - target - ) - || findBestTypeForObjectLiteral(source, target) - || findBestTypeForInvokable(source, target) - || findMostOverlappyType(source, target); - } - - function discriminateTypeByDiscriminableItems( - target: UnionType, - discriminators: [() => Type, __String][], - related: (source: Type, target: Type) => boolean | Ternary - ): Type | undefined; - function discriminateTypeByDiscriminableItems( - target: UnionType, - discriminators: [() => Type, __String][], - related: (source: Type, target: Type) => boolean | Ternary, - defaultValue: Type - ): Type; - function discriminateTypeByDiscriminableItems( - target: UnionType, - discriminators: [() => Type, __String][], - related: (source: Type, target: Type) => boolean | Ternary, - defaultValue?: Type - ) { - // undefined=unknown, true=discriminated, false=not discriminated - // The state of each type progresses from left to right. Discriminated types stop at 'true'. - const discriminable = target.types - .map(_ => undefined) as (boolean | undefined)[]; - for (const [getDiscriminatingType, propertyName] - of discriminators) - { - let i = 0; - for (const type of target.types) { - const targetType = getTypeOfPropertyOfType( - type, - propertyName - ); - if (targetType - && related(getDiscriminatingType(), targetType)) - { - discriminable[i] = discriminable[i] === undefined - ? true - : discriminable[i]; - } else { - discriminable[i] = false; - } - i++; - } - } - const match = discriminable.indexOf(/*searchElement*/ true); - // make sure exactly 1 matches before returning it - return match === -1 - || discriminable.indexOf(/*searchElement*/ true, match + 1) - !== -1 - ? defaultValue - : target.types[match]; - } - - /** - * A type is 'weak' if it is an object type with at least one optional property - * and no required properties, call/construct signatures or index signatures - */ - function isWeakType(type: Type): boolean { - if (type.flags & TypeFlags.Object) { - const resolved = - resolveStructuredTypeMembers( type); - return resolved.callSignatures.length === 0 - && resolved.constructSignatures.length === 0 - && !resolved.stringIndexInfo && !resolved.numberIndexInfo - && resolved.properties.length > 0 - && every( - resolved.properties, - p => !!(p.flags & SymbolFlags.Optional) - ); - } - if (type.flags & TypeFlags.Intersection) { - return every(( type).types, isWeakType); - } - return false; - } - - function hasCommonProperties( - source: Type, - target: Type, - isComparingJsxAttributes: boolean - ) { - for (const prop of getPropertiesOfType(source)) { - if (isKnownProperty( - target, - prop.escapedName, - isComparingJsxAttributes - )) { - return true; - } - } - return false; - } - - // Return a type reference where the source type parameter is replaced with the target marker - // type, and flag the result as a marker type reference. - function getMarkerTypeReference( - type: GenericType, - source: TypeParameter, - target: Type - ) { - const result = createTypeReference( - type, - map(type.typeParameters, t => t === source ? target : t) - ); - result.objectFlags |= ObjectFlags.MarkerType; - return result; - } - - function getAliasVariances(symbol: Symbol) { - const links = getSymbolLinks(symbol); - return getVariancesWorker( - links.typeParameters, - links, - (_links, param, marker) => { - const type = getTypeAliasInstantiation( - symbol, - instantiateTypes( - links.typeParameters!, - makeUnaryTypeMapper(param, marker) - ) - ); - type.aliasTypeArgumentsContainsMarker = true; - return type; - } - ); - } - - // Return an array containing the variance of each type parameter. The variance is effectively - // a digest of the type comparisons that occur for each type argument when instantiations of the - // generic type are structurally compared. We infer the variance information by comparing - // instantiations of the generic type for type arguments with known relations. The function - // returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function - // has been invoked recursively for the given generic type. - function getVariancesWorker( - typeParameters: readonly TypeParameter[] = emptyArray, - cache: TCache, - createMarkerType: ( - input: TCache, - param: TypeParameter, - marker: Type - ) => Type - ): VarianceFlags[] { - let variances = cache.variances; - if (!variances) { - // The emptyArray singleton is used to signal a recursive invocation. - cache.variances = emptyArray; - variances = []; - for (const tp of typeParameters) { - let unmeasurable = false; - let unreliable = false; - const oldHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = (onlyUnreliable) => onlyUnreliable - ? unreliable = true - : unmeasurable = true; - // We first compare instantiations where the type parameter is replaced with - // marker types that have a known subtype relationship. From this we can infer - // invariance, covariance, contravariance or bivariance. - const typeWithSuper = createMarkerType( - cache, - tp, - markerSuperType - ); - const typeWithSub = createMarkerType( - cache, - tp, - markerSubType - ); - let variance = - (isTypeAssignableTo(typeWithSub, typeWithSuper) - ? VarianceFlags.Covariant - : 0) - | (isTypeAssignableTo(typeWithSuper, typeWithSub) - ? VarianceFlags.Contravariant - : 0); - // If the instantiations appear to be related bivariantly it may be because the - // type parameter is independent (i.e. it isn't witnessed anywhere in the generic - // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. - if (variance === VarianceFlags.Bivariant - && isTypeAssignableTo( - createMarkerType(cache, tp, markerOtherType), - typeWithSuper - )) - { - variance = VarianceFlags.Independent; - } - outofbandVarianceMarkerHandler = oldHandler; - if (unmeasurable || unreliable) { - if (unmeasurable) { - variance |= VarianceFlags.Unmeasurable; - } - if (unreliable) { - variance |= VarianceFlags.Unreliable; - } - } - variances.push(variance); - } - cache.variances = variances; - } - return variances; - } - - function getVariances(type: GenericType): VarianceFlags[] { - // Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters) - if (type === globalArrayType || type === globalReadonlyArrayType - || type.objectFlags & ObjectFlags.Tuple) - { - return emptyArray; - } - return getVariancesWorker( - type.typeParameters, - type, - getMarkerTypeReference - ); - } - - // Return true if the given type reference has a 'void' type argument for a covariant type parameter. - // See comment at call in recursiveTypeRelatedTo for when this case matters. - function hasCovariantVoidArgument( - typeArguments: readonly Type[], - variances: VarianceFlags[] - ): boolean { - for (let i = 0; i < variances.length; i++) { - if ((variances[i] & VarianceFlags.VarianceMask) - === VarianceFlags.Covariant - && typeArguments[i].flags & TypeFlags.Void) - { - return true; - } - } - return false; - } - - function isUnconstrainedTypeParameter(type: Type) { - return type.flags & TypeFlags.TypeParameter - && !getConstraintOfTypeParameter( type); - } - - function isNonDeferredTypeReference(type: - Type): type is TypeReference - { - return !!(getObjectFlags(type) & ObjectFlags.Reference) - && !( type).node; - } - - function isTypeReferenceWithGenericArguments(type: Type): boolean { - return isNonDeferredTypeReference(type) - && some( - getTypeArguments(type), - t => isUnconstrainedTypeParameter(t) - || isTypeReferenceWithGenericArguments(t) - ); - } - - /** - * getTypeReferenceId(A) returns "111=0-12=1" - * where A.id=111 and number.id=12 - */ - function getTypeReferenceId( - type: TypeReference, - typeParameters: Type[], - depth = 0 - ) { - let result = '' + type.target.id; - for (const t of getTypeArguments(type)) { - if (isUnconstrainedTypeParameter(t)) { - let index = typeParameters.indexOf(t); - if (index < 0) { - index = typeParameters.length; - typeParameters.push(t); - } - result += '=' + index; - } else if (depth < 4 - && isTypeReferenceWithGenericArguments(t)) - { - result += '<' - + getTypeReferenceId( - t as TypeReference, - typeParameters, - depth + 1 - ) + '>'; - } else { - result += '-' + t.id; - } - } - return result; - } - - /** - * To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters. - * For other cases, the types ids are used. - */ - function getRelationKey( - source: Type, - target: Type, - isIntersectionConstituent: boolean, - relation: Map - ) { - if (relation === identityRelation && source.id > target.id) { - const temp = source; - source = target; - target = temp; - } - const delimiter = isIntersectionConstituent ? ';' : ','; - if (isTypeReferenceWithGenericArguments(source) - && isTypeReferenceWithGenericArguments(target)) - { - const typeParameters: Type[] = []; - return getTypeReferenceId( - source, - typeParameters - ) + delimiter - + getTypeReferenceId( - target, - typeParameters - ); - } - return source.id + delimiter + target.id; - } - - // Invoke the callback for each underlying property symbol of the given symbol and return the first - // value that isn't undefined. - function forEachProperty( - prop: Symbol, - callback: (p: Symbol) => T - ): T | undefined { - if (getCheckFlags(prop) & CheckFlags.Synthetic) { - for (const t of ( prop).containingType! - .types) - { - const p = getPropertyOfType(t, prop.escapedName); - const result = p && forEachProperty(p, callback); - if (result) { - return result; - } - } - return undefined; - } - return callback(prop); - } - - // Return the declaring class type of a property or undefined if property not declared in class - function getDeclaringClass(prop: Symbol) { - return prop.parent && prop.parent.flags & SymbolFlags.Class - ? getDeclaredTypeOfSymbol(getParentOfSymbol(prop)!) - : undefined; - } - - // Return true if some underlying source property is declared in a class that derives - // from the given base class. - function isPropertyInClassDerivedFrom( - prop: Symbol, - baseClass: Type | undefined - ) { - return forEachProperty(prop, sp => { - const sourceClass = getDeclaringClass(sp); - return sourceClass - ? hasBaseType(sourceClass, baseClass) - : false; - }); - } - - // Return true if source property is a valid override of protected parts of target property. - function isValidOverrideOf(sourceProp: Symbol, targetProp: Symbol) { - return !forEachProperty( - targetProp, - tp => getDeclarationModifierFlagsFromSymbol(tp) - & ModifierFlags.Protected - ? !isPropertyInClassDerivedFrom( - sourceProp, - getDeclaringClass(tp) - ) - : false - ); - } - - // Return true if the given class derives from each of the declaring classes of the protected - // constituents of the given property. - function isClassDerivedFromDeclaringClasses( - checkClass: Type, - prop: Symbol - ) { - return forEachProperty( - prop, - p => getDeclarationModifierFlagsFromSymbol(p) - & ModifierFlags.Protected - ? !hasBaseType(checkClass, getDeclaringClass(p)) - : false - ) - ? undefined - : checkClass; - } - - // Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons - // for 5 or more occurrences or instantiations of the type have been recorded on the given stack. It is possible, - // though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely - // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5 - // levels, but unequal at some level beyond that. - // In addition, this will also detect when an indexed access has been chained off of 5 or more times (which is essentially - // the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding false positives - // for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`). - function isDeeplyNestedType( - type: Type, - stack: Type[], - depth: number - ): boolean { - // We track all object types that have an associated symbol (representing the origin of the type) - if (depth >= 5 && type.flags & TypeFlags.Object - && !isObjectOrArrayLiteralType(type)) - { - const symbol = type.symbol; - if (symbol) { - let count = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (t.flags & TypeFlags.Object - && t.symbol === symbol) - { - count++; - if (count >= 5) return true; - } - } - } - } - if (depth >= 5 && type.flags & TypeFlags.IndexedAccess) { - const root = getRootObjectTypeFromIndexedAccessChain(type); - let count = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (getRootObjectTypeFromIndexedAccessChain(t) === root) { - count++; - if (count >= 5) return true; - } - } - } - return false; - } - - /** - * Gets the leftmost object type in a chain of indexed accesses, eg, in A[P][Q], returns A - */ - function getRootObjectTypeFromIndexedAccessChain(type: Type) { - let t = type; - while (t.flags & TypeFlags.IndexedAccess) { - t = (t as IndexedAccessType).objectType; - } - return t; - } - - function isPropertyIdenticalTo( - sourceProp: Symbol, - targetProp: Symbol - ): boolean { - return compareProperties( - sourceProp, - targetProp, - compareTypesIdentical - ) !== Ternary.False; - } - - function compareProperties( - sourceProp: Symbol, - targetProp: Symbol, - compareTypes: (source: Type, target: Type) => Ternary - ): Ternary { - // Two members are considered identical when - // - they are public properties with identical names, optionality, and types, - // - they are private or protected properties originating in the same declaration and having identical types - if (sourceProp === targetProp) { - return Ternary.True; - } - const sourcePropAccessibility = - getDeclarationModifierFlagsFromSymbol(sourceProp) - & ModifierFlags.NonPublicAccessibilityModifier; - const targetPropAccessibility = - getDeclarationModifierFlagsFromSymbol(targetProp) - & ModifierFlags.NonPublicAccessibilityModifier; - if (sourcePropAccessibility !== targetPropAccessibility) { - return Ternary.False; - } - if (sourcePropAccessibility) { - if (getTargetSymbol(sourceProp) - !== getTargetSymbol(targetProp)) - { - return Ternary.False; - } - } else { - if ((sourceProp.flags & SymbolFlags.Optional) - !== (targetProp.flags & SymbolFlags.Optional)) - { - return Ternary.False; - } - } - if (isReadonlySymbol(sourceProp) - !== isReadonlySymbol(targetProp)) - { - return Ternary.False; - } - return compareTypes( - getTypeOfSymbol(sourceProp), - getTypeOfSymbol(targetProp) - ); - } - - function isMatchingSignature( - source: Signature, - target: Signature, - partialMatch: boolean - ) { - const sourceParameterCount = getParameterCount(source); - const targetParameterCount = getParameterCount(target); - const sourceMinArgumentCount = getMinArgumentCount(source); - const targetMinArgumentCount = getMinArgumentCount(target); - const sourceHasRestParameter = hasEffectiveRestParameter(source); - const targetHasRestParameter = hasEffectiveRestParameter(target); - // A source signature matches a target signature if the two signatures have the same number of required, - // optional, and rest parameters. - if (sourceParameterCount === targetParameterCount - && sourceMinArgumentCount === targetMinArgumentCount - && sourceHasRestParameter === targetHasRestParameter) - { - return true; - } - // A source signature partially matches a target signature if the target signature has no fewer required - // parameters - if (partialMatch - && sourceMinArgumentCount <= targetMinArgumentCount) - { - return true; - } - return false; - } - - /** - * See signatureRelatedTo, compareSignaturesIdentical - */ - function compareSignaturesIdentical( - source: Signature, - target: Signature, - partialMatch: boolean, - ignoreThisTypes: boolean, - ignoreReturnTypes: boolean, - compareTypes: (s: Type, t: Type) => Ternary - ): Ternary { - // TODO (drosen): De-duplicate code between related functions. - if (source === target) { - return Ternary.True; - } - if (!(isMatchingSignature(source, target, partialMatch))) { - return Ternary.False; - } - // Check that the two signatures have the same number of type parameters. - if (length(source.typeParameters) - !== length(target.typeParameters)) - { - return Ternary.False; - } - // Check that type parameter constraints and defaults match. If they do, instantiate the source - // signature with the type parameters of the target signature and continue the comparison. - if (target.typeParameters) { - const mapper = createTypeMapper( - source.typeParameters!, - target.typeParameters - ); - for (let i = 0; i < target.typeParameters.length; i++) { - const s = source.typeParameters![i]; - const t = target.typeParameters[i]; - if (!(s === t - || compareTypes( - instantiateType( - getConstraintFromTypeParameter(s), - mapper - ) || unknownType, - getConstraintFromTypeParameter(t) || unknownType - ) - && compareTypes( - instantiateType( - getDefaultFromTypeParameter(s), - mapper - ) || unknownType, - getDefaultFromTypeParameter(t) || unknownType - ))) - { - return Ternary.False; - } - } - source = instantiateSignature( - source, - mapper, /*eraseTypeParameters*/ - true - ); - } - let result = Ternary.True; - if (!ignoreThisTypes) { - const sourceThisType = getThisTypeOfSignature(source); - if (sourceThisType) { - const targetThisType = getThisTypeOfSignature(target); - if (targetThisType) { - const related = compareTypes( - sourceThisType, - targetThisType - ); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - } - const targetLen = getParameterCount(target); - for (let i = 0; i < targetLen; i++) { - const s = getTypeAtPosition(source, i); - const t = getTypeAtPosition(target, i); - const related = compareTypes(t, s); - if (!related) { - return Ternary.False; - } - result &= related; - } - if (!ignoreReturnTypes) { - const sourceTypePredicate = - getTypePredicateOfSignature(source); - const targetTypePredicate = - getTypePredicateOfSignature(target); - result &= sourceTypePredicate || targetTypePredicate - ? compareTypePredicatesIdentical( - sourceTypePredicate, - targetTypePredicate, - compareTypes - ) - : compareTypes( - getReturnTypeOfSignature(source), - getReturnTypeOfSignature(target) - ); - } - return result; - } - - function compareTypePredicatesIdentical( - source: TypePredicate | undefined, - target: TypePredicate | undefined, - compareTypes: (s: Type, t: Type) => Ternary - ): Ternary { - return !(source && target - && typePredicateKindsMatch(source, target)) - ? Ternary.False - : source.type === target.type - ? Ternary.True - : source.type && target.type - ? compareTypes(source.type, target.type) - : Ternary.False; - } - - function literalTypesWithSameBaseType(types: Type[]): boolean { - let commonBaseType: Type | undefined; - for (const t of types) { - const baseType = getBaseTypeOfLiteralType(t); - if (!commonBaseType) { - commonBaseType = baseType; - } - if (baseType === t || baseType !== commonBaseType) { - return false; - } - } - return true; - } - - // When the candidate types are all literal types with the same base type, return a union - // of those literal types. Otherwise, return the leftmost type for which no type to the - // right is a supertype. - function getSupertypeOrUnion(types: Type[]): Type { - return literalTypesWithSameBaseType(types) - ? getUnionType(types) - : reduceLeft(types, (s, t) => isTypeSubtypeOf(s, t) ? t : s)!; - } - - function getCommonSupertype(types: Type[]): Type { - if (!strictNullChecks) { - return getSupertypeOrUnion(types); - } - const primaryTypes = filter( - types, - t => !(t.flags & TypeFlags.Nullable) - ); - return primaryTypes.length - ? getNullableType( - getSupertypeOrUnion(primaryTypes), - getFalsyFlagsOfTypes(types) & TypeFlags.Nullable - ) - : getUnionType(types, UnionReduction.Subtype); - } - - // Return the leftmost type for which no type to the right is a subtype. - function getCommonSubtype(types: Type[]) { - return reduceLeft(types, (s, t) => isTypeSubtypeOf(t, s) ? t : s)!; - } - - function isArrayType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Reference) - && (( type).target === globalArrayType - || ( type).target - === globalReadonlyArrayType); - } - - function isReadonlyArrayType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Reference) - && ( type).target === globalReadonlyArrayType; - } - - function isMutableArrayOrTuple(type: Type): boolean { - return isArrayType(type) && !isReadonlyArrayType(type) - || isTupleType(type) && !type.target.readonly; - } - - function getElementTypeOfArrayType(type: Type): Type | undefined { - return isArrayType(type) - ? getTypeArguments(type as TypeReference)[0] - : undefined; - } - - function isArrayLikeType(type: Type): boolean { - // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, - // or if it is not the undefined or null type and if it is assignable to ReadonlyArray - return isArrayType(type) || !(type.flags & TypeFlags.Nullable) - && isTypeAssignableTo(type, anyReadonlyArrayType); - } - - function isEmptyArrayLiteralType(type: Type): boolean { - const elementType = isArrayType(type) - ? getTypeArguments( type)[0] - : undefined; - return elementType === undefinedWideningType - || elementType === implicitNeverType; - } - - function isTupleLikeType(type: Type): boolean { - return isTupleType(type) - || !!getPropertyOfType(type, '0' as __String); - } - - function isArrayOrTupleLikeType(type: Type): boolean { - return isArrayLikeType(type) || isTupleLikeType(type); - } - - function getTupleElementType(type: Type, index: number) { - const propType = getTypeOfPropertyOfType( - type, - '' + index as __String - ); - if (propType) { - return propType; - } - if (everyType(type, isTupleType)) { - return mapType( - type, - t => getRestTypeOfTupleType( t) - || undefinedType - ); - } - return undefined; - } - - function isNeitherUnitTypeNorNever(type: Type): boolean { - return !(type.flags & (TypeFlags.Unit | TypeFlags.Never)); - } - - function isUnitType(type: Type): boolean { - return !!(type.flags & TypeFlags.Unit); - } - - function isLiteralType(type: Type): boolean { - return type.flags & TypeFlags.Boolean - ? true - : type.flags & TypeFlags.Union - ? type.flags & TypeFlags.EnumLiteral - ? true - : every(( type).types, isUnitType) - : isUnitType(type); - } - - function getBaseTypeOfLiteralType(type: Type): Type { - return type.flags & TypeFlags.EnumLiteral - ? getBaseTypeOfEnumLiteralType( type) - : type.flags & TypeFlags.StringLiteral - ? stringType - : type.flags & TypeFlags.NumberLiteral - ? numberType - : type.flags & TypeFlags.BigIntLiteral - ? bigintType - : type.flags & TypeFlags.BooleanLiteral - ? booleanType - : type.flags & TypeFlags.Union - ? getUnionType(sameMap( - ( type).types, - getBaseTypeOfLiteralType - )) - : type; - } - - function getWidenedLiteralType(type: Type): Type { - return type.flags & TypeFlags.EnumLiteral - && isFreshLiteralType(type) - ? getBaseTypeOfEnumLiteralType( type) - : type.flags & TypeFlags.StringLiteral - && isFreshLiteralType(type) - ? stringType - : type.flags & TypeFlags.NumberLiteral - && isFreshLiteralType(type) - ? numberType - : type.flags & TypeFlags.BigIntLiteral - && isFreshLiteralType(type) - ? bigintType - : type.flags & TypeFlags.BooleanLiteral - && isFreshLiteralType(type) - ? booleanType - : type.flags & TypeFlags.Union - ? getUnionType(sameMap( - ( type).types, - getWidenedLiteralType - )) - : type; - } - - function getWidenedUniqueESSymbolType(type: Type): Type { - return type.flags & TypeFlags.UniqueESSymbol - ? esSymbolType - : type.flags & TypeFlags.Union - ? getUnionType(sameMap( - ( type).types, - getWidenedUniqueESSymbolType - )) - : type; - } - - function getWidenedLiteralLikeTypeForContextualType( - type: Type, - contextualType: Type | undefined - ) { - if (!isLiteralOfContextualType(type, contextualType)) { - type = getWidenedUniqueESSymbolType(getWidenedLiteralType(type)); - } - return type; - } - - function getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded( - type: Type | undefined, - contextualSignatureReturnType: Type | undefined, - isAsync: boolean - ) { - if (type && isUnitType(type)) { - const contextualType = !contextualSignatureReturnType - ? undefined - : isAsync - ? getPromisedTypeOfPromise(contextualSignatureReturnType) - : contextualSignatureReturnType; - type = getWidenedLiteralLikeTypeForContextualType( - type, - contextualType - ); - } - return type; - } - - function getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded( - type: Type | undefined, - contextualSignatureReturnType: Type | undefined, - kind: IterationTypeKind, - isAsyncGenerator: boolean - ) { - if (type && isUnitType(type)) { - const contextualType = !contextualSignatureReturnType - ? undefined - : getIterationTypeOfGeneratorFunctionReturnType( - kind, - contextualSignatureReturnType, - isAsyncGenerator - ); - type = getWidenedLiteralLikeTypeForContextualType( - type, - contextualType - ); - } - return type; - } - - /** - * Check if a Type was written as a tuple type literal. - * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. - */ - function isTupleType(type: Type): type is TupleTypeReference { - return !!(getObjectFlags(type) & ObjectFlags.Reference - && ( type).target.objectFlags - & ObjectFlags.Tuple); - } - - function getRestTypeOfTupleType(type: TupleTypeReference) { - return type.target.hasRestElement - ? getTypeArguments(type)[type.target.typeParameters!.length - - 1] - : undefined; - } - - function getRestArrayTypeOfTupleType(type: TupleTypeReference) { - const restType = getRestTypeOfTupleType(type); - return restType && createArrayType(restType); - } - - function getLengthOfTupleType(type: TupleTypeReference) { - return getTypeReferenceArity(type) - - (type.target.hasRestElement ? 1 : 0); - } - - function isZeroBigInt({ value }: BigIntLiteralType) { - return value.base10Value === '0'; - } - - function getFalsyFlagsOfTypes(types: Type[]): TypeFlags { - let result: TypeFlags = 0; - for (const t of types) { - result |= getFalsyFlags(t); - } - return result; - } - - // Returns the String, Number, Boolean, StringLiteral, NumberLiteral, BooleanLiteral, Void, Undefined, or Null - // flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns - // no flags for all other types (including non-falsy literal types). - function getFalsyFlags(type: Type): TypeFlags { - return type.flags & TypeFlags.Union - ? getFalsyFlagsOfTypes(( type).types) - : type.flags & TypeFlags.StringLiteral - ? ( type).value === '' - ? TypeFlags.StringLiteral - : 0 - : type.flags & TypeFlags.NumberLiteral - ? ( type).value === 0 - ? TypeFlags.NumberLiteral - : 0 - : type.flags & TypeFlags.BigIntLiteral - ? isZeroBigInt( type) - ? TypeFlags.BigIntLiteral - : 0 - : type.flags & TypeFlags.BooleanLiteral - ? (type === falseType - || type === regularFalseType) - ? TypeFlags.BooleanLiteral - : 0 - : type.flags & TypeFlags.PossiblyFalsy; - } - - function removeDefinitelyFalsyTypes(type: Type): Type { - return getFalsyFlags(type) & TypeFlags.DefinitelyFalsy - ? filterType( - type, - t => !(getFalsyFlags(t) & TypeFlags.DefinitelyFalsy) - ) - : type; - } - - function extractDefinitelyFalsyTypes(type: Type): Type { - return mapType(type, getDefinitelyFalsyPartOfType); - } - - function getDefinitelyFalsyPartOfType(type: Type): Type { - return type.flags & TypeFlags.String - ? emptyStringType - : type.flags & TypeFlags.Number - ? zeroType - : type.flags & TypeFlags.BigInt - ? zeroBigIntType - : type === regularFalseType - || type === falseType - || type.flags - & (TypeFlags.Void | TypeFlags.Undefined - | TypeFlags.Null) - || type.flags & TypeFlags.StringLiteral - && ( type).value === '' - || type.flags & TypeFlags.NumberLiteral - && ( type).value === 0 - || type.flags & TypeFlags.BigIntLiteral - && isZeroBigInt( type) - ? type - : neverType; - } - - /** - * Add undefined or null or both to a type if they are missing. - * @param type - type to add undefined and/or null to if not present - * @param flags - Either TypeFlags.Undefined or TypeFlags.Null, or both - */ - function getNullableType(type: Type, flags: TypeFlags): Type { - const missing = - (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null); - return missing === 0 - ? type - : missing === TypeFlags.Undefined - ? getUnionType([type, undefinedType]) - : missing === TypeFlags.Null - ? getUnionType([type, nullType]) - : getUnionType([type, undefinedType, nullType]); - } - - function getOptionalType(type: Type): Type { - Debug.assert(strictNullChecks); - return type.flags & TypeFlags.Undefined - ? type - : getUnionType([type, undefinedType]); - } - - function getGlobalNonNullableTypeInstantiation(type: Type) { - if (!deferredGlobalNonNullableTypeAlias) { - deferredGlobalNonNullableTypeAlias = getGlobalSymbol( - 'NonNullable' as __String, - SymbolFlags.TypeAlias, /*diagnostic*/ - undefined - ) || unknownSymbol; - } - // Use NonNullable global type alias if available to improve quick info/declaration emit - if (deferredGlobalNonNullableTypeAlias !== unknownSymbol) { - return getTypeAliasInstantiation( - deferredGlobalNonNullableTypeAlias, - [type] - ); - } - return getTypeWithFacts( - type, - TypeFacts.NEUndefinedOrNull - ); // Type alias unavailable, fall back to non-higher-order behavior - } - - function getNonNullableType(type: Type): Type { - return strictNullChecks - ? getGlobalNonNullableTypeInstantiation(type) - : type; - } - - function addOptionalTypeMarker(type: Type) { - return strictNullChecks - ? getUnionType([type, optionalType]) - : type; - } - - function isNotOptionalTypeMarker(type: Type) { - return type !== optionalType; - } - - function removeOptionalTypeMarker(type: Type): Type { - return strictNullChecks - ? filterType(type, isNotOptionalTypeMarker) - : type; - } - - function propagateOptionalTypeMarker( - type: Type, - node: OptionalChain, - wasOptional: boolean - ) { - return wasOptional - ? isOutermostOptionalChain(node) - ? getOptionalType(type) - : addOptionalTypeMarker(type) - : type; - } - - function getOptionalExpressionType( - exprType: Type, - expression: Expression - ) { - return isExpressionOfOptionalChainRoot(expression) - ? getNonNullableType(exprType) - : isOptionalChain(expression) - ? removeOptionalTypeMarker(exprType) - : exprType; - } - - /** - * Is source potentially coercible to target type under `==`. - * Assumes that `source` is a constituent of a union, hence - * the boolean literal flag on the LHS, but not on the RHS. - * - * This does not fully replicate the semantics of `==`. The - * intention is to catch cases that are clearly not right. - * - * Comparing (string | number) to number should not remove the - * string element. - * - * Comparing (string | number) to 1 will remove the string - * element, though this is not sound. This is a pragmatic - * choice. - * - * @see narrowTypeByEquality - * - * @param source - * @param target - */ - function isCoercibleUnderDoubleEquals( - source: Type, - target: Type - ): boolean { - return ((source.flags - & (TypeFlags.Number | TypeFlags.String - | TypeFlags.BooleanLiteral)) !== 0) - && ((target.flags - & (TypeFlags.Number | TypeFlags.String - | TypeFlags.Boolean)) !== 0); - } - - /** - * Return true if type was inferred from an object literal, written as an object type literal, or is the shape of a module - * with no call or construct signatures. - */ - function isObjectTypeWithInferableIndex(type: Type): boolean { - return !!(type.symbol - && (type.symbol.flags - & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral - | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 - && !typeHasCallOrConstructSignatures(type)) - || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped - && isObjectTypeWithInferableIndex((type as ReverseMappedType) - .source)); - } - - function createSymbolWithType(source: Symbol, type: Type | undefined) { - const symbol = createSymbol( - source.flags, - source.escapedName, - getCheckFlags(source) & CheckFlags.Readonly - ); - symbol.declarations = source.declarations; - symbol.parent = source.parent; - symbol.type = type; - symbol.target = source; - if (source.valueDeclaration) { - symbol.valueDeclaration = source.valueDeclaration; - } - if (source.nameType) { - symbol.nameType = source.nameType; - } - return symbol; - } - - function transformTypeOfMembers( - type: Type, - f: (propertyType: Type) => Type - ) { - const members = createSymbolTable(); - for (const property of getPropertiesOfObjectType(type)) { - const original = getTypeOfSymbol(property); - const updated = f(original); - members.set( - property.escapedName, - updated === original - ? property - : createSymbolWithType(property, updated) - ); - } - return members; - } - - /** - * If the the provided object literal is subject to the excess properties check, - * create a new that is exempt. Recursively mark object literal members as exempt. - * Leave signatures alone since they are not subject to the check. - */ - function getRegularTypeOfObjectLiteral(type: Type): Type { - if (!(isObjectLiteralType(type) - && getObjectFlags(type) & ObjectFlags.FreshLiteral)) - { - return type; - } - const regularType = ( type).regularType; - if (regularType) { - return regularType; - } - - const resolved = type; - const members = transformTypeOfMembers( - type, - getRegularTypeOfObjectLiteral - ); - const regularNew = createAnonymousType( - resolved.symbol, - members, - resolved.callSignatures, - resolved.constructSignatures, - resolved.stringIndexInfo, - resolved.numberIndexInfo - ); - regularNew.flags = resolved.flags; - regularNew - .objectFlags |= resolved.objectFlags - & ~ObjectFlags.FreshLiteral; - ( type).regularType = regularNew; - return regularNew; - } - - function createWideningContext( - parent: WideningContext | undefined, - propertyName: __String | undefined, - siblings: Type[] | undefined - ): WideningContext { - return { parent, propertyName, siblings, - resolvedProperties: undefined }; - } - - function getSiblingsOfContext(context: WideningContext): Type[] { - if (!context.siblings) { - const siblings: Type[] = []; - for (const type of getSiblingsOfContext(context.parent!)) { - if (isObjectLiteralType(type)) { - const prop = getPropertyOfObjectType( - type, - context.propertyName! - ); - if (prop) { - forEachType(getTypeOfSymbol(prop), t => { - siblings.push(t); - }); - } - } - } - context.siblings = siblings; - } - return context.siblings; - } - - function getPropertiesOfContext(context: WideningContext): Symbol[] { - if (!context.resolvedProperties) { - const names = - createMap() as UnderscoreEscapedMap; - for (const t of getSiblingsOfContext(context)) { - if (isObjectLiteralType(t) - && !(getObjectFlags(t) & ObjectFlags.ContainsSpread)) - { - for (const prop of getPropertiesOfType(t)) { - names.set(prop.escapedName, prop); - } - } - } - context.resolvedProperties = arrayFrom(names.values()); - } - return context.resolvedProperties; - } - - function getWidenedProperty( - prop: Symbol, - context: WideningContext | undefined - ): Symbol { - if (!(prop.flags & SymbolFlags.Property)) { - // Since get accessors already widen their return value there is no need to - // widen accessor based properties here. - return prop; - } - const original = getTypeOfSymbol(prop); - const propContext = context - && createWideningContext( - context, - prop.escapedName, /*siblings*/ - undefined - ); - const widened = getWidenedTypeWithContext(original, propContext); - return widened === original - ? prop - : createSymbolWithType(prop, widened); - } - - function getUndefinedProperty(prop: Symbol) { - const cached = undefinedProperties.get(prop.escapedName); - if (cached) { - return cached; - } - const result = createSymbolWithType(prop, undefinedType); - result.flags |= SymbolFlags.Optional; - undefinedProperties.set(prop.escapedName, result); - return result; - } - - function getWidenedTypeOfObjectLiteral( - type: Type, - context: WideningContext | undefined - ): Type { - const members = createSymbolTable(); - for (const prop of getPropertiesOfObjectType(type)) { - members.set( - prop.escapedName, - getWidenedProperty(prop, context) - ); - } - if (context) { - for (const prop of getPropertiesOfContext(context)) { - if (!members.has(prop.escapedName)) { - members.set( - prop.escapedName, - getUndefinedProperty(prop) - ); - } - } - } - const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String); - const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number); - const result = createAnonymousType( - type.symbol, - members, - emptyArray, - emptyArray, - stringIndexInfo - && createIndexInfo( - getWidenedType(stringIndexInfo.type), - stringIndexInfo.isReadonly - ), - numberIndexInfo - && createIndexInfo( - getWidenedType(numberIndexInfo.type), - numberIndexInfo.isReadonly - ) - ); - result - .objectFlags |= (getObjectFlags(type) - & (ObjectFlags.JSLiteral - | ObjectFlags - .NonInferrableType)); // Retain js literal flag through widening - return result; - } - - function getWidenedType(type: Type) { - return getWidenedTypeWithContext(type, /*context*/ undefined); - } - - function getWidenedTypeWithContext( - type: Type, - context: WideningContext | undefined - ): Type { - if (getObjectFlags(type) & ObjectFlags.RequiresWidening) { - if (context === undefined && type.widened) { - return type.widened; - } - let result: Type | undefined; - if (type.flags & (TypeFlags.Any | TypeFlags.Nullable)) { - result = anyType; - } else if (isObjectLiteralType(type)) { - result = getWidenedTypeOfObjectLiteral(type, context); - } else if (type.flags & TypeFlags.Union) { - const unionContext = context - || createWideningContext( - /*parent*/ undefined, /*propertyName*/ - undefined, - ( type).types - ); - const widenedTypes = sameMap( - ( type).types, - t => t.flags & TypeFlags.Nullable - ? t - : getWidenedTypeWithContext(t, unionContext) - ); - // Widening an empty object literal transitions from a highly restrictive type to - // a highly inclusive one. For that reason we perform subtype reduction here if the - // union includes empty object types (e.g. reducing {} | string to just {}). - result = getUnionType( - widenedTypes, - some(widenedTypes, isEmptyObjectType) - ? UnionReduction.Subtype - : UnionReduction.Literal - ); - } else if (type.flags & TypeFlags.Intersection) { - result = getIntersectionType(sameMap( - ( type).types, - getWidenedType - )); - } else if (isArrayType(type) || isTupleType(type)) { - result = createTypeReference( - ( type).target, - sameMap( - getTypeArguments( type), - getWidenedType - ) - ); - } - if (result && context === undefined) { - type.widened = result; - } - return result || type; - } - return type; - } - - /** - * Reports implicit any errors that occur as a result of widening 'null' and 'undefined' - * to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to - * getWidenedType. But in some cases getWidenedType is called without reporting errors - * (type argument inference is an example). - * - * The return value indicates whether an error was in fact reported. The particular circumstances - * are on a best effort basis. Currently, if the null or undefined that causes widening is inside - * an object literal property (arbitrarily deeply), this function reports an error. If no error is - * reported, reportImplicitAnyError is a suitable fallback to report a general error. - */ - function reportWideningErrorsInType(type: Type): boolean { - let errorReported = false; - if (getObjectFlags(type) & ObjectFlags.ContainsWideningType) { - if (type.flags & TypeFlags.Union) { - if (some(( type).types, isEmptyObjectType)) { - errorReported = true; - } else { - for (const t of ( type).types) { - if (reportWideningErrorsInType(t)) { - errorReported = true; - } - } - } - } - if (isArrayType(type) || isTupleType(type)) { - for (const t of getTypeArguments( type)) { - if (reportWideningErrorsInType(t)) { - errorReported = true; - } - } - } - if (isObjectLiteralType(type)) { - for (const p of getPropertiesOfObjectType(type)) { - const t = getTypeOfSymbol(p); - if (getObjectFlags(t) - & ObjectFlags.ContainsWideningType) - { - if (!reportWideningErrorsInType(t)) { - error( - p.valueDeclaration, - Diagnostics - .Object_literal_s_property_0_implicitly_has_an_1_type, - symbolToString(p), - typeToString(getWidenedType(t)) - ); - } - errorReported = true; - } - } - } - } - return errorReported; - } - - function reportImplicitAny( - declaration: Declaration, - type: Type, - wideningKind?: WideningKind - ) { - const typeAsString = typeToString(getWidenedType(type)); - if (isInJSFile(declaration) - && !isCheckJsEnabledForFile( - getSourceFileOfNode(declaration), - compilerOptions - )) - { - // Only report implicit any errors/suggestions in TS and ts-check JS files - return; - } - let diagnostic: DiagnosticMessage; - switch (declaration.kind) { - case SyntaxKind.BinaryExpression: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - diagnostic = noImplicitAny - ? Diagnostics.Member_0_implicitly_has_an_1_type - : Diagnostics - .Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; - break; - case SyntaxKind.Parameter: - const param = declaration as ParameterDeclaration; - if (isIdentifier(param.name) - && (isCallSignatureDeclaration(param.parent) - || isMethodSignature(param.parent) - || isFunctionTypeNode(param.parent)) - && param.parent.parameters.indexOf(param) > -1 - && (resolveName( - param, - param.name.escapedText, - SymbolFlags.Type, - undefined, - param.name.escapedText, /*isUse*/ - true - ) - || param.name.originalKeywordKind - && isTypeNodeKind(param.name.originalKeywordKind))) - { - const newName = 'arg' - + param.parent.parameters.indexOf(param); - errorOrSuggestion( - noImplicitAny, - declaration, - Diagnostics - .Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1, - newName, - declarationNameToString(param.name) - ); - return; - } - diagnostic = ( declaration) - .dotDotDotToken - ? noImplicitAny - ? Diagnostics - .Rest_parameter_0_implicitly_has_an_any_type - : Diagnostics - .Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage - : noImplicitAny - ? Diagnostics.Parameter_0_implicitly_has_an_1_type - : Diagnostics - .Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; - break; - case SyntaxKind.BindingElement: - diagnostic = Diagnostics - .Binding_element_0_implicitly_has_an_1_type; - if (!noImplicitAny) { - // Don't issue a suggestion for binding elements since the codefix doesn't yet support them. - return; - } - break; - case SyntaxKind.JSDocFunctionType: - error( - declaration, - Diagnostics - .Function_type_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, - typeAsString - ); - return; - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - if (noImplicitAny - && !(declaration as NamedDeclaration).name) - { - if (wideningKind === WideningKind.GeneratorYield) { - error( - declaration, - Diagnostics - .Generator_implicitly_has_yield_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type_annotation, - typeAsString - ); - } else { - error( - declaration, - Diagnostics - .Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, - typeAsString - ); - } - return; - } - diagnostic = !noImplicitAny - ? Diagnostics - ._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage - : wideningKind === WideningKind.GeneratorYield - ? Diagnostics - ._0_which_lacks_return_type_annotation_implicitly_has_an_1_yield_type - : Diagnostics - ._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type; - break; - case SyntaxKind.MappedType: - if (noImplicitAny) { - error( - declaration, - Diagnostics - .Mapped_object_type_implicitly_has_an_any_template_type - ); - } - return; - default: - diagnostic = noImplicitAny - ? Diagnostics.Variable_0_implicitly_has_an_1_type - : Diagnostics - .Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; - } - errorOrSuggestion( - noImplicitAny, - declaration, - diagnostic, - declarationNameToString(getNameOfDeclaration(declaration)), - typeAsString - ); - } - - function reportErrorsFromWidening( - declaration: Declaration, - type: Type, - wideningKind?: WideningKind - ) { - if (produceDiagnostics && noImplicitAny - && getObjectFlags(type) & ObjectFlags.ContainsWideningType) - { - // Report implicit any error within type if possible, otherwise report error on declaration - if (!reportWideningErrorsInType(type)) { - reportImplicitAny(declaration, type, wideningKind); - } - } - } - - function applyToParameterTypes( - source: Signature, - target: Signature, - callback: (s: Type, t: Type) => void - ) { - const sourceCount = getParameterCount(source); - const targetCount = getParameterCount(target); - const sourceRestType = getEffectiveRestType(source); - const targetRestType = getEffectiveRestType(target); - const targetNonRestCount = targetRestType - ? targetCount - 1 - : targetCount; - const paramCount = sourceRestType - ? targetNonRestCount - : Math.min(sourceCount, targetNonRestCount); - const sourceThisType = getThisTypeOfSignature(source); - if (sourceThisType) { - const targetThisType = getThisTypeOfSignature(target); - if (targetThisType) { - callback(sourceThisType, targetThisType); - } - } - for (let i = 0; i < paramCount; i++) { - callback( - getTypeAtPosition(source, i), - getTypeAtPosition(target, i) - ); - } - if (targetRestType) { - callback( - getRestTypeAtPosition(source, paramCount), - targetRestType - ); - } - } - - function applyToReturnTypes( - source: Signature, - target: Signature, - callback: (s: Type, t: Type) => void - ) { - const sourceTypePredicate = getTypePredicateOfSignature(source); - const targetTypePredicate = getTypePredicateOfSignature(target); - if (sourceTypePredicate && targetTypePredicate - && typePredicateKindsMatch( - sourceTypePredicate, - targetTypePredicate - ) && sourceTypePredicate.type && targetTypePredicate.type) - { - callback(sourceTypePredicate.type, targetTypePredicate.type); - } else { - callback( - getReturnTypeOfSignature(source), - getReturnTypeOfSignature(target) - ); - } - } - - function createInferenceContext( - typeParameters: readonly TypeParameter[], - signature: Signature | undefined, - flags: InferenceFlags, - compareTypes?: TypeComparer - ): InferenceContext { - return createInferenceContextWorker( - typeParameters.map(createInferenceInfo), - signature, - flags, - compareTypes || compareTypesAssignable - ); - } - - function cloneInferenceContext( - context: T, - extraFlags: InferenceFlags = 0 - ): InferenceContext | T & undefined { - return context - && createInferenceContextWorker( - map(context.inferences, cloneInferenceInfo), - context.signature, - context.flags | extraFlags, - context.compareTypes - ); - } - - function createInferenceContextWorker( - inferences: InferenceInfo[], - signature: Signature | undefined, - flags: InferenceFlags, - compareTypes: TypeComparer - ): InferenceContext { - const context: InferenceContext = { - inferences, - signature, - flags, - compareTypes, - mapper: t => mapToInferredType(context, t, /*fix*/ true), - nonFixingMapper: t => mapToInferredType( - context, - t, /*fix*/ - false - ) - }; - return context; - } - - function mapToInferredType( - context: InferenceContext, - t: Type, - fix: boolean - ): Type { - const inferences = context.inferences; - for (let i = 0; i < inferences.length; i++) { - const inference = inferences[i]; - if (t === inference.typeParameter) { - if (fix && !inference.isFixed) { - clearCachedInferences(inferences); - inference.isFixed = true; - } - return getInferredType(context, i); - } - } - return t; - } - - function clearCachedInferences(inferences: InferenceInfo[]) { - for (const inference of inferences) { - if (!inference.isFixed) { - inference.inferredType = undefined; - } - } - } - - function createInferenceInfo(typeParameter: - TypeParameter): InferenceInfo - { - return { - typeParameter, - candidates: undefined, - contraCandidates: undefined, - inferredType: undefined, - priority: undefined, - topLevel: true, - isFixed: false - }; - } - - function cloneInferenceInfo(inference: InferenceInfo): InferenceInfo { - return { - typeParameter: inference.typeParameter, - candidates: inference.candidates - && inference.candidates.slice(), - contraCandidates: inference.contraCandidates - && inference.contraCandidates.slice(), - inferredType: inference.inferredType, - priority: inference.priority, - topLevel: inference.topLevel, - isFixed: inference.isFixed - }; - } - - function cloneInferredPartOfContext(context: - InferenceContext): InferenceContext | undefined - { - const inferences = filter( - context.inferences, - hasInferenceCandidates - ); - return inferences.length - ? createInferenceContextWorker( - map(inferences, cloneInferenceInfo), - context.signature, - context.flags, - context.compareTypes - ) - : undefined; - } - - function getMapperFromContext(context: T): TypeMapper | T & undefined - { - return context && context.mapper; - } - - // Return true if the given type could possibly reference a type parameter for which - // we perform type inference (i.e. a type parameter of a generic function). We cache - // results for union and intersection types for performance reasons. - function couldContainTypeVariables(type: Type): boolean { - const objectFlags = getObjectFlags(type); - return !!(type.flags & TypeFlags.Instantiable - || objectFlags & ObjectFlags.Reference - && (( type).node - || forEach( - getTypeArguments( type), - couldContainTypeVariables - )) - || objectFlags & ObjectFlags.Anonymous && type.symbol - && type.symbol.flags - & (SymbolFlags.Function | SymbolFlags.Method - | SymbolFlags.Class | SymbolFlags.TypeLiteral - | SymbolFlags.ObjectLiteral) - && type.symbol.declarations - || objectFlags - & (ObjectFlags.Mapped | ObjectFlags.ObjectRestType) - || type.flags & TypeFlags.UnionOrIntersection - && !(type.flags & TypeFlags.EnumLiteral) - && couldUnionOrIntersectionContainTypeVariables( type)); - } - - function couldUnionOrIntersectionContainTypeVariables(type: - UnionOrIntersectionType): boolean - { - if (type.couldContainTypeVariables === undefined) { - type.couldContainTypeVariables = some( - type.types, - couldContainTypeVariables - ); - } - return type.couldContainTypeVariables; - } - - function isTypeParameterAtTopLevel( - type: Type, - typeParameter: TypeParameter - ): boolean { - return !!(type === typeParameter - || type.flags & TypeFlags.UnionOrIntersection - && some( - ( type).types, - t => isTypeParameterAtTopLevel(t, typeParameter) - ) - || type.flags & TypeFlags.Conditional && ( - isTypeParameterAtTopLevel( - getTrueTypeFromConditionalType( type), - typeParameter - ) - || isTypeParameterAtTopLevel( - getFalseTypeFromConditionalType( type), - typeParameter - ) - )); - } - - /** Create an object with properties named in the string literal type. Every property has type `any` */ - function createEmptyObjectTypeFromStringLiteral(type: Type) { - const members = createSymbolTable(); - forEachType(type, t => { - if (!(t.flags & TypeFlags.StringLiteral)) { - return; - } - const name = - escapeLeadingUnderscores((t as StringLiteralType).value); - const literalProp = createSymbol(SymbolFlags.Property, name); - literalProp.type = anyType; - if (t.symbol) { - literalProp.declarations = t.symbol.declarations; - literalProp.valueDeclaration = t.symbol.valueDeclaration; - } - members.set(name, literalProp); - }); - const indexInfo = type.flags & TypeFlags.String - ? createIndexInfo(emptyObjectType, /*isReadonly*/ false) - : undefined; - return createAnonymousType( - undefined, - members, - emptyArray, - emptyArray, - indexInfo, - undefined - ); - } - - /** - * Infer a suitable input type for a homomorphic mapped type { [P in keyof T]: X }. We construct - * an object type with the same set of properties as the source type, where the type of each - * property is computed by inferring from the source property type to X for the type - * variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). - */ - function inferTypeForHomomorphicMappedType( - source: Type, - target: MappedType, - constraint: IndexType - ): Type | undefined { - const key = source.id + ',' + target.id + ',' + constraint.id; - if (reverseMappedCache.has(key)) { - return reverseMappedCache.get(key); - } - reverseMappedCache.set(key, undefined); - const type = createReverseMappedType(source, target, constraint); - reverseMappedCache.set(key, type); - return type; - } - - // We consider a type to be partially inferable if it isn't marked non-inferable or if it is - // an object literal type with at least one property of an inferable type. For example, an object - // literal { a: 123, b: x => true } is marked non-inferable because it contains a context sensitive - // arrow function, but is considered partially inferable because property 'a' has an inferable type. - function isPartiallyInferableType(type: Type): boolean { - return !(getObjectFlags(type) & ObjectFlags.NonInferrableType) - || isObjectLiteralType(type) - && some( - getPropertiesOfType(type), - prop => isPartiallyInferableType(getTypeOfSymbol(prop)) - ); - } - - function createReverseMappedType( - source: Type, - target: MappedType, - constraint: IndexType - ) { - // We consider a source type reverse mappable if it has a string index signature or if - // it has one or more properties and is of a partially inferable type. - if (!(getIndexInfoOfType(source, IndexKind.String) - || getPropertiesOfType(source).length !== 0 - && isPartiallyInferableType(source))) - { - return undefined; - } - // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been - // applied to the element type(s). - if (isArrayType(source)) { - return createArrayType( - inferReverseMappedType( - getTypeArguments( source)[0], - target, - constraint - ), - isReadonlyArrayType(source) - ); - } - if (isTupleType(source)) { - const elementTypes = map( - getTypeArguments(source), - t => inferReverseMappedType(t, target, constraint) - ); - const minLength = - getMappedTypeModifiers(target) - & MappedTypeModifiers.IncludeOptional - ? getTypeReferenceArity(source) - - (source.target.hasRestElement ? 1 : 0) - : source.target.minLength; - return createTupleType( - elementTypes, - minLength, - source.target.hasRestElement, - source.target.readonly, - source.target.associatedNames - ); - } - // For all other object types we infer a new object type where the reverse mapping has been - // applied to the type of each property. - const reversed = createObjectType( - ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ - undefined - ) as ReverseMappedType; - reversed.source = source; - reversed.mappedType = target; - reversed.constraintType = constraint; - return reversed; - } - - function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) { - return inferReverseMappedType( - symbol.propertyType, - symbol.mappedType, - symbol.constraintType - ); - } - - function inferReverseMappedType( - sourceType: Type, - target: MappedType, - constraint: IndexType - ): Type { - const typeParameter = getIndexedAccessType( - constraint.type, - getTypeParameterFromMappedType(target) - ); - const templateType = getTemplateTypeFromMappedType(target); - const inference = createInferenceInfo(typeParameter); - inferTypes([inference], sourceType, templateType); - return getTypeFromInference(inference) || unknownType; - } - - function* getUnmatchedProperties( - source: Type, - target: Type, - requireOptionalProperties: boolean, - matchDiscriminantProperties: boolean - ): IterableIterator { - const properties = getPropertiesOfType(target); - for (const targetProp of properties) { - // TODO: remove this when we support static private identifier fields and find other solutions to get privateNamesAndStaticFields test to pass - if (isStaticPrivateIdentifierProperty(targetProp)) { - continue; - } - if (requireOptionalProperties - || !(targetProp.flags & SymbolFlags.Optional - || getCheckFlags(targetProp) & CheckFlags.Partial)) - { - const sourceProp = getPropertyOfType( - source, - targetProp.escapedName - ); - if (!sourceProp) { - yield targetProp; - } else if (matchDiscriminantProperties) { - const targetType = getTypeOfSymbol(targetProp); - if (targetType.flags & TypeFlags.Unit) { - const sourceType = getTypeOfSymbol(sourceProp); - if (!(sourceType.flags & TypeFlags.Any - || getRegularTypeOfLiteralType(sourceType) - === getRegularTypeOfLiteralType(targetType))) - { - yield targetProp; - } - } - } - } - } - } - - function getUnmatchedProperty( - source: Type, - target: Type, - requireOptionalProperties: boolean, - matchDiscriminantProperties: boolean - ): Symbol | undefined { - const result = getUnmatchedProperties( - source, - target, - requireOptionalProperties, - matchDiscriminantProperties - ).next(); - if (!result.done) return result.value; - } - - function tupleTypesDefinitelyUnrelated( - source: TupleTypeReference, - target: TupleTypeReference - ) { - return target.target.minLength > source.target.minLength - || !getRestTypeOfTupleType(target) - && (!!getRestTypeOfTupleType(source) - || getLengthOfTupleType(target) - < getLengthOfTupleType(source)); - } - - function typesDefinitelyUnrelated(source: Type, target: Type) { - // Two tuple types with incompatible arities are definitely unrelated. - // Two object types that each have a property that is unmatched in the other are definitely unrelated. - return isTupleType(source) && isTupleType(target) - && tupleTypesDefinitelyUnrelated(source, target) - || !!getUnmatchedProperty( - source, - target, /*requireOptionalProperties*/ - false, /*matchDiscriminantProperties*/ - true - ) - && !!getUnmatchedProperty( - target, - source, /*requireOptionalProperties*/ - false, /*matchDiscriminantProperties*/ - true - ); - } - - function getTypeFromInference(inference: InferenceInfo) { - return inference.candidates - ? getUnionType(inference.candidates, UnionReduction.Subtype) - : inference.contraCandidates - ? getIntersectionType(inference.contraCandidates) - : undefined; - } - - function inferTypes( - inferences: InferenceInfo[], - originalSource: Type, - originalTarget: Type, - priority: InferencePriority = 0, - contravariant = false - ) { - let symbolStack: Symbol[]; - let visited: Map; - let bivariant = false; - let propagationType: Type; - let inferencePriority = InferencePriority.MaxValue; - let allowComplexConstraintInference = true; - inferFromTypes(originalSource, originalTarget); - - function inferFromTypes(source: Type, target: Type): void { - if (!couldContainTypeVariables(target)) { - return; - } - if (source === wildcardType) { - // We are inferring from an 'any' type. We want to infer this type for every type parameter - // referenced in the target type, so we record it as the propagation type and infer from the - // target to itself. Then, as we find candidates we substitute the propagation type. - const savePropagationType = propagationType; - propagationType = source; - inferFromTypes(target, target); - propagationType = savePropagationType; - return; - } - if (source.aliasSymbol && source.aliasTypeArguments - && source.aliasSymbol === target.aliasSymbol) - { - // Source and target are types originating in the same generic type alias declaration. - // Simply infer from source type arguments to target type arguments. - inferFromTypeArguments( - source.aliasTypeArguments, - target.aliasTypeArguments!, - getAliasVariances(source.aliasSymbol) - ); - return; - } - if (source === target - && source.flags & TypeFlags.UnionOrIntersection) - { - // When source and target are the same union or intersection type, just relate each constituent - // type to itself. - for (const t of ( source).types) { - inferFromTypes(t, t); - } - return; - } - if (target.flags & TypeFlags.Union) { - // First, infer between identically matching source and target constituents and remove the - // matching types. - const [tempSources, tempTargets] = inferFromMatchingTypes( - source.flags & TypeFlags.Union - ? ( source).types - : [source], - ( target).types, - isTypeOrBaseIdenticalTo - ); - // Next, infer between closely matching source and target constituents and remove - // the matching types. Types closely match when they are instantiations of the same - // object type or instantiations of the same type alias. - const [sources, targets] = inferFromMatchingTypes( - tempSources, - tempTargets, - isTypeCloselyMatchedBy - ); - if (targets.length === 0) { - return; - } - target = getUnionType(targets); - if (sources.length === 0) { - // All source constituents have been matched and there is nothing further to infer from. - // However, simply making no inferences is undesirable because it could ultimately mean - // inferring a type parameter constraint. Instead, make a lower priority inference from - // the full source to whatever remains in the target. For example, when inferring from - // string to 'string | T', make a lower priority inference of string for T. - inferWithPriority( - source, - target, - InferencePriority.NakedTypeVariable - ); - return; - } - source = getUnionType(sources); - } else if (target.flags & TypeFlags.Intersection - && some( - ( target).types, - t => !!getInferenceInfoForType(t) - || (isGenericMappedType(t) - && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) - || neverType)) - )) - { - // We reduce intersection types only when they contain naked type parameters. For example, when - // inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and - // infer { extra: any } for T. But when inferring to 'string[] & Iterable' we want to keep the - // string[] on the source side and infer string for T. - // Likewise, we consider a homomorphic mapped type constrainted to the target type parameter as similar to a "naked type variable" - // in such scenarios. - if (!(source.flags & TypeFlags.Union)) { - // Infer between identically matching source and target constituents and remove the matching types. - const [sources, targets] = inferFromMatchingTypes( - source.flags & TypeFlags.Intersection - ? ( source).types - : [source], - ( target).types, - isTypeIdenticalTo - ); - if (sources.length === 0 || targets.length === 0) { - return; - } - source = getIntersectionType(sources); - target = getIntersectionType(targets); - } - } else if (target.flags - & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) - { - target = getActualTypeVariable(target); - } - if (target.flags & TypeFlags.TypeVariable) { - // If target is a type parameter, make an inference, unless the source type contains - // the anyFunctionType (the wildcard type that's used to avoid contextually typing functions). - // Because the anyFunctionType is internal, it should not be exposed to the user by adding - // it as an inference candidate. Hopefully, a better candidate will come along that does - // not contain anyFunctionType when we come back to this argument for its second round - // of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard - // when constructing types from type parameters that had no inference candidates). - if (getObjectFlags(source) & ObjectFlags.NonInferrableType - || source === nonInferrableAnyType - || source === silentNeverType - || (priority & InferencePriority.ReturnType - && (source === autoType - || source === autoArrayType))) - { - return; - } - const inference = getInferenceInfoForType(target); - if (inference) { - if (!inference.isFixed) { - if (inference.priority === undefined - || priority < inference.priority) - { - inference.candidates = undefined; - inference.contraCandidates = undefined; - inference.topLevel = true; - inference.priority = priority; - } - if (priority === inference.priority) { - const candidate = propagationType || source; - // We make contravariant inferences only if we are in a pure contravariant position, - // i.e. only if we have not descended into a bivariant position. - if (contravariant && !bivariant) { - if (!contains( - inference.contraCandidates, - candidate - )) { - inference.contraCandidates = append( - inference.contraCandidates, - candidate - ); - clearCachedInferences(inferences); - } - } else if (!contains( - inference.candidates, - candidate - )) { - inference.candidates = append( - inference.candidates, - candidate - ); - clearCachedInferences(inferences); - } - } - if (!(priority & InferencePriority.ReturnType) - && target.flags & TypeFlags.TypeParameter - && inference.topLevel - && !isTypeParameterAtTopLevel( - originalTarget, - target - )) - { - inference.topLevel = false; - clearCachedInferences(inferences); - } - } - inferencePriority = Math.min( - inferencePriority, - priority - ); - return; - } else { - // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine - const simplified = getSimplifiedType( - target, /*writing*/ - false - ); - if (simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); - } else if (target.flags & TypeFlags.IndexedAccess) { - const indexType = getSimplifiedType( - (target as IndexedAccessType) - .indexType, /*writing*/ - false - ); - // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider - // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. - if (indexType.flags & TypeFlags.Instantiable) { - const simplified = - distributeIndexOverObjectType( - getSimplifiedType( - (target as IndexedAccessType) - .objectType, /*writing*/ - false - ), - indexType, /*writing*/ - false - ); - if (simplified && simplified !== target) { - invokeOnce( - source, - simplified, - inferFromTypes - ); - } - } - } - } - } - if (getObjectFlags(source) & ObjectFlags.Reference - && getObjectFlags(target) & ObjectFlags.Reference && ( - ( source).target - === ( target).target - || isArrayType(source) && isArrayType(target) - ) - && !(( source).node - && ( target).node)) - { - // If source and target are references to the same generic type, infer from type arguments - inferFromTypeArguments( - getTypeArguments( source), - getTypeArguments( target), - getVariances(( source).target) - ); - } else if (source.flags & TypeFlags.Index - && target.flags & TypeFlags.Index) - { - contravariant = !contravariant; - inferFromTypes( - ( source).type, - ( target).type - ); - contravariant = !contravariant; - } else if ((isLiteralType(source) - || source.flags & TypeFlags.String) - && target.flags & TypeFlags.Index) - { - const empty = - createEmptyObjectTypeFromStringLiteral(source); - contravariant = !contravariant; - inferWithPriority( - empty, - (target as IndexType).type, - InferencePriority.LiteralKeyof - ); - contravariant = !contravariant; - } else if (source.flags & TypeFlags.IndexedAccess - && target.flags & TypeFlags.IndexedAccess) - { - inferFromTypes( - ( source).objectType, - ( target).objectType - ); - inferFromTypes( - ( source).indexType, - ( target).indexType - ); - } else if (source.flags & TypeFlags.Conditional - && target.flags & TypeFlags.Conditional) - { - inferFromTypes( - ( source).checkType, - ( target).checkType - ); - inferFromTypes( - ( source).extendsType, - ( target).extendsType - ); - inferFromTypes( - getTrueTypeFromConditionalType( source), - getTrueTypeFromConditionalType( target) - ); - inferFromTypes( - getFalseTypeFromConditionalType( source), - getFalseTypeFromConditionalType( target) - ); - } else if (target.flags & TypeFlags.Conditional) { - const savePriority = priority; - priority |= contravariant - ? InferencePriority.ContravariantConditional - : 0; - const targetTypes = - [getTrueTypeFromConditionalType( target), - getFalseTypeFromConditionalType( target)]; - inferToMultipleTypes(source, targetTypes, target.flags); - priority = savePriority; - } else if (target.flags & TypeFlags.UnionOrIntersection) { - inferToMultipleTypes( - source, - ( target).types, - target.flags - ); - } else if (source.flags & TypeFlags.Union) { - // Source is a union or intersection type, infer from each constituent type - const sourceTypes = ( source) - .types; - for (const sourceType of sourceTypes) { - inferFromTypes(sourceType, target); - } - } else { - if (!(priority & InferencePriority.NoConstraints - && source.flags - & (TypeFlags.Intersection - | TypeFlags.Instantiable))) - { - const apparentSource = getApparentType(source); - // getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type. - // If that occurs and it doesn't simplify to an object or intersection, we'll need to restart `inferFromTypes` - // with the simplified source. - if (apparentSource !== source - && allowComplexConstraintInference - && !(apparentSource.flags - & (TypeFlags.Object | TypeFlags.Intersection))) - { - // TODO: The `allowComplexConstraintInference` flag is a hack! This forbids inference from complex constraints within constraints! - // This isn't required algorithmically, but rather is used to lower the memory burden caused by performing inference - // that is _too good_ in projects with complicated constraints (eg, fp-ts). In such cases, if we did not limit ourselves - // here, we might produce more valid inferences for types, causing us to do more checks and perform more instantiations - // (in addition to the extra stack depth here) which, in turn, can push the already close process over its limit. - // TL;DR: If we ever become generally more memory efficient (or our resource budget ever increases), we should just - // remove this `allowComplexConstraintInference` flag. - allowComplexConstraintInference = false; - return inferFromTypes(apparentSource, target); - } - source = apparentSource; - } - if (source.flags - & (TypeFlags.Object | TypeFlags.Intersection)) - { - invokeOnce(source, target, inferFromObjectTypes); - } - } - } - - function inferWithPriority( - source: Type, - target: Type, - newPriority: InferencePriority - ) { - const savePriority = priority; - priority |= newPriority; - inferFromTypes(source, target); - priority = savePriority; - } - - function invokeOnce( - source: Type, - target: Type, - action: (source: Type, target: Type) => void - ) { - const key = source.id + ',' + target.id; - const status = visited && visited.get(key); - if (status !== undefined) { - inferencePriority = Math.min(inferencePriority, status); - return; - } - (visited || (visited = createMap())).set( - key, - InferencePriority.Circularity - ); - const saveInferencePriority = inferencePriority; - inferencePriority = InferencePriority.MaxValue; - action(source, target); - visited.set(key, inferencePriority); - inferencePriority = Math.min( - inferencePriority, - saveInferencePriority - ); - } - - function inferFromMatchingTypes( - sources: Type[], - targets: Type[], - matches: (s: Type, t: Type) => boolean - ): [Type[], Type[]] { - let matchedSources: Type[] | undefined; - let matchedTargets: Type[] | undefined; - for (const t of targets) { - for (const s of sources) { - if (matches(s, t)) { - inferFromTypes(s, t); - matchedSources = appendIfUnique(matchedSources, s); - matchedTargets = appendIfUnique(matchedTargets, t); - } - } - } - return [ - matchedSources - ? filter(sources, t => !contains(matchedSources, t)) - : sources, - matchedTargets - ? filter(targets, t => !contains(matchedTargets, t)) - : targets - ]; - } - - function inferFromTypeArguments( - sourceTypes: readonly Type[], - targetTypes: readonly Type[], - variances: readonly VarianceFlags[] - ) { - const count = sourceTypes.length < targetTypes.length - ? sourceTypes.length - : targetTypes.length; - for (let i = 0; i < count; i++) { - if (i < variances.length - && (variances[i] & VarianceFlags.VarianceMask) - === VarianceFlags.Contravariant) - { - inferFromContravariantTypes( - sourceTypes[i], - targetTypes[i] - ); - } else { - inferFromTypes(sourceTypes[i], targetTypes[i]); - } - } - } - - function inferFromContravariantTypes(source: Type, target: Type) { - if (strictFunctionTypes - || priority & InferencePriority.AlwaysStrict) - { - contravariant = !contravariant; - inferFromTypes(source, target); - contravariant = !contravariant; - } else { - inferFromTypes(source, target); - } - } - - function getInferenceInfoForType(type: Type) { - if (type.flags & TypeFlags.TypeVariable) { - for (const inference of inferences) { - if (type === inference.typeParameter) { - return inference; - } - } - } - return undefined; - } - - function getSingleTypeVariableFromIntersectionTypes(types: - Type[]) - { - let typeVariable: Type | undefined; - for (const type of types) { - const t = type.flags & TypeFlags.Intersection - && find( - ( type).types, - t => !!getInferenceInfoForType(t) - ); - if (!t || typeVariable && t !== typeVariable) { - return undefined; - } - typeVariable = t; - } - return typeVariable; - } - - function inferToMultipleTypes( - source: Type, - targets: Type[], - targetFlags: TypeFlags - ) { - let typeVariableCount = 0; - if (targetFlags & TypeFlags.Union) { - let nakedTypeVariable: Type | undefined; - const sources = source.flags & TypeFlags.Union - ? ( source).types - : [source]; - const matched = new Array(sources.length); - let inferenceCircularity = false; - // First infer to types that are not naked type variables. For each source type we - // track whether inferences were made from that particular type to some target with - // equal priority (i.e. of equal quality) to what we would infer for a naked type - // parameter. - for (const t of targets) { - if (getInferenceInfoForType(t)) { - nakedTypeVariable = t; - typeVariableCount++; - } else { - for (let i = 0; i < sources.length; i++) { - const saveInferencePriority = - inferencePriority; - inferencePriority = InferencePriority.MaxValue; - inferFromTypes(sources[i], t); - if (inferencePriority === priority) { - matched[i] = true; - } - inferenceCircularity = inferenceCircularity - || inferencePriority - === InferencePriority.Circularity; - inferencePriority = Math.min( - inferencePriority, - saveInferencePriority - ); - } - } - } - if (typeVariableCount === 0) { - // If every target is an intersection of types containing a single naked type variable, - // make a lower priority inference to that type variable. This handles inferring from - // 'A | B' to 'T & (X | Y)' where we want to infer 'A | B' for T. - const intersectionTypeVariable = - getSingleTypeVariableFromIntersectionTypes(targets); - if (intersectionTypeVariable) { - inferWithPriority( - source, - intersectionTypeVariable, - InferencePriority.NakedTypeVariable - ); - } - return; - } - // If the target has a single naked type variable and no inference circularities were - // encountered above (meaning we explored the types fully), create a union of the source - // types from which no inferences have been made so far and infer from that union to the - // naked type variable. - if (typeVariableCount === 1 && !inferenceCircularity) { - const unmatched = flatMap( - sources, - (s, i) => matched[i] ? undefined : s - ); - if (unmatched.length) { - inferFromTypes( - getUnionType(unmatched), - nakedTypeVariable! - ); - return; - } - } - } else { - // We infer from types that are not naked type variables first so that inferences we - // make from nested naked type variables and given slightly higher priority by virtue - // of being first in the candidates array. - for (const t of targets) { - if (getInferenceInfoForType(t)) { - typeVariableCount++; - } else { - inferFromTypes(source, t); - } - } - } - // Inferences directly to naked type variables are given lower priority as they are - // less specific. For example, when inferring from Promise to T | Promise, - // we want to infer string for T, not Promise | string. For intersection types - // we only infer to single naked type variables. - if (targetFlags & TypeFlags.Intersection - ? typeVariableCount === 1 - : typeVariableCount > 0) - { - for (const t of targets) { - if (getInferenceInfoForType(t)) { - inferWithPriority( - source, - t, - InferencePriority.NakedTypeVariable - ); - } - } - } - } - - function inferToMappedType( - source: Type, - target: MappedType, - constraintType: Type - ): boolean { - if (constraintType.flags & TypeFlags.Union) { - let result = false; - for (const type of (constraintType as UnionType).types) { - result = inferToMappedType(source, target, type) - || result; - } - return result; - } - if (constraintType.flags & TypeFlags.Index) { - // We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X }, - // where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source - // type and then make a secondary inference from that type to T. We make a secondary inference - // such that direct inferences to T get priority over inferences to Partial, for example. - const inference = - getInferenceInfoForType(( constraintType) - .type); - if (inference && !inference.isFixed) { - const inferredType = inferTypeForHomomorphicMappedType( - source, - target, - constraintType - ); - if (inferredType) { - // We assign a lower priority to inferences made from types containing non-inferrable - // types because we may only have a partial result (i.e. we may have failed to make - // reverse inferences for some properties). - inferWithPriority( - inferredType, - inference.typeParameter, - getObjectFlags(source) - & ObjectFlags.NonInferrableType - ? InferencePriority - .PartialHomomorphicMappedType - : InferencePriority.HomomorphicMappedType - ); - } - } - return true; - } - if (constraintType.flags & TypeFlags.TypeParameter) { - // We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type - // parameter. First infer from 'keyof S' to K. - inferWithPriority( - getIndexType(source), - constraintType, - InferencePriority.MappedTypeConstraint - ); - // If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X }, - // where K extends keyof T, we make the same inferences as for a homomorphic mapped type - // { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a - // Pick. - const extendedConstraint = - getConstraintOfType(constraintType); - if (extendedConstraint - && inferToMappedType( - source, - target, - extendedConstraint - )) - { - return true; - } - // If no inferences can be made to K's constraint, infer from a union of the property types - // in the source to the template type X. - const propTypes = map( - getPropertiesOfType(source), - getTypeOfSymbol - ); - const stringIndexType = getIndexTypeOfType( - source, - IndexKind.String - ); - const numberIndexInfo = getNonEnumNumberIndexInfo(source); - const numberIndexType = numberIndexInfo - && numberIndexInfo.type; - inferFromTypes( - getUnionType(append( - append(propTypes, stringIndexType), - numberIndexType - )), - getTemplateTypeFromMappedType(target) - ); - return true; - } - return false; - } - - function inferFromObjectTypes(source: Type, target: Type) { - // If we are already processing another target type with the same associated symbol (such as - // an instantiation of the same generic type), we do not explore this target as it would yield - // no further inferences. We exclude the static side of classes from this check since it shares - // its symbol with the instance side which would lead to false positives. - const isNonConstructorObject = target.flags & TypeFlags.Object - && !(getObjectFlags(target) & ObjectFlags.Anonymous - && target.symbol - && target.symbol.flags & SymbolFlags.Class); - const symbol = isNonConstructorObject - ? target.symbol - : undefined; - if (symbol) { - if (contains(symbolStack, symbol)) { - inferencePriority = InferencePriority.Circularity; - return; - } - (symbolStack || (symbolStack = [])).push(symbol); - inferFromObjectTypesWorker(source, target); - symbolStack.pop(); - } else { - inferFromObjectTypesWorker(source, target); - } - } - - function inferFromObjectTypesWorker(source: Type, target: Type) { - if (getObjectFlags(source) & ObjectFlags.Reference - && getObjectFlags(target) & ObjectFlags.Reference && ( - ( source).target - === ( target).target - || isArrayType(source) && isArrayType(target) - )) - { - // If source and target are references to the same generic type, infer from type arguments - inferFromTypeArguments( - getTypeArguments( source), - getTypeArguments( target), - getVariances(( source).target) - ); - return; - } - if (isGenericMappedType(source) - && isGenericMappedType(target)) - { - // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer - // from S to T and from X to Y. - inferFromTypes( - getConstraintTypeFromMappedType(source), - getConstraintTypeFromMappedType(target) - ); - inferFromTypes( - getTemplateTypeFromMappedType(source), - getTemplateTypeFromMappedType(target) - ); - } - if (getObjectFlags(target) & ObjectFlags.Mapped) { - const constraintType = - getConstraintTypeFromMappedType( target); - if (inferToMappedType( - source, - target, - constraintType - )) { - return; - } - } - // Infer from the members of source and target only if the two types are possibly related - if (!typesDefinitelyUnrelated(source, target)) { - inferFromProperties(source, target); - inferFromSignatures(source, target, SignatureKind.Call); - inferFromSignatures( - source, - target, - SignatureKind.Construct - ); - inferFromIndexTypes(source, target); - } - } - - function inferFromProperties(source: Type, target: Type) { - if (isArrayType(source) || isTupleType(source)) { - if (isTupleType(target)) { - const sourceLength = isTupleType(source) - ? getLengthOfTupleType(source) - : 0; - const targetLength = getLengthOfTupleType(target); - const sourceRestType = isTupleType(source) - ? getRestTypeOfTupleType(source) - : getElementTypeOfArrayType(source); - const targetRestType = getRestTypeOfTupleType(target); - const fixedLength = - targetLength < sourceLength || sourceRestType - ? targetLength - : sourceLength; - for (let i = 0; i < fixedLength; i++) { - inferFromTypes( - i < sourceLength - ? getTypeArguments( source) - [i] - : sourceRestType!, - getTypeArguments(target)[i] - ); - } - if (targetRestType) { - const types = fixedLength < sourceLength - ? getTypeArguments( source) - .slice(fixedLength, sourceLength) - : []; - if (sourceRestType) { - types.push(sourceRestType); - } - if (types.length) { - inferFromTypes( - getUnionType(types), - targetRestType - ); - } - } - return; - } - if (isArrayType(target)) { - inferFromIndexTypes(source, target); - return; - } - } - const properties = getPropertiesOfObjectType(target); - for (const targetProp of properties) { - const sourceProp = getPropertyOfType( - source, - targetProp.escapedName - ); - if (sourceProp) { - inferFromTypes( - getTypeOfSymbol(sourceProp), - getTypeOfSymbol(targetProp) - ); - } - } - } - - function inferFromSignatures( - source: Type, - target: Type, - kind: SignatureKind - ) { - const sourceSignatures = getSignaturesOfType(source, kind); - const targetSignatures = getSignaturesOfType(target, kind); - const sourceLen = sourceSignatures.length; - const targetLen = targetSignatures.length; - const len = sourceLen < targetLen ? sourceLen : targetLen; - const skipParameters = - !!(getObjectFlags(source) & ObjectFlags.NonInferrableType); - for (let i = 0; i < len; i++) { - inferFromSignature( - getBaseSignature(sourceSignatures[sourceLen - len - + i]), - getBaseSignature(targetSignatures[targetLen - len - + i]), - skipParameters - ); - } - } - - function inferFromSignature( - source: Signature, - target: Signature, - skipParameters: boolean - ) { - if (!skipParameters) { - const saveBivariant = bivariant; - const kind = target.declaration - ? target.declaration.kind - : SyntaxKind.Unknown; - // Once we descend into a bivariant signature we remain bivariant for all nested inferences - bivariant = bivariant - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.MethodSignature - || kind === SyntaxKind.Constructor; - applyToParameterTypes( - source, - target, - inferFromContravariantTypes - ); - bivariant = saveBivariant; - } - applyToReturnTypes(source, target, inferFromTypes); - } - - function inferFromIndexTypes(source: Type, target: Type) { - const targetStringIndexType = getIndexTypeOfType( - target, - IndexKind.String - ); - if (targetStringIndexType) { - const sourceIndexType = - getIndexTypeOfType(source, IndexKind.String) - || getImplicitIndexTypeOfType( - source, - IndexKind.String - ); - if (sourceIndexType) { - inferFromTypes(sourceIndexType, targetStringIndexType); - } - } - const targetNumberIndexType = getIndexTypeOfType( - target, - IndexKind.Number - ); - if (targetNumberIndexType) { - const sourceIndexType = - getIndexTypeOfType(source, IndexKind.Number) - || getIndexTypeOfType(source, IndexKind.String) - || getImplicitIndexTypeOfType( - source, - IndexKind.Number - ); - if (sourceIndexType) { - inferFromTypes(sourceIndexType, targetNumberIndexType); - } - } - } - } - - function isTypeOrBaseIdenticalTo(s: Type, t: Type) { - return isTypeIdenticalTo(s, t) - || !!(s.flags - & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) - && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t); - } - - function isTypeCloselyMatchedBy(s: Type, t: Type) { - return !!(s.flags & TypeFlags.Object && t.flags & TypeFlags.Object - && s.symbol && s.symbol === t.symbol - || s.aliasSymbol && s.aliasTypeArguments - && s.aliasSymbol === t.aliasSymbol); - } - - function hasPrimitiveConstraint(type: TypeParameter): boolean { - const constraint = getConstraintOfTypeParameter(type); - return !!constraint - && maybeTypeOfKind( - constraint.flags & TypeFlags.Conditional - ? getDefaultConstraintOfConditionalType(constraint as ConditionalType) - : constraint, - TypeFlags.Primitive | TypeFlags.Index - ); - } - - function isObjectLiteralType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.ObjectLiteral); - } - - function isObjectOrArrayLiteralType(type: Type) { - return !!(getObjectFlags(type) - & (ObjectFlags.ObjectLiteral | ObjectFlags.ArrayLiteral)); - } - - function unionObjectAndArrayLiteralCandidates(candidates: - Type[]): Type[] - { - if (candidates.length > 1) { - const objectLiterals = filter( - candidates, - isObjectOrArrayLiteralType - ); - if (objectLiterals.length) { - const literalsType = getUnionType( - objectLiterals, - UnionReduction.Subtype - ); - return concatenate( - filter( - candidates, - t => !isObjectOrArrayLiteralType(t) - ), - [literalsType] - ); - } - } - return candidates; - } - - function getContravariantInference(inference: InferenceInfo) { - return inference.priority! - & InferencePriority.PriorityImpliesCombination - ? getIntersectionType(inference.contraCandidates!) - : getCommonSubtype(inference.contraCandidates!); - } - - function getCovariantInference( - inference: InferenceInfo, - signature: Signature - ) { - // Extract all object and array literal types and replace them with a single widened and normalized type. - const candidates = - unionObjectAndArrayLiteralCandidates(inference.candidates!); - // We widen inferred literal types if - // all inferences were made to top-level occurrences of the type parameter, and - // the type parameter has no constraint or its constraint includes no primitive or literal types, and - // the type parameter was fixed during inference or does not occur at top-level in the return type. - const primitiveConstraint = - hasPrimitiveConstraint(inference.typeParameter); - const widenLiteralTypes = !primitiveConstraint - && inference.topLevel - && (inference.isFixed - || !isTypeParameterAtTopLevel( - getReturnTypeOfSignature(signature), - inference.typeParameter - )); - const baseCandidates = primitiveConstraint - ? sameMap(candidates, getRegularTypeOfLiteralType) - : widenLiteralTypes - ? sameMap(candidates, getWidenedLiteralType) - : candidates; - // If all inferences were made from a position that implies a combined result, infer a union type. - // Otherwise, infer a common supertype. - const unwidenedType = - inference.priority! - & InferencePriority.PriorityImpliesCombination - ? getUnionType(baseCandidates, UnionReduction.Subtype) - : getCommonSupertype(baseCandidates); - return getWidenedType(unwidenedType); - } - - function getInferredType( - context: InferenceContext, - index: number - ): Type { - const inference = context.inferences[index]; - if (!inference.inferredType) { - let inferredType: Type | undefined; - const signature = context.signature; - if (signature) { - const inferredCovariantType = inference.candidates - ? getCovariantInference(inference, signature) - : undefined; - if (inference.contraCandidates) { - const inferredContravariantType = - getContravariantInference(inference); - // If we have both co- and contra-variant inferences, we prefer the contra-variant inference - // unless the co-variant inference is a subtype and not 'never'. - inferredType = inferredCovariantType - && !(inferredCovariantType.flags & TypeFlags.Never) - && isTypeSubtypeOf( - inferredCovariantType, - inferredContravariantType - ) - ? inferredCovariantType - : inferredContravariantType; - } else if (inferredCovariantType) { - inferredType = inferredCovariantType; - } else if (context.flags & InferenceFlags.NoDefault) { - // We use silentNeverType as the wildcard that signals no inferences. - inferredType = silentNeverType; - } else { - // Infer either the default or the empty object type when no inferences were - // made. It is important to remember that in this case, inference still - // succeeds, meaning there is no error for not having inference candidates. An - // inference error only occurs when there are *conflicting* candidates, i.e. - // candidates with no common supertype. - const defaultType = - getDefaultFromTypeParameter(inference - .typeParameter); - if (defaultType) { - // Instantiate the default type. Any forward reference to a type - // parameter should be instantiated to the empty object type. - inferredType = instantiateType( - defaultType, - combineTypeMappers( - createBackreferenceMapper(context, index), - context.nonFixingMapper - ) - ); - } - } - } else { - inferredType = getTypeFromInference(inference); - } - - inference.inferredType = inferredType - || getDefaultTypeArgumentType(!!(context.flags - & InferenceFlags.AnyDefault)); - - const constraint = - getConstraintOfTypeParameter(inference.typeParameter); - if (constraint) { - const instantiatedConstraint = instantiateType( - constraint, - context.nonFixingMapper - ); - if (!inferredType - || !context.compareTypes( - inferredType, - getTypeWithThisArgument( - instantiatedConstraint, - inferredType - ) - )) - { - inference - .inferredType = inferredType = instantiatedConstraint; - } - } - } - - return inference.inferredType; - } - - function getDefaultTypeArgumentType(isInJavaScriptFile: - boolean): Type - { - return isInJavaScriptFile ? anyType : unknownType; - } - - function getInferredTypes(context: InferenceContext): Type[] { - const result: Type[] = []; - for (let i = 0; i < context.inferences.length; i++) { - result.push(getInferredType(context, i)); - } - return result; - } - - // EXPRESSION TYPE CHECKING - - function getCannotFindNameDiagnosticForName(node: - Identifier): DiagnosticMessage - { - switch (node.escapedText) { - case 'document': - case 'console': - return Diagnostics - .Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_include_dom; - case '$': - return compilerOptions.types - ? Diagnostics - .Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig - : Diagnostics - .Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery; - case 'describe': - case 'suite': - case 'it': - case 'test': - return compilerOptions.types - ? Diagnostics - .Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig - : Diagnostics - .Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha; - case 'process': - case 'require': - case 'Buffer': - case 'module': - return compilerOptions.types - ? Diagnostics - .Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig - : Diagnostics - .Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode; - case 'Map': - case 'Set': - case 'Promise': - case 'Symbol': - case 'WeakMap': - case 'WeakSet': - case 'Iterator': - case 'AsyncIterator': - return Diagnostics - .Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later; - default: - if (node.parent.kind - === SyntaxKind.ShorthandPropertyAssignment) - { - return Diagnostics - .No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer; - } else { - return Diagnostics.Cannot_find_name_0; - } - } - } - - function getResolvedSymbol(node: Identifier): Symbol { - const links = getNodeLinks(node); - if (!links.resolvedSymbol) { - links.resolvedSymbol = !nodeIsMissing(node) - && resolveName( - node, - node.escapedText, - SymbolFlags.Value | SymbolFlags.ExportValue, - getCannotFindNameDiagnosticForName(node), - node, - !isWriteOnlyAccess(node), - /*excludeGlobals*/ false, - Diagnostics.Cannot_find_name_0_Did_you_mean_1 - ) || unknownSymbol; - } - return links.resolvedSymbol; - } - - function isInTypeQuery(node: Node): boolean { - // TypeScript 1.0 spec (April 2014): 3.6.3 - // A type query consists of the keyword typeof followed by an expression. - // The expression is restricted to a single identifier or a sequence of identifiers separated by periods - return !!findAncestor( - node, - n => n.kind === SyntaxKind.TypeQuery - ? true - : n.kind === SyntaxKind.Identifier - || n.kind === SyntaxKind.QualifiedName - ? false - : 'quit' - ); - } - - // Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers - // separated by dots). The key consists of the id of the symbol referenced by the - // leftmost identifier followed by zero or more property names separated by dots. - // The result is undefined if the reference isn't a dotted name. We prefix nodes - // occurring in an apparent type position with '@' because the control flow type - // of such nodes may be based on the apparent type instead of the declared type. - function getFlowCacheKey( - node: Node, - declaredType: Type, - initialType: Type, - flowContainer: Node | undefined - ): string | undefined { - switch (node.kind) { - case SyntaxKind.Identifier: - const symbol = getResolvedSymbol( node); - return symbol !== unknownSymbol - ? `${flowContainer ? getNodeId(flowContainer) : '-1' - }|${getTypeId(declaredType)}|${ - getTypeId(initialType)}|${ - isConstraintPosition(node) ? '@' : ''}${ - getSymbolId(symbol)}` - : undefined; - case SyntaxKind.ThisKeyword: - return '0'; - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - return getFlowCacheKey( - ( node) - .expression, - declaredType, - initialType, - flowContainer - ); - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - const propName = - getAccessedPropertyName( node); - if (propName !== undefined) { - const key = getFlowCacheKey( - ( node).expression, - declaredType, - initialType, - flowContainer - ); - return key && key + '.' + propName; - } - } - return undefined; - } - - function isMatchingReference(source: Node, target: Node): boolean { - switch (target.kind) { - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.NonNullExpression: - return isMatchingReference( - source, - (target as NonNullExpression | ParenthesizedExpression) - .expression - ); - } - switch (source.kind) { - case SyntaxKind.Identifier: - return target.kind === SyntaxKind.Identifier - && getResolvedSymbol( source) - === getResolvedSymbol( target) - || (target.kind === SyntaxKind.VariableDeclaration - || target.kind === SyntaxKind.BindingElement) - && getExportSymbolOfValueSymbolIfExported(getResolvedSymbol( source)) - === getSymbolOfNode(target); - case SyntaxKind.ThisKeyword: - return target.kind === SyntaxKind.ThisKeyword; - case SyntaxKind.SuperKeyword: - return target.kind === SyntaxKind.SuperKeyword; - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - return isMatchingReference( - (source as NonNullExpression | ParenthesizedExpression) - .expression, - target - ); - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return isAccessExpression(target) - && getAccessedPropertyName( source) - === getAccessedPropertyName(target) - && isMatchingReference( - ( source).expression, - target.expression - ); - } - return false; - } - - function getAccessedPropertyName(access: AccessExpression): __String - | undefined - { - return access.kind === SyntaxKind.PropertyAccessExpression - ? access.name.escapedText - : isStringOrNumericLiteralLike(access.argumentExpression) - ? escapeLeadingUnderscores(access.argumentExpression.text) - : undefined; - } - - function containsMatchingReference(source: Node, target: Node) { - while (isAccessExpression(source)) { - source = source.expression; - if (isMatchingReference(source, target)) { - return true; - } - } - return false; - } - - function optionalChainContainsReference(source: Node, target: Node) { - while (isOptionalChain(source)) { - source = source.expression; - if (isMatchingReference(source, target)) { - return true; - } - } - return false; - } - - // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared - // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property - // a possible discriminant if its type differs in the constituents of containing union type, and if every - // choice is a unit type or a union of unit types. - function containsMatchingReferenceDiscriminant( - source: Node, - target: Node - ) { - let name; - return isAccessExpression(target) - && containsMatchingReference(source, target.expression) - && (name = getAccessedPropertyName(target)) !== undefined - && isDiscriminantProperty( - getDeclaredTypeOfReference(target.expression), - name - ); - } - - function getDeclaredTypeOfReference(expr: Node): Type | undefined { - if (expr.kind === SyntaxKind.Identifier) { - return getTypeOfSymbol(getResolvedSymbol( expr)); - } - if (isAccessExpression(expr)) { - const type = getDeclaredTypeOfReference(expr.expression); - if (type) { - const propName = getAccessedPropertyName(expr); - return propName !== undefined - ? getTypeOfPropertyOfType(type, propName) - : undefined; - } - } - return undefined; - } - - function isDiscriminantProperty( - type: Type | undefined, - name: __String - ) { - if (type && type.flags & TypeFlags.Union) { - const prop = getUnionOrIntersectionProperty( - type, - name - ); - if (prop - && getCheckFlags(prop) & CheckFlags.SyntheticProperty) - { - if (( prop).isDiscriminantProperty - === undefined) - { - ( prop) - .isDiscriminantProperty = (( prop) - .checkFlags & CheckFlags.Discriminant) - === CheckFlags.Discriminant - && !maybeTypeOfKind( - getTypeOfSymbol(prop), - TypeFlags.Instantiable - ); - } - return !!( prop).isDiscriminantProperty; - } - } - return false; - } - - function isSyntheticThisPropertyAccess(expr: Node) { - return isAccessExpression(expr) - && expr.expression.kind === SyntaxKind.ThisKeyword - && !!(expr.expression.flags & NodeFlags.Synthesized); - } - - function findDiscriminantProperties( - sourceProperties: Symbol[], - target: Type - ): Symbol[] | undefined { - let result: Symbol[] | undefined; - for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty( - target, - sourceProperty.escapedName - )) { - if (result) { - result.push(sourceProperty); - continue; - } - result = [sourceProperty]; - } - } - return result; - } - - function isOrContainsMatchingReference(source: Node, target: Node) { - return isMatchingReference(source, target) - || containsMatchingReference(source, target); - } - - function hasMatchingArgument( - callExpression: CallExpression, - reference: Node - ) { - if (callExpression.arguments) { - for (const argument of callExpression.arguments) { - if (isOrContainsMatchingReference(reference, argument)) { - return true; - } - } - } - if (callExpression.expression.kind - === SyntaxKind.PropertyAccessExpression - && isOrContainsMatchingReference( - reference, - ( callExpression.expression) - .expression - )) - { - return true; - } - return false; - } - - function getFlowNodeId(flow: FlowNode): number { - if (!flow.id || flow.id < 0) { - flow.id = nextFlowId; - nextFlowId++; - } - return flow.id; - } - - function typeMaybeAssignableTo(source: Type, target: Type) { - if (!(source.flags & TypeFlags.Union)) { - return isTypeAssignableTo(source, target); - } - for (const t of ( source).types) { - if (isTypeAssignableTo(t, target)) { - return true; - } - } - return false; - } - - // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. - // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, - // we remove type string. - function getAssignmentReducedType( - declaredType: UnionType, - assignedType: Type - ) { - if (declaredType !== assignedType) { - if (assignedType.flags & TypeFlags.Never) { - return assignedType; - } - let reducedType = filterType( - declaredType, - t => typeMaybeAssignableTo(assignedType, t) - ); - if (assignedType.flags & TypeFlags.BooleanLiteral - && isFreshLiteralType(assignedType)) - { - reducedType = mapType( - reducedType, - getFreshTypeOfLiteralType - ); // Ensure that if the assignment is a fresh type, that we narrow to fresh types - } - // Our crude heuristic produces an invalid result in some cases: see GH#26130. - // For now, when that happens, we give up and don't narrow at all. (This also - // means we'll never narrow for erroneous assignments where the assigned type - // is not assignable to the declared type.) - if (isTypeAssignableTo(assignedType, reducedType)) { - return reducedType; - } - } - return declaredType; - } - - function getTypeFactsOfTypes(types: Type[]): TypeFacts { - let result: TypeFacts = TypeFacts.None; - for (const t of types) { - result |= getTypeFacts(t); - } - return result; - } - - function isFunctionObjectType(type: ObjectType): boolean { - // We do a quick check for a "bind" property before performing the more expensive subtype - // check. This gives us a quicker out in the common case where an object type is not a function. - const resolved = resolveStructuredTypeMembers(type); - return !!(resolved.callSignatures.length - || resolved.constructSignatures.length - || resolved.members.get('bind' as __String) - && isTypeSubtypeOf(type, globalFunctionType)); - } - - function getTypeFacts(type: Type): TypeFacts { - const flags = type.flags; - if (flags & TypeFlags.String) { - return strictNullChecks - ? TypeFacts.StringStrictFacts - : TypeFacts.StringFacts; - } - if (flags & TypeFlags.StringLiteral) { - const isEmpty = ( type).value === ''; - return strictNullChecks - ? isEmpty - ? TypeFacts.EmptyStringStrictFacts - : TypeFacts.NonEmptyStringStrictFacts - : isEmpty ? TypeFacts.EmptyStringFacts - : TypeFacts.NonEmptyStringFacts; - } - if (flags & (TypeFlags.Number | TypeFlags.Enum)) { - return strictNullChecks - ? TypeFacts.NumberStrictFacts - : TypeFacts.NumberFacts; - } - if (flags & TypeFlags.NumberLiteral) { - const isZero = ( type).value === 0; - return strictNullChecks - ? isZero - ? TypeFacts.ZeroNumberStrictFacts - : TypeFacts.NonZeroNumberStrictFacts - : isZero ? TypeFacts.ZeroNumberFacts - : TypeFacts.NonZeroNumberFacts; - } - if (flags & TypeFlags.BigInt) { - return strictNullChecks - ? TypeFacts.BigIntStrictFacts - : TypeFacts.BigIntFacts; - } - if (flags & TypeFlags.BigIntLiteral) { - const isZero = isZeroBigInt( type); - return strictNullChecks - ? isZero - ? TypeFacts.ZeroBigIntStrictFacts - : TypeFacts.NonZeroBigIntStrictFacts - : isZero ? TypeFacts.ZeroBigIntFacts - : TypeFacts.NonZeroBigIntFacts; - } - if (flags & TypeFlags.Boolean) { - return strictNullChecks - ? TypeFacts.BooleanStrictFacts - : TypeFacts.BooleanFacts; - } - if (flags & TypeFlags.BooleanLike) { - return strictNullChecks - ? (type === falseType || type === regularFalseType) - ? TypeFacts.FalseStrictFacts - : TypeFacts.TrueStrictFacts - : (type === falseType || type === regularFalseType) - ? TypeFacts.FalseFacts - : TypeFacts.TrueFacts; - } - if (flags & TypeFlags.Object) { - return getObjectFlags(type) & ObjectFlags.Anonymous - && isEmptyObjectType( type) - ? strictNullChecks - ? TypeFacts.EmptyObjectStrictFacts - : TypeFacts.EmptyObjectFacts - : isFunctionObjectType( type) - ? strictNullChecks ? TypeFacts.FunctionStrictFacts - : TypeFacts.FunctionFacts - : strictNullChecks ? TypeFacts.ObjectStrictFacts - : TypeFacts.ObjectFacts; - } - if (flags & (TypeFlags.Void | TypeFlags.Undefined)) { - return TypeFacts.UndefinedFacts; - } - if (flags & TypeFlags.Null) { - return TypeFacts.NullFacts; - } - if (flags & TypeFlags.ESSymbolLike) { - return strictNullChecks - ? TypeFacts.SymbolStrictFacts - : TypeFacts.SymbolFacts; - } - if (flags & TypeFlags.NonPrimitive) { - return strictNullChecks - ? TypeFacts.ObjectStrictFacts - : TypeFacts.ObjectFacts; - } - if (flags & TypeFlags.Instantiable) { - return getTypeFacts(getBaseConstraintOfType(type) - || unknownType); - } - if (flags & TypeFlags.UnionOrIntersection) { - return getTypeFactsOfTypes(( type) - .types); - } - return TypeFacts.All; - } - - function getTypeWithFacts(type: Type, include: TypeFacts) { - return filterType(type, t => (getTypeFacts(t) & include) !== 0); - } - - function getTypeWithDefault( - type: Type, - defaultExpression: Expression - ) { - if (defaultExpression) { - const defaultType = getTypeOfExpression(defaultExpression); - return getUnionType([getTypeWithFacts( - type, - TypeFacts.NEUndefined - ), defaultType]); - } - return type; - } - - function getTypeOfDestructuredProperty( - type: Type, - name: PropertyName - ) { - const nameType = getLiteralTypeFromPropertyName(name); - if (!isTypeUsableAsPropertyName(nameType)) return errorType; - const text = getPropertyNameFromType(nameType); - return getConstraintForLocation( - getTypeOfPropertyOfType(type, text), - name - ) - || isNumericLiteralName(text) - && getIndexTypeOfType(type, IndexKind.Number) - || getIndexTypeOfType(type, IndexKind.String) - || errorType; - } - - function getTypeOfDestructuredArrayElement(type: Type, index: number) { - return everyType(type, isTupleLikeType) - && getTupleElementType(type, index) - || checkIteratedTypeOrElementType( - IterationUse.Destructuring, - type, - undefinedType, /*errorNode*/ - undefined - ) - || errorType; - } - - function getTypeOfDestructuredSpreadExpression(type: Type) { - return createArrayType(checkIteratedTypeOrElementType( - IterationUse.Destructuring, - type, - undefinedType, /*errorNode*/ - undefined - ) || errorType); - } - - function getAssignedTypeOfBinaryExpression(node: - BinaryExpression): Type - { - const isDestructuringDefaultAssignment = - node.parent.kind === SyntaxKind.ArrayLiteralExpression - && isDestructuringAssignmentTarget(node.parent) - || node.parent.kind === SyntaxKind.PropertyAssignment - && isDestructuringAssignmentTarget(node.parent.parent); - return isDestructuringDefaultAssignment - ? getTypeWithDefault(getAssignedType(node), node.right) - : getTypeOfExpression(node.right); - } - - function isDestructuringAssignmentTarget(parent: Node) { - return parent.parent.kind === SyntaxKind.BinaryExpression - && (parent.parent as BinaryExpression).left === parent - || parent.parent.kind === SyntaxKind.ForOfStatement - && (parent.parent as ForOfStatement).initializer === parent; - } - - function getAssignedTypeOfArrayLiteralElement( - node: ArrayLiteralExpression, - element: Expression - ): Type { - return getTypeOfDestructuredArrayElement( - getAssignedType(node), - node.elements.indexOf(element) - ); - } - - function getAssignedTypeOfSpreadExpression(node: SpreadElement): Type { - return getTypeOfDestructuredSpreadExpression(getAssignedType( node - .parent)); - } - - function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment - | ShorthandPropertyAssignment): Type - { - return getTypeOfDestructuredProperty( - getAssignedType(node.parent), - node.name - ); - } - - function getAssignedTypeOfShorthandPropertyAssignment(node: - ShorthandPropertyAssignment): Type - { - return getTypeWithDefault( - getAssignedTypeOfPropertyAssignment(node), - node.objectAssignmentInitializer! - ); - } - - function getAssignedType(node: Expression): Type { - const { parent } = node; - switch (parent.kind) { - case SyntaxKind.ForInStatement: - return stringType; - case SyntaxKind.ForOfStatement: - return checkRightHandSideOfForOf( - ( parent).expression, - ( parent).awaitModifier - ) || errorType; - case SyntaxKind.BinaryExpression: - return getAssignedTypeOfBinaryExpression( parent); - case SyntaxKind.DeleteExpression: - return undefinedType; - case SyntaxKind.ArrayLiteralExpression: - return getAssignedTypeOfArrayLiteralElement( - parent, - node - ); - case SyntaxKind.SpreadElement: - return getAssignedTypeOfSpreadExpression( parent); - case SyntaxKind.PropertyAssignment: - return getAssignedTypeOfPropertyAssignment( parent); - case SyntaxKind.ShorthandPropertyAssignment: - return getAssignedTypeOfShorthandPropertyAssignment( parent); - } - return errorType; - } - - function getInitialTypeOfBindingElement(node: BindingElement): Type { - const pattern = node.parent; - const parentType = - getInitialType( pattern - .parent); - const type = pattern.kind === SyntaxKind.ObjectBindingPattern - ? getTypeOfDestructuredProperty( - parentType, - node.propertyName || node.name - ) - : !node.dotDotDotToken - ? getTypeOfDestructuredArrayElement( - parentType, - pattern.elements.indexOf(node) - ) - : getTypeOfDestructuredSpreadExpression(parentType); - return getTypeWithDefault(type, node.initializer!); - } - - function getTypeOfInitializer(node: Expression) { - // Return the cached type if one is available. If the type of the variable was inferred - // from its initializer, we'll already have cached the type. Otherwise we compute it now - // without caching such that transient types are reflected. - const links = getNodeLinks(node); - return links.resolvedType || getTypeOfExpression(node); - } - - function getInitialTypeOfVariableDeclaration(node: - VariableDeclaration) - { - if (node.initializer) { - return getTypeOfInitializer(node.initializer); - } - if (node.parent.parent.kind === SyntaxKind.ForInStatement) { - return stringType; - } - if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { - return checkRightHandSideOfForOf( - node.parent.parent.expression, - node.parent.parent.awaitModifier - ) || errorType; - } - return errorType; - } - - function getInitialType(node: VariableDeclaration | BindingElement) { - return node.kind === SyntaxKind.VariableDeclaration - ? getInitialTypeOfVariableDeclaration(node) - : getInitialTypeOfBindingElement(node); - } - - function isEmptyArrayAssignment(node: VariableDeclaration - | BindingElement | Expression) - { - return node.kind === SyntaxKind.VariableDeclaration - && ( node).initializer - && isEmptyArrayLiteral(( node) - .initializer!) - || node.kind !== SyntaxKind.BindingElement - && node.parent.kind === SyntaxKind.BinaryExpression - && isEmptyArrayLiteral(( node.parent).right); - } - - function getReferenceCandidate(node: Expression): Expression { - switch (node.kind) { - case SyntaxKind.ParenthesizedExpression: - return getReferenceCandidate(( node) - .expression); - case SyntaxKind.BinaryExpression: - switch (( node).operatorToken.kind) { - case SyntaxKind.EqualsToken: - return getReferenceCandidate(( node) - .left); - case SyntaxKind.CommaToken: - return getReferenceCandidate(( node) - .right); - } - } - return node; - } - - function getReferenceRoot(node: Node): Node { - const { parent } = node; - return parent.kind === SyntaxKind.ParenthesizedExpression - || parent.kind === SyntaxKind.BinaryExpression - && ( parent).operatorToken.kind - === SyntaxKind.EqualsToken - && ( parent).left === node - || parent.kind === SyntaxKind.BinaryExpression - && ( parent).operatorToken.kind - === SyntaxKind.CommaToken - && ( parent).right === node - ? getReferenceRoot(parent) - : node; - } - - function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { - if (clause.kind === SyntaxKind.CaseClause) { - return getRegularTypeOfLiteralType(getTypeOfExpression(clause - .expression)); - } - return neverType; - } - - function getSwitchClauseTypes(switchStatement: - SwitchStatement): Type[] - { - const links = getNodeLinks(switchStatement); - if (!links.switchTypes) { - links.switchTypes = []; - for (const clause of switchStatement.caseBlock.clauses) { - links.switchTypes.push(getTypeOfSwitchClause(clause)); - } - } - return links.switchTypes; - } - - // Get the types from all cases in a switch on `typeof`. An - // `undefined` element denotes an explicit `default` clause. - function getSwitchClauseTypeOfWitnesses(switchStatement: - SwitchStatement): (string | undefined)[] - { - const witnesses: (string | undefined)[] = []; - for (const clause of switchStatement.caseBlock.clauses) { - if (clause.kind === SyntaxKind.CaseClause) { - if (isStringLiteralLike(clause.expression)) { - witnesses.push(clause.expression.text); - continue; - } - return emptyArray; - } - witnesses.push(/*explicitDefaultStatement*/ undefined); - } - return witnesses; - } - - function eachTypeContainedIn(source: Type, types: Type[]) { - return source.flags & TypeFlags.Union - ? !forEach( - ( source).types, - t => !contains(types, t) - ) - : contains(types, source); - } - - function isTypeSubsetOf(source: Type, target: Type) { - return source === target || target.flags & TypeFlags.Union - && isTypeSubsetOfUnion(source, target); - } - - function isTypeSubsetOfUnion(source: Type, target: UnionType) { - if (source.flags & TypeFlags.Union) { - for (const t of ( source).types) { - if (!containsType(target.types, t)) { - return false; - } - } - return true; - } - if (source.flags & TypeFlags.EnumLiteral - && getBaseTypeOfEnumLiteralType( source) - === target) - { - return true; - } - return containsType(target.types, source); - } - - function forEachType(type: Type, f: (t: Type) => T | undefined): T - | undefined - { - return type.flags & TypeFlags.Union - ? forEach(( type).types, f) - : f(type); - } - - function everyType(type: Type, f: (t: Type) => boolean): boolean { - return type.flags & TypeFlags.Union - ? every(( type).types, f) - : f(type); - } - - function filterType(type: Type, f: (t: Type) => boolean): Type { - if (type.flags & TypeFlags.Union) { - const types = ( type).types; - const filtered = filter(types, f); - return filtered === types - ? type - : getUnionTypeFromSortedList( - filtered, - ( type).objectFlags - ); - } - return f(type) ? type : neverType; - } - - function countTypes(type: Type) { - return type.flags & TypeFlags.Union - ? (type as UnionType).types.length - : 1; - } - - // Apply a mapping function to a type and return the resulting type. If the source type - // is a union type, the mapping function is applied to each constituent type and a union - // of the resulting types is returned. - function mapType( - type: Type, - mapper: (t: Type) => Type, - noReductions?: boolean - ): Type; - function mapType( - type: Type, - mapper: (t: Type) => Type | undefined, - noReductions?: boolean - ): Type | undefined; - function mapType( - type: Type, - mapper: (t: Type) => Type | undefined, - noReductions?: boolean - ): Type | undefined { - if (type.flags & TypeFlags.Never) { - return type; - } - if (!(type.flags & TypeFlags.Union)) { - return mapper(type); - } - let mappedTypes: Type[] | undefined; - for (const t of ( type).types) { - const mapped = mapper(t); - if (mapped) { - if (!mappedTypes) { - mappedTypes = [mapped]; - } else { - mappedTypes.push(mapped); - } - } - } - return mappedTypes - && getUnionType( - mappedTypes, - noReductions ? UnionReduction.None : UnionReduction.Literal - ); - } - - function extractTypesOfKind(type: Type, kind: TypeFlags) { - return filterType(type, t => (t.flags & kind) !== 0); - } - - // Return a new type in which occurrences of the string and number primitive types in - // typeWithPrimitives have been replaced with occurrences of string literals and numeric - // literals in typeWithLiterals, respectively. - function replacePrimitivesWithLiterals( - typeWithPrimitives: Type, - typeWithLiterals: Type - ) { - if (isTypeSubsetOf(stringType, typeWithPrimitives) - && maybeTypeOfKind(typeWithLiterals, TypeFlags.StringLiteral) - || isTypeSubsetOf(numberType, typeWithPrimitives) - && maybeTypeOfKind(typeWithLiterals, TypeFlags.NumberLiteral) - || isTypeSubsetOf(bigintType, typeWithPrimitives) - && maybeTypeOfKind(typeWithLiterals, TypeFlags.BigIntLiteral)) - { - return mapType( - typeWithPrimitives, - t => t.flags & TypeFlags.String - ? extractTypesOfKind( - typeWithLiterals, - TypeFlags.String | TypeFlags.StringLiteral - ) - : t.flags & TypeFlags.Number - ? extractTypesOfKind( - typeWithLiterals, - TypeFlags.Number | TypeFlags.NumberLiteral - ) - : t.flags & TypeFlags.BigInt - ? extractTypesOfKind( - typeWithLiterals, - TypeFlags.BigInt | TypeFlags.BigIntLiteral - ) - : t - ); - } - return typeWithPrimitives; - } - - function isIncomplete(flowType: FlowType) { - return flowType.flags === 0; - } - - function getTypeFromFlowType(flowType: FlowType) { - return flowType.flags === 0 - ? ( flowType).type - : flowType; - } - - function createFlowType(type: Type, incomplete: boolean): FlowType { - return incomplete ? { flags: 0, type } : type; - } - - // An evolving array type tracks the element types that have so far been seen in an - // 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving - // array types are ultimately converted into manifest array types (using getFinalArrayType) - // and never escape the getFlowTypeOfReference function. - function createEvolvingArrayType(elementType: - Type): EvolvingArrayType - { - const result = - createObjectType(ObjectFlags - .EvolvingArray); - result.elementType = elementType; - return result; - } - - function getEvolvingArrayType(elementType: Type): EvolvingArrayType { - return evolvingArrayTypes[elementType.id] - || (evolvingArrayTypes[elementType - .id] = createEvolvingArrayType(elementType)); - } - - // When adding evolving array element types we do not perform subtype reduction. Instead, - // we defer subtype reduction until the evolving array type is finalized into a manifest - // array type. - function addEvolvingArrayElementType( - evolvingArrayType: EvolvingArrayType, - node: Expression - ): EvolvingArrayType { - const elementType = - getBaseTypeOfLiteralType(getContextFreeTypeOfExpression(node)); - return isTypeSubsetOf(elementType, evolvingArrayType.elementType) - ? evolvingArrayType - : getEvolvingArrayType(getUnionType([evolvingArrayType - .elementType, elementType])); - } - - function createFinalArrayType(elementType: Type) { - return elementType.flags & TypeFlags.Never - ? autoArrayType - : createArrayType(elementType.flags & TypeFlags.Union - ? getUnionType( - ( elementType).types, - UnionReduction.Subtype - ) - : elementType); - } - - // We perform subtype reduction upon obtaining the final array type from an evolving array type. - function getFinalArrayType(evolvingArrayType: - EvolvingArrayType): Type - { - return evolvingArrayType.finalArrayType - || (evolvingArrayType - .finalArrayType = createFinalArrayType(evolvingArrayType - .elementType)); - } - - function finalizeEvolvingArrayType(type: Type): Type { - return getObjectFlags(type) & ObjectFlags.EvolvingArray - ? getFinalArrayType( type) - : type; - } - - function getElementTypeOfEvolvingArrayType(type: Type) { - return getObjectFlags(type) & ObjectFlags.EvolvingArray - ? ( type).elementType - : neverType; - } - - function isEvolvingArrayTypeList(types: Type[]) { - let hasEvolvingArrayType = false; - for (const t of types) { - if (!(t.flags & TypeFlags.Never)) { - if (!(getObjectFlags(t) & ObjectFlags.EvolvingArray)) { - return false; - } - hasEvolvingArrayType = true; - } - } - return hasEvolvingArrayType; - } - - // At flow control branch or loop junctions, if the type along every antecedent code path - // is an evolving array type, we construct a combined evolving array type. Otherwise we - // finalize all evolving array types. - function getUnionOrEvolvingArrayType( - types: Type[], - subtypeReduction: UnionReduction - ) { - return isEvolvingArrayTypeList(types) - ? getEvolvingArrayType(getUnionType(map( - types, - getElementTypeOfEvolvingArrayType - ))) - : getUnionType( - sameMap(types, finalizeEvolvingArrayType), - subtypeReduction - ); - } - - // Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or - // 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type. - function isEvolvingArrayOperationTarget(node: Node) { - const root = getReferenceRoot(node); - const parent = root.parent; - const isLengthPushOrUnshift = isPropertyAccessExpression(parent) - && ( - parent.name.escapedText === 'length' - || parent.parent.kind === SyntaxKind.CallExpression - && isIdentifier(parent.name) - && isPushOrUnshiftIdentifier(parent.name) - ); - const isElementAssignment = - parent.kind === SyntaxKind.ElementAccessExpression - && ( parent).expression === root - && parent.parent.kind === SyntaxKind.BinaryExpression - && ( parent.parent).operatorToken.kind - === SyntaxKind.EqualsToken - && ( parent.parent).left === parent - && !isAssignmentTarget(parent.parent) - && isTypeAssignableToKind( - getTypeOfExpression(( parent) - .argumentExpression), - TypeFlags.NumberLike - ); - return isLengthPushOrUnshift || isElementAssignment; - } - - function isDeclarationWithExplicitTypeAnnotation(declaration: - Declaration | undefined) - { - return !!(declaration && ( - declaration.kind === SyntaxKind.VariableDeclaration - || declaration.kind === SyntaxKind.Parameter - || declaration.kind === SyntaxKind.PropertyDeclaration - || declaration.kind === SyntaxKind.PropertySignature - ) - && getEffectiveTypeAnnotationNode(declaration as VariableDeclaration - | ParameterDeclaration | PropertyDeclaration - | PropertySignature)); - } - - function getExplicitTypeOfSymbol( - symbol: Symbol, - diagnostic?: Diagnostic - ) { - if (symbol.flags - & (SymbolFlags.Function | SymbolFlags.Method - | SymbolFlags.Class | SymbolFlags.ValueModule)) - { - return getTypeOfSymbol(symbol); - } - if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { - if (isDeclarationWithExplicitTypeAnnotation(symbol - .valueDeclaration)) - { - return getTypeOfSymbol(symbol); - } - if (diagnostic && symbol.valueDeclaration) { - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - symbol.valueDeclaration, - Diagnostics._0_is_declared_here, - symbolToString(symbol) - ) - ); - } - } - } - - // We require the dotted function name in an assertion expression to be comprised of identifiers - // that reference function, method, class or value module symbols; or variable, property or - // parameter symbols with declarations that have explicit type annotations. Such references are - // resolvable with no possibility of triggering circularities in control flow analysis. - function getTypeOfDottedName( - node: Expression, - diagnostic: Diagnostic | undefined - ): Type | undefined { - if (!(node.flags & NodeFlags.InWithStatement)) { - switch (node.kind) { - case SyntaxKind.Identifier: - const symbol = - getExportSymbolOfValueSymbolIfExported(getResolvedSymbol( node)); - return getExplicitTypeOfSymbol( - symbol.flags & SymbolFlags.Alias - ? resolveAlias(symbol) - : symbol, - diagnostic - ); - case SyntaxKind.ThisKeyword: - return getExplicitThisType(node); - case SyntaxKind.PropertyAccessExpression: - const type = getTypeOfDottedName( - ( node).expression, - diagnostic - ); - const prop = type - && getPropertyOfType( - type, - ( node).name - .escapedText - ); - return prop - && getExplicitTypeOfSymbol(prop, diagnostic); - case SyntaxKind.ParenthesizedExpression: - return getTypeOfDottedName( - ( node).expression, - diagnostic - ); - } - } - } - - function getEffectsSignature(node: CallExpression) { - const links = getNodeLinks(node); - let signature = links.effectsSignature; - if (signature === undefined) { - // A call expression parented by an expression statement is a potential assertion. Other call - // expressions are potential type predicate function calls. In order to avoid triggering - // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call - // target expression of an assertion. - let funcType: Type | undefined; - if (node.parent.kind === SyntaxKind.ExpressionStatement) { - funcType = getTypeOfDottedName( - node.expression, /*diagnostic*/ - undefined - ); - } else if (node.expression.kind !== SyntaxKind.SuperKeyword) { - if (isOptionalChain(node)) { - funcType = checkNonNullType( - getOptionalExpressionType( - checkExpression(node.expression), - node.expression - ), - node.expression - ); - } else { - funcType = checkNonNullExpression(node.expression); - } - } - const signatures = getSignaturesOfType( - funcType && getApparentType(funcType) || unknownType, - SignatureKind.Call - ); - const candidate = - signatures.length === 1 && !signatures[0].typeParameters - ? signatures[0] - : some(signatures, hasTypePredicateOrNeverReturnType) - ? getResolvedSignature(node) - : undefined; - signature = links - .effectsSignature = candidate - && hasTypePredicateOrNeverReturnType(candidate) - ? candidate - : unknownSignature; - } - return signature === unknownSignature ? undefined : signature; - } - - function hasTypePredicateOrNeverReturnType(signature: Signature) { - return !!(getTypePredicateOfSignature(signature) - || signature.declaration - && (getReturnTypeFromAnnotation(signature.declaration) - || unknownType).flags & TypeFlags.Never); - } - - function getTypePredicateArgument( - predicate: TypePredicate, - callExpression: CallExpression - ) { - if (predicate.kind === TypePredicateKind.Identifier - || predicate.kind === TypePredicateKind.AssertsIdentifier) - { - return callExpression.arguments[predicate.parameterIndex]; - } - const invokedExpression = - skipParentheses(callExpression.expression); - return isAccessExpression(invokedExpression) - ? skipParentheses(invokedExpression.expression) - : undefined; - } - - function reportFlowControlError(node: Node) { - const block = findAncestor( - node, - isFunctionOrModuleBlock - ); - const sourceFile = getSourceFileOfNode(node); - const span = getSpanOfTokenAtPosition( - sourceFile, - block.statements.pos - ); - diagnostics - .add(createFileDiagnostic( - sourceFile, - span.start, - span.length, - Diagnostics - .The_containing_function_or_module_body_is_too_large_for_control_flow_analysis - )); - } - - function isReachableFlowNode(flow: FlowNode) { - const result = isReachableFlowNodeWorker( - flow, /*skipCacheCheck*/ - false - ); - lastFlowNode = flow; - lastFlowNodeReachable = result; - return result; - } - - function isUnlockedReachableFlowNode(flow: FlowNode) { - return !(flow.flags & FlowFlags.PreFinally - && ( flow).lock.locked) - && isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false); - } - - function isFalseExpression(expr: Expression): boolean { - const node = skipParentheses(expr); - return node.kind === SyntaxKind.FalseKeyword - || node.kind === SyntaxKind.BinaryExpression && ( - ( node).operatorToken.kind - === SyntaxKind.AmpersandAmpersandToken - && (isFalseExpression(( node).left) - || isFalseExpression(( node).right)) - || ( node).operatorToken.kind - === SyntaxKind.BarBarToken - && isFalseExpression(( node).left) - && isFalseExpression(( node).right) - ); - } - - function isReachableFlowNodeWorker( - flow: FlowNode, - noCacheCheck: boolean - ): boolean { - while (true) { - if (flow === lastFlowNode) { - return lastFlowNodeReachable; - } - const flags = flow.flags; - if (flags & FlowFlags.Shared) { - if (!noCacheCheck) { - const id = getFlowNodeId(flow); - const reachable = flowNodeReachable[id]; - return reachable !== undefined - ? reachable - : (flowNodeReachable - [id] = isReachableFlowNodeWorker( - flow, /*skipCacheCheck*/ - true - )); - } - noCacheCheck = false; - } - if (flags - & (FlowFlags.Assignment | FlowFlags.Condition - | FlowFlags.ArrayMutation | FlowFlags.PreFinally)) - { - flow = ( flow).antecedent; - } else if (flags & FlowFlags.Call) { - const signature = - getEffectsSignature(( flow).node); - if (signature) { - const predicate = - getTypePredicateOfSignature(signature); - if (predicate - && predicate.kind - === TypePredicateKind.AssertsIdentifier) - { - const predicateArgument = ( flow).node - .arguments[predicate.parameterIndex]; - if (predicateArgument - && isFalseExpression(predicateArgument)) - { - return false; - } - } - if (getReturnTypeOfSignature(signature).flags - & TypeFlags.Never) - { - return false; - } - } - flow = ( flow).antecedent; - } else if (flags & FlowFlags.BranchLabel) { - // A branching point is reachable if any branch is reachable. - return some( - ( flow).antecedents, - isUnlockedReachableFlowNode - ); - } else if (flags & FlowFlags.LoopLabel) { - // A loop is reachable if the control flow path that leads to the top is reachable. - flow = ( flow).antecedents![0]; - } else if (flags & FlowFlags.SwitchClause) { - // The control flow path representing an unmatched value in a switch statement with - // no default clause is unreachable if the switch statement is exhaustive. - if (( flow).clauseStart - === ( flow).clauseEnd - && isExhaustiveSwitchStatement(( flow) - .switchStatement)) - { - return false; - } - flow = ( flow).antecedent; - } else if (flags & FlowFlags.AfterFinally) { - // Cache is unreliable once we start locking nodes - lastFlowNode = undefined; - ( flow).locked = true; - const result = isReachableFlowNodeWorker( - ( flow) - .antecedent, /*skipCacheCheck*/ - false - ); - ( flow).locked = false; - return result; - } else { - return !(flags & FlowFlags.Unreachable); - } - } - } - - function getFlowTypeOfReference( - reference: Node, - declaredType: Type, - initialType = declaredType, - flowContainer?: Node, - couldBeUninitialized?: boolean - ) { - let key: string | undefined; - let keySet = false; - let flowDepth = 0; - if (flowAnalysisDisabled) { - return errorType; - } - if (!reference.flowNode || !couldBeUninitialized - && !(declaredType.flags & TypeFlags.Narrowable)) - { - return declaredType; - } - flowInvocationCount++; - const sharedFlowStart = sharedFlowCount; - const evolvedType = - getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode)); - sharedFlowCount = sharedFlowStart; - // When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation, - // we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations - // on empty arrays are possible without implicit any errors and new element types can be inferred without - // type mismatch errors. - const resultType = - getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray - && isEvolvingArrayOperationTarget(reference) - ? autoArrayType - : finalizeEvolvingArrayType(evolvedType); - if (resultType === unreachableNeverType || reference.parent - && reference.parent.kind === SyntaxKind.NonNullExpression - && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull) - .flags & TypeFlags.Never) - { - return declaredType; - } - return resultType; - - function getOrSetCacheKey() { - if (keySet) { - return key; - } - keySet = true; - return key = getFlowCacheKey( - reference, - declaredType, - initialType, - flowContainer - ); - } - - function getTypeAtFlowNode(flow: FlowNode): FlowType { - if (flowDepth === 2000) { - // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error - // and disable further control flow analysis in the containing function or module body. - flowAnalysisDisabled = true; - reportFlowControlError(reference); - return errorType; - } - flowDepth++; - while (true) { - const flags = flow.flags; - if (flags & FlowFlags.Shared) { - // We cache results of flow type resolution for shared nodes that were previously visited in - // the same getFlowTypeOfReference invocation. A node is considered shared when it is the - // antecedent of more than one node. - for (let i = sharedFlowStart; i < sharedFlowCount; - i++) - { - if (sharedFlowNodes[i] === flow) { - flowDepth--; - return sharedFlowTypes[i]; - } - } - } - let type: FlowType | undefined; - if (flags & FlowFlags.AfterFinally) { - // block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement - ( flow).locked = true; - type = getTypeAtFlowNode(( flow) - .antecedent); - ( flow).locked = false; - } else if (flags & FlowFlags.PreFinally) { - // locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel - // so here just redirect to antecedent - flow = ( flow).antecedent; - continue; - } else if (flags & FlowFlags.Assignment) { - type = getTypeAtFlowAssignment( flow); - if (!type) { - flow = ( flow).antecedent; - continue; - } - } else if (flags & FlowFlags.Call) { - type = getTypeAtFlowCall( flow); - if (!type) { - flow = ( flow).antecedent; - continue; - } - } else if (flags & FlowFlags.Condition) { - type = getTypeAtFlowCondition( flow); - } else if (flags & FlowFlags.SwitchClause) { - type = getTypeAtSwitchClause( flow); - } else if (flags & FlowFlags.Label) { - if (( flow).antecedents!.length === 1) { - flow = ( flow).antecedents![0]; - continue; - } - type = flags & FlowFlags.BranchLabel - ? getTypeAtFlowBranchLabel( flow) - : getTypeAtFlowLoopLabel( flow); - } else if (flags & FlowFlags.ArrayMutation) { - type = getTypeAtFlowArrayMutation( flow); - if (!type) { - flow = ( flow).antecedent; - continue; - } - } else if (flags & FlowFlags.Start) { - // Check if we should continue with the control flow of the containing function. - const container = ( flow).node; - if (container && container !== flowContainer - && reference.kind - !== SyntaxKind.PropertyAccessExpression - && reference.kind - !== SyntaxKind.ElementAccessExpression - && reference.kind !== SyntaxKind.ThisKeyword) - { - flow = container.flowNode!; - continue; - } - // At the top of the flow we have the initial type. - type = initialType; - } else { - // Unreachable code errors are reported in the binding phase. Here we - // simply return the non-auto declared type to reduce follow-on errors. - type = convertAutoToAny(declaredType); - } - if (flags & FlowFlags.Shared) { - // Record visited node and the associated type in the cache. - sharedFlowNodes[sharedFlowCount] = flow; - sharedFlowTypes[sharedFlowCount] = type; - sharedFlowCount++; - } - flowDepth--; - return type; - } - } - - function getInitialOrAssignedType(flow: FlowAssignment) { - const node = flow.node; - return getConstraintForLocation( - node.kind === SyntaxKind.VariableDeclaration - || node.kind === SyntaxKind.BindingElement - ? getInitialType( node) - : getAssignedType(node), - reference - ); - } - - function getTypeAtFlowAssignment(flow: FlowAssignment) { - const node = flow.node; - // Assignments only narrow the computed type if the declared type is a union type. Thus, we - // only need to evaluate the assigned type if the declared type is a union type. - if (isMatchingReference(reference, node)) { - if (!isReachableFlowNode(flow)) { - return unreachableNeverType; - } - if (getAssignmentTargetKind(node) - === AssignmentKind.Compound) - { - const flowType = getTypeAtFlowNode(flow.antecedent); - return createFlowType( - getBaseTypeOfLiteralType(getTypeFromFlowType(flowType)), - isIncomplete(flowType) - ); - } - if (declaredType === autoType - || declaredType === autoArrayType) - { - if (isEmptyArrayAssignment(node)) { - return getEvolvingArrayType(neverType); - } - const assignedType = - getBaseTypeOfLiteralType(getInitialOrAssignedType(flow)); - return isTypeAssignableTo(assignedType, declaredType) - ? assignedType - : anyArrayType; - } - if (declaredType.flags & TypeFlags.Union) { - return getAssignmentReducedType( - declaredType, - getInitialOrAssignedType(flow) - ); - } - return declaredType; - } - // We didn't have a direct match. However, if the reference is a dotted name, this - // may be an assignment to a left hand part of the reference. For example, for a - // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, - // return the declared type. - if (containsMatchingReference(reference, node)) { - if (!isReachableFlowNode(flow)) { - return unreachableNeverType; - } - // A matching dotted name might also be an expando property on a function *expression*, - // in which case we continue control flow analysis back to the function's declaration - if (isVariableDeclaration(node) - && (isInJSFile(node) || isVarConst(node))) - { - const init = getDeclaredExpandoInitializer(node); - if (init - && (init.kind === SyntaxKind.FunctionExpression - || init.kind === SyntaxKind.ArrowFunction)) - { - return getTypeAtFlowNode(flow.antecedent); - } - } - return declaredType; - } - // for (const _ in ref) acts as a nonnull on ref - if (isVariableDeclaration(node) - && node.parent.parent.kind === SyntaxKind.ForInStatement - && isMatchingReference( - reference, - node.parent.parent.expression - )) - { - return getNonNullableTypeIfNeeded(getTypeFromFlowType(getTypeAtFlowNode(flow - .antecedent))); - } - // Assignment doesn't affect reference - return undefined; - } - - function narrowTypeByAssertion( - type: Type, - expr: Expression - ): Type { - const node = skipParentheses(expr); - if (node.kind === SyntaxKind.FalseKeyword) { - return unreachableNeverType; - } - if (node.kind === SyntaxKind.BinaryExpression) { - if (( node).operatorToken.kind - === SyntaxKind.AmpersandAmpersandToken) - { - return narrowTypeByAssertion( - narrowTypeByAssertion( - type, - ( node).left - ), - ( node).right - ); - } - if (( node).operatorToken.kind - === SyntaxKind.BarBarToken) - { - return getUnionType([narrowTypeByAssertion( - type, - ( node).left - ), - narrowTypeByAssertion( - type, - ( node).right - )]); - } - } - return narrowType(type, node, /*assumeTrue*/ true); - } - - function getTypeAtFlowCall(flow: FlowCall): FlowType | undefined { - const signature = getEffectsSignature(flow.node); - if (signature) { - const predicate = getTypePredicateOfSignature(signature); - if (predicate - && (predicate.kind === TypePredicateKind.AssertsThis - || predicate.kind - === TypePredicateKind.AssertsIdentifier)) - { - const flowType = getTypeAtFlowNode(flow.antecedent); - const type = getTypeFromFlowType(flowType); - const narrowedType = predicate.type - ? narrowTypeByTypePredicate( - type, - predicate, - flow.node, /*assumeTrue*/ - true - ) - : predicate.kind - === TypePredicateKind.AssertsIdentifier - && predicate.parameterIndex >= 0 - && predicate.parameterIndex - < flow.node.arguments.length - ? narrowTypeByAssertion( - type, - flow.node.arguments[predicate - .parameterIndex] - ) - : type; - return narrowedType === type - ? flowType - : createFlowType( - narrowedType, - isIncomplete(flowType) - ); - } - if (getReturnTypeOfSignature(signature).flags - & TypeFlags.Never) - { - return unreachableNeverType; - } - } - return undefined; - } - - function getTypeAtFlowArrayMutation(flow: - FlowArrayMutation): FlowType | undefined - { - if (declaredType === autoType - || declaredType === autoArrayType) - { - const node = flow.node; - const expr = node.kind === SyntaxKind.CallExpression - ? ( node.expression) - .expression - : ( node.left).expression; - if (isMatchingReference( - reference, - getReferenceCandidate(expr) - )) { - const flowType = getTypeAtFlowNode(flow.antecedent); - const type = getTypeFromFlowType(flowType); - if (getObjectFlags(type) & ObjectFlags.EvolvingArray) { - let evolvedType = type; - if (node.kind === SyntaxKind.CallExpression) { - for (const arg of node.arguments) { - evolvedType = addEvolvingArrayElementType( - evolvedType, - arg - ); - } - } else { - // We must get the context free expression type so as to not recur in an uncached fashion on the LHS (which causes exponential blowup in compile time) - const indexType = - getContextFreeTypeOfExpression(( node - .left).argumentExpression); - if (isTypeAssignableToKind( - indexType, - TypeFlags.NumberLike - )) { - evolvedType = addEvolvingArrayElementType( - evolvedType, - node.right - ); - } - } - return evolvedType === type - ? flowType - : createFlowType( - evolvedType, - isIncomplete(flowType) - ); - } - return flowType; - } - } - return undefined; - } - - function getTypeAtFlowCondition(flow: FlowCondition): FlowType { - const flowType = getTypeAtFlowNode(flow.antecedent); - const type = getTypeFromFlowType(flowType); - if (type.flags & TypeFlags.Never) { - return flowType; - } - // If we have an antecedent type (meaning we're reachable in some way), we first - // attempt to narrow the antecedent type. If that produces the never type, and if - // the antecedent type is incomplete (i.e. a transient type in a loop), then we - // take the type guard as an indication that control *could* reach here once we - // have the complete type. We proceed by switching to the silent never type which - // doesn't report errors when operators are applied to it. Note that this is the - // *only* place a silent never type is ever generated. - const assumeTrue = - (flow.flags & FlowFlags.TrueCondition) !== 0; - const nonEvolvingType = finalizeEvolvingArrayType(type); - const narrowedType = narrowType( - nonEvolvingType, - flow.node, - assumeTrue - ); - if (narrowedType === nonEvolvingType) { - return flowType; - } - const incomplete = isIncomplete(flowType); - const resultType = - incomplete && narrowedType.flags & TypeFlags.Never - ? silentNeverType - : narrowedType; - return createFlowType(resultType, incomplete); - } - - function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType { - const expr = flow.switchStatement.expression; - const flowType = getTypeAtFlowNode(flow.antecedent); - let type = getTypeFromFlowType(flowType); - if (isMatchingReference(reference, expr)) { - type = narrowTypeBySwitchOnDiscriminant( - type, - flow.switchStatement, - flow.clauseStart, - flow.clauseEnd - ); - } else if (expr.kind === SyntaxKind.TypeOfExpression - && isMatchingReference( - reference, - (expr as TypeOfExpression).expression - )) - { - type = narrowBySwitchOnTypeOf( - type, - flow.switchStatement, - flow.clauseStart, - flow.clauseEnd - ); - } else { - if (strictNullChecks) { - if (optionalChainContainsReference(expr, reference)) { - type = narrowTypeBySwitchOptionalChainContainment( - type, - flow.switchStatement, - flow.clauseStart, - flow.clauseEnd, - t => !(t.flags - & (TypeFlags.Undefined | TypeFlags.Never)) - ); - } else if (expr.kind === SyntaxKind.TypeOfExpression - && optionalChainContainsReference( - (expr as TypeOfExpression).expression, - reference - )) - { - type = narrowTypeBySwitchOptionalChainContainment( - type, - flow.switchStatement, - flow.clauseStart, - flow.clauseEnd, - t => !(t.flags & TypeFlags.Never - || t.flags & TypeFlags.StringLiteral - && ( t).value - === 'undefined') - ); - } - } - if (isMatchingReferenceDiscriminant(expr, type)) { - type = narrowTypeByDiscriminant( - type, - expr as AccessExpression, - t => narrowTypeBySwitchOnDiscriminant( - t, - flow.switchStatement, - flow.clauseStart, - flow.clauseEnd - ) - ); - } else if (containsMatchingReferenceDiscriminant( - reference, - expr - )) { - type = declaredType; - } - } - return createFlowType(type, isIncomplete(flowType)); - } - - function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { - const antecedentTypes: Type[] = []; - let subtypeReduction = false; - let seenIncomplete = false; - let bypassFlow: FlowSwitchClause | undefined; - for (const antecedent of flow.antecedents!) { - if (antecedent.flags & FlowFlags.PreFinally - && ( antecedent).lock.locked) - { - // if flow correspond to branch from pre-try to finally and this branch is locked - this means that - // we initially have started following the flow outside the finally block. - // in this case we should ignore this branch. - continue; - } - if (!bypassFlow - && antecedent.flags & FlowFlags.SwitchClause - && ( antecedent).clauseStart - === ( antecedent).clauseEnd) - { - // The antecedent is the bypass branch of a potentially exhaustive switch statement. - bypassFlow = antecedent; - continue; - } - const flowType = getTypeAtFlowNode(antecedent); - const type = getTypeFromFlowType(flowType); - // If the type at a particular antecedent path is the declared type and the - // reference is known to always be assigned (i.e. when declared and initial types - // are the same), there is no reason to process more antecedents since the only - // possible outcome is subtypes that will be removed in the final union type anyway. - if (type === declaredType - && declaredType === initialType) - { - return type; - } - pushIfUnique(antecedentTypes, type); - // If an antecedent type is not a subset of the declared type, we need to perform - // subtype reduction. This happens when a "foreign" type is injected into the control - // flow using the instanceof operator or a user defined type predicate. - if (!isTypeSubsetOf(type, declaredType)) { - subtypeReduction = true; - } - if (isIncomplete(flowType)) { - seenIncomplete = true; - } - } - if (bypassFlow) { - const flowType = getTypeAtFlowNode(bypassFlow); - const type = getTypeFromFlowType(flowType); - // If the bypass flow contributes a type we haven't seen yet and the switch statement - // isn't exhaustive, process the bypass flow type. Since exhaustiveness checks increase - // the risk of circularities, we only want to perform them when they make a difference. - if (!contains(antecedentTypes, type) - && !isExhaustiveSwitchStatement(bypassFlow - .switchStatement)) - { - if (type === declaredType - && declaredType === initialType) - { - return type; - } - antecedentTypes.push(type); - if (!isTypeSubsetOf(type, declaredType)) { - subtypeReduction = true; - } - if (isIncomplete(flowType)) { - seenIncomplete = true; - } - } - } - return createFlowType( - getUnionOrEvolvingArrayType( - antecedentTypes, - subtypeReduction - ? UnionReduction.Subtype - : UnionReduction.Literal - ), - seenIncomplete - ); - } - - function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { - // If we have previously computed the control flow type for the reference at - // this flow loop junction, return the cached type. - const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] - || (flowLoopCaches[id] = createMap()); - const key = getOrSetCacheKey(); - if (!key) { - // No cache key is generated when binding patterns are in unnarrowable situations - return declaredType; - } - const cached = cache.get(key); - if (cached) { - return cached; - } - // If this flow loop junction and reference are already being processed, return - // the union of the types computed for each branch so far, marked as incomplete. - // It is possible to see an empty array in cases where loops are nested and the - // back edge of the outer loop reaches an inner loop that is already being analyzed. - // In such cases we restart the analysis of the inner loop, which will then see - // a non-empty in-process array for the outer loop and eventually terminate because - // the first antecedent of a loop junction is always the non-looping control flow - // path that leads to the top. - for (let i = flowLoopStart; i < flowLoopCount; i++) { - if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key - && flowLoopTypes[i].length) - { - return createFlowType( - getUnionOrEvolvingArrayType( - flowLoopTypes[i], - UnionReduction.Literal - ), /*incomplete*/ - true - ); - } - } - // Add the flow loop junction and reference to the in-process stack and analyze - // each antecedent code path. - const antecedentTypes: Type[] = []; - let subtypeReduction = false; - let firstAntecedentType: FlowType | undefined; - for (const antecedent of flow.antecedents!) { - let flowType; - if (!firstAntecedentType) { - // The first antecedent of a loop junction is always the non-looping control - // flow path that leads to the top. - flowType = firstAntecedentType = getTypeAtFlowNode(antecedent); - } else { - // All but the first antecedent are the looping control flow paths that lead - // back to the loop junction. We track these on the flow loop stack. - flowLoopNodes[flowLoopCount] = flow; - flowLoopKeys[flowLoopCount] = key; - flowLoopTypes[flowLoopCount] = antecedentTypes; - flowLoopCount++; - const saveFlowTypeCache = flowTypeCache; - flowTypeCache = undefined; - flowType = getTypeAtFlowNode(antecedent); - flowTypeCache = saveFlowTypeCache; - flowLoopCount--; - // If we see a value appear in the cache it is a sign that control flow analysis - // was restarted and completed by checkExpressionCached. We can simply pick up - // the resulting type and bail out. - const cached = cache.get(key); - if (cached) { - return cached; - } - } - const type = getTypeFromFlowType(flowType); - pushIfUnique(antecedentTypes, type); - // If an antecedent type is not a subset of the declared type, we need to perform - // subtype reduction. This happens when a "foreign" type is injected into the control - // flow using the instanceof operator or a user defined type predicate. - if (!isTypeSubsetOf(type, declaredType)) { - subtypeReduction = true; - } - // If the type at a particular antecedent path is the declared type there is no - // reason to process more antecedents since the only possible outcome is subtypes - // that will be removed in the final union type anyway. - if (type === declaredType) { - break; - } - } - // The result is incomplete if the first antecedent (the non-looping control flow path) - // is incomplete. - const result = getUnionOrEvolvingArrayType( - antecedentTypes, - subtypeReduction - ? UnionReduction.Subtype - : UnionReduction.Literal - ); - if (isIncomplete(firstAntecedentType!)) { - return createFlowType(result, /*incomplete*/ true); - } - cache.set(key, result); - return result; - } - - function isMatchingReferenceDiscriminant( - expr: Expression, - computedType: Type - ) { - if (!(computedType.flags & TypeFlags.Union) - || !isAccessExpression(expr)) - { - return false; - } - const name = getAccessedPropertyName(expr); - if (name === undefined) { - return false; - } - return isMatchingReference(reference, expr.expression) - && isDiscriminantProperty(computedType, name); - } - - function narrowTypeByDiscriminant( - type: Type, - access: AccessExpression, - narrowType: (t: Type) => Type - ): Type { - const propName = getAccessedPropertyName(access); - if (propName === undefined) { - return type; - } - const propType = getTypeOfPropertyOfType(type, propName); - if (!propType) { - return type; - } - const narrowedPropType = narrowType(propType); - return filterType(type, t => { - const discriminantType = getTypeOfPropertyOrIndexSignature( - t, - propName - ); - return !(discriminantType.flags & TypeFlags.Never) - && isTypeComparableTo( - discriminantType, - narrowedPropType - ); - }); - } - - function narrowTypeByTruthiness( - type: Type, - expr: Expression, - assumeTrue: boolean - ): Type { - if (isMatchingReference(reference, expr)) { - return getTypeWithFacts( - type, - assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy - ); - } - if (strictNullChecks && assumeTrue - && optionalChainContainsReference(expr, reference)) - { - type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); - } - if (isMatchingReferenceDiscriminant(expr, declaredType)) { - return narrowTypeByDiscriminant( - type, - expr, - t => getTypeWithFacts( - t, - assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy - ) - ); - } - if (containsMatchingReferenceDiscriminant(reference, expr)) { - return declaredType; - } - return type; - } - - function isTypePresencePossible( - type: Type, - propName: __String, - assumeTrue: boolean - ) { - if (getIndexInfoOfType(type, IndexKind.String)) { - return true; - } - const prop = getPropertyOfType(type, propName); - if (prop) { - return prop.flags & SymbolFlags.Optional - ? true - : assumeTrue; - } - return !assumeTrue; - } - - function narrowByInKeyword( - type: Type, - literal: LiteralExpression, - assumeTrue: boolean - ) { - if (type.flags & (TypeFlags.Union | TypeFlags.Object) - || isThisTypeParameter(type)) - { - const propName = escapeLeadingUnderscores(literal.text); - return filterType( - type, - t => isTypePresencePossible(t, propName, assumeTrue) - ); - } - return type; - } - - function narrowTypeByBinaryExpression( - type: Type, - expr: BinaryExpression, - assumeTrue: boolean - ): Type { - switch (expr.operatorToken.kind) { - case SyntaxKind.EqualsToken: - return narrowTypeByTruthiness( - narrowType(type, expr.right, assumeTrue), - expr.left, - assumeTrue - ); - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - const operator = expr.operatorToken.kind; - const left = getReferenceCandidate(expr.left); - const right = getReferenceCandidate(expr.right); - if (left.kind === SyntaxKind.TypeOfExpression - && isStringLiteralLike(right)) - { - return narrowTypeByTypeof( - type, - left, - operator, - right, - assumeTrue - ); - } - if (right.kind === SyntaxKind.TypeOfExpression - && isStringLiteralLike(left)) - { - return narrowTypeByTypeof( - type, - right, - operator, - left, - assumeTrue - ); - } - if (isMatchingReference(reference, left)) { - return narrowTypeByEquality( - type, - operator, - right, - assumeTrue - ); - } - if (isMatchingReference(reference, right)) { - return narrowTypeByEquality( - type, - operator, - left, - assumeTrue - ); - } - if (strictNullChecks) { - if (optionalChainContainsReference( - left, - reference - )) { - type = narrowTypeByOptionalChainContainment( - type, - operator, - right, - assumeTrue - ); - } else if (optionalChainContainsReference( - right, - reference - )) { - type = narrowTypeByOptionalChainContainment( - type, - operator, - left, - assumeTrue - ); - } - } - if (isMatchingReferenceDiscriminant( - left, - declaredType - )) { - return narrowTypeByDiscriminant( - type, - left, - t => narrowTypeByEquality( - t, - operator, - right, - assumeTrue - ) - ); - } - if (isMatchingReferenceDiscriminant( - right, - declaredType - )) { - return narrowTypeByDiscriminant( - type, - right, - t => narrowTypeByEquality( - t, - operator, - left, - assumeTrue - ) - ); - } - if (containsMatchingReferenceDiscriminant( - reference, - left - ) - || containsMatchingReferenceDiscriminant( - reference, - right - )) - { - return declaredType; - } - break; - case SyntaxKind.InstanceOfKeyword: - return narrowTypeByInstanceof(type, expr, assumeTrue); - case SyntaxKind.InKeyword: - const target = getReferenceCandidate(expr.right); - if (isStringLiteralLike(expr.left) - && isMatchingReference(reference, target)) - { - return narrowByInKeyword( - type, - expr.left, - assumeTrue - ); - } - break; - case SyntaxKind.CommaToken: - return narrowType(type, expr.right, assumeTrue); - } - return type; - } - - function narrowTypeByOptionalChainContainment( - type: Type, - operator: SyntaxKind, - value: Expression, - assumeTrue: boolean - ): Type { - // We are in a branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from - // the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the - // operator is !== and the type of value is undefined. - const effectiveTrue = - operator === SyntaxKind.EqualsEqualsToken - || operator === SyntaxKind.EqualsEqualsEqualsToken - ? assumeTrue - : !assumeTrue; - const doubleEquals = operator === SyntaxKind.EqualsEqualsToken - || operator === SyntaxKind.ExclamationEqualsToken; - const valueNonNullish = - !(getTypeFacts(getTypeOfExpression(value)) - & (doubleEquals - ? TypeFacts.EQUndefinedOrNull - : TypeFacts.EQUndefined)); - return effectiveTrue === valueNonNullish - ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) - : type; - } - - function narrowTypeByEquality( - type: Type, - operator: SyntaxKind, - value: Expression, - assumeTrue: boolean - ): Type { - if (type.flags & TypeFlags.Any) { - return type; - } - if (operator === SyntaxKind.ExclamationEqualsToken - || operator === SyntaxKind.ExclamationEqualsEqualsToken) - { - assumeTrue = !assumeTrue; - } - const valueType = getTypeOfExpression(value); - if ((type.flags & TypeFlags.Unknown) && assumeTrue - && (operator === SyntaxKind.EqualsEqualsEqualsToken - || operator - === SyntaxKind.ExclamationEqualsEqualsToken)) - { - if (valueType.flags - & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) - { - return valueType; - } - if (valueType.flags & TypeFlags.Object) { - return nonPrimitiveType; - } - return type; - } - if (valueType.flags & TypeFlags.Nullable) { - if (!strictNullChecks) { - return type; - } - const doubleEquals = - operator === SyntaxKind.EqualsEqualsToken - || operator === SyntaxKind.ExclamationEqualsToken; - const facts = doubleEquals - ? assumeTrue - ? TypeFacts.EQUndefinedOrNull - : TypeFacts.NEUndefinedOrNull - : valueType.flags & TypeFlags.Null - ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull - : assumeTrue ? TypeFacts.EQUndefined - : TypeFacts.NEUndefined; - return getTypeWithFacts(type, facts); - } - if (type.flags & TypeFlags.NotUnionOrUnit) { - return type; - } - if (assumeTrue) { - const narrowedType = filterType(type, filterFn); - return narrowedType.flags & TypeFlags.Never - ? type - : replacePrimitivesWithLiterals( - narrowedType, - valueType - ); - } - if (isUnitType(valueType)) { - const regularType = getRegularTypeOfLiteralType(valueType); - return filterType( - type, - t => getRegularTypeOfLiteralType(t) !== regularType - ); - } - return type; - } - - function narrowTypeByTypeof( - type: Type, - typeOfExpr: TypeOfExpression, - operator: SyntaxKind, - literal: LiteralExpression, - assumeTrue: boolean - ): Type { - // We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands - if (operator === SyntaxKind.ExclamationEqualsToken - || operator === SyntaxKind.ExclamationEqualsEqualsToken) - { - assumeTrue = !assumeTrue; - } - const target = getReferenceCandidate(typeOfExpr.expression); - if (!isMatchingReference(reference, target)) { - if (strictNullChecks - && optionalChainContainsReference(target, reference) - && assumeTrue === (literal.text !== 'undefined')) - { - return getTypeWithFacts( - type, - TypeFacts.NEUndefinedOrNull - ); - } - // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the - // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, target)) { - return declaredType; - } - return type; - } - if (type.flags & TypeFlags.Any - && literal.text === 'function') - { - return type; - } - const facts = assumeTrue - ? typeofEQFacts.get(literal.text) - || TypeFacts.TypeofEQHostObject - : typeofNEFacts.get(literal.text) - || TypeFacts.TypeofNEHostObject; - return getTypeWithFacts( - assumeTrue ? mapType(type, narrowTypeForTypeof) : type, - facts - ); - - function narrowTypeForTypeof(type: Type) { - if (type.flags & TypeFlags.Unknown - && literal.text === 'object') - { - return getUnionType([nonPrimitiveType, nullType]); - } - // We narrow a non-union type to an exact primitive type if the non-union type - // is a supertype of that primitive type. For example, type 'any' can be narrowed - // to one of the primitive types. - const targetType = literal.text === 'function' - ? globalFunctionType - : typeofTypesByName.get(literal.text); - if (targetType) { - if (isTypeSubtypeOf(type, targetType)) { - return type; - } - if (isTypeSubtypeOf(targetType, type)) { - return targetType; - } - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type) - || anyType; - if (isTypeSubtypeOf(targetType, constraint)) { - return getIntersectionType([type, targetType]); - } - } - } - return type; - } - } - - function narrowTypeBySwitchOptionalChainContainment( - type: Type, - switchStatement: SwitchStatement, - clauseStart: number, - clauseEnd: number, - clauseCheck: (type: Type) => boolean - ) { - const everyClauseChecks = clauseStart !== clauseEnd - && every( - getSwitchClauseTypes(switchStatement).slice( - clauseStart, - clauseEnd - ), - clauseCheck - ); - return everyClauseChecks - ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) - : type; - } - - function narrowTypeBySwitchOnDiscriminant( - type: Type, - switchStatement: SwitchStatement, - clauseStart: number, - clauseEnd: number - ) { - // We only narrow if all case expressions specify - // values with unit types, except for the case where - // `type` is unknown. In this instance we map object - // types to the nonPrimitive type and narrow with that. - const switchTypes = getSwitchClauseTypes(switchStatement); - if (!switchTypes.length) { - return type; - } - const clauseTypes = switchTypes.slice(clauseStart, clauseEnd); - const hasDefaultClause = clauseStart === clauseEnd - || contains(clauseTypes, neverType); - if ((type.flags & TypeFlags.Unknown) && !hasDefaultClause) { - let groundClauseTypes: Type[] | undefined; - for (let i = 0; i < clauseTypes.length; i += 1) { - const t = clauseTypes[i]; - if (t.flags - & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) - { - if (groundClauseTypes !== undefined) { - groundClauseTypes.push(t); - } - } else if (t.flags & TypeFlags.Object) { - if (groundClauseTypes === undefined) { - groundClauseTypes = clauseTypes.slice(0, i); - } - groundClauseTypes.push(nonPrimitiveType); - } else { - return type; - } - } - return getUnionType(groundClauseTypes === undefined - ? clauseTypes - : groundClauseTypes); - } - const discriminantType = getUnionType(clauseTypes); - const caseType = discriminantType.flags & TypeFlags.Never - ? neverType - : replacePrimitivesWithLiterals( - filterType( - type, - t => areTypesComparable(discriminantType, t) - ), - discriminantType - ); - if (!hasDefaultClause) { - return caseType; - } - const defaultType = filterType( - type, - t => !(isUnitType(t) && contains( - switchTypes, - getRegularTypeOfLiteralType(t) - )) - ); - return caseType.flags & TypeFlags.Never - ? defaultType - : getUnionType([caseType, defaultType]); - } - - function getImpliedTypeFromTypeofCase(type: Type, text: string) { - switch (text) { - case 'function': - return type.flags & TypeFlags.Any - ? type - : globalFunctionType; - case 'object': - return type.flags & TypeFlags.Unknown - ? getUnionType([nonPrimitiveType, nullType]) - : type; - default: - return typeofTypesByName.get(text) || type; - } - } - - function narrowTypeForTypeofSwitch(candidate: Type) { - return (type: Type) => { - if (isTypeSubtypeOf(candidate, type)) { - return candidate; - } - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type) - || anyType; - if (isTypeSubtypeOf(candidate, constraint)) { - return getIntersectionType([type, candidate]); - } - } - return type; - }; - } - - function narrowBySwitchOnTypeOf( - type: Type, - switchStatement: SwitchStatement, - clauseStart: number, - clauseEnd: number - ): Type { - const switchWitnesses = - getSwitchClauseTypeOfWitnesses(switchStatement); - if (!switchWitnesses.length) { - return type; - } - // Equal start and end denotes implicit fallthrough; undefined marks explicit default clause - const defaultCaseLocation = findIndex( - switchWitnesses, - elem => elem === undefined - ); - const hasDefaultClause = clauseStart === clauseEnd - || (defaultCaseLocation >= clauseStart - && defaultCaseLocation < clauseEnd); - let clauseWitnesses: string[]; - let switchFacts: TypeFacts; - if (defaultCaseLocation > -1) { - // We no longer need the undefined denoting an - // explicit default case. Remove the undefined and - // fix-up clauseStart and clauseEnd. This means - // that we don't have to worry about undefined - // in the witness array. - const witnesses = switchWitnesses - .filter(witness => witness !== undefined); - // The adjusted clause start and end after removing the `default` statement. - const fixedClauseStart = defaultCaseLocation < clauseStart - ? clauseStart - 1 - : clauseStart; - const fixedClauseEnd = defaultCaseLocation < clauseEnd - ? clauseEnd - 1 - : clauseEnd; - clauseWitnesses = witnesses.slice( - fixedClauseStart, - fixedClauseEnd - ); - switchFacts = getFactsFromTypeofSwitch( - fixedClauseStart, - fixedClauseEnd, - witnesses, - hasDefaultClause - ); - } else { - clauseWitnesses = switchWitnesses.slice( - clauseStart, - clauseEnd - ); - switchFacts = getFactsFromTypeofSwitch( - clauseStart, - clauseEnd, - switchWitnesses, - hasDefaultClause - ); - } - if (hasDefaultClause) { - return filterType( - type, - t => (getTypeFacts(t) & switchFacts) === switchFacts - ); - } - /* - The implied type is the raw type suggested by a - value being caught in this clause. - - When the clause contains a default case we ignore - the implied type and try to narrow using any facts - we can learn: see `switchFacts`. - - Example: - switch (typeof x) { - case 'number': - case 'string': break; - default: break; - case 'number': - case 'boolean': break - } - - In the first clause (case `number` and `string`) the - implied type is number | string. - - In the default clause we de not compute an implied type. - - In the third clause (case `number` and `boolean`) - the naive implied type is number | boolean, however - we use the type facts to narrow the implied type to - boolean. We know that number cannot be selected - because it is caught in the first clause. - */ - let impliedType = getTypeWithFacts( - getUnionType(clauseWitnesses - .map(text => getImpliedTypeFromTypeofCase( - type, - text - ))), - switchFacts - ); - if (impliedType.flags & TypeFlags.Union) { - impliedType = getAssignmentReducedType( - impliedType as UnionType, - getBaseConstraintOrType(type) - ); - } - return getTypeWithFacts( - mapType(type, narrowTypeForTypeofSwitch(impliedType)), - switchFacts - ); - } - - function narrowTypeByInstanceof( - type: Type, - expr: BinaryExpression, - assumeTrue: boolean - ): Type { - const left = getReferenceCandidate(expr.left); - if (!isMatchingReference(reference, left)) { - if (assumeTrue && strictNullChecks - && optionalChainContainsReference(left, reference)) - { - return getTypeWithFacts( - type, - TypeFacts.NEUndefinedOrNull - ); - } - // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the - // narrowed type of 'y' to its declared type. We do this because preceding 'x.y' - // references might reference a different 'y' property. However, we make an exception - // for property accesses where x is a synthetic 'this' expression, indicating that we - // were called from isPropertyInitializedInConstructor. Without this exception, - // initializations of 'this' properties that occur before a 'this instanceof XXX' - // check would not be considered. - if (containsMatchingReference(reference, left) - && !isSyntheticThisPropertyAccess(reference)) - { - return declaredType; - } - return type; - } - - // Check that right operand is a function type with a prototype property - const rightType = getTypeOfExpression(expr.right); - if (!isTypeDerivedFrom(rightType, globalFunctionType)) { - return type; - } - - let targetType: Type | undefined; - const prototypeProperty = getPropertyOfType( - rightType, - 'prototype' as __String - ); - if (prototypeProperty) { - // Target type is type of the prototype property - const prototypePropertyType = - getTypeOfSymbol(prototypeProperty); - if (!isTypeAny(prototypePropertyType)) { - targetType = prototypePropertyType; - } - } - - // Don't narrow from 'any' if the target type is exactly 'Object' or 'Function' - if (isTypeAny(type) - && (targetType === globalObjectType - || targetType === globalFunctionType)) - { - return type; - } - - if (!targetType) { - const constructSignatures = getSignaturesOfType( - rightType, - SignatureKind.Construct - ); - targetType = constructSignatures.length - ? getUnionType(map( - constructSignatures, - signature => getReturnTypeOfSignature(getErasedSignature(signature)) - )) - : emptyObjectType; - } - - return getNarrowedType( - type, - targetType, - assumeTrue, - isTypeDerivedFrom - ); - } - - function getNarrowedType( - type: Type, - candidate: Type, - assumeTrue: boolean, - isRelated: (source: Type, target: Type) => boolean - ) { - if (!assumeTrue) { - return filterType(type, t => !isRelated(t, candidate)); - } - // If the current type is a union type, remove all constituents that couldn't be instances of - // the candidate type. If one or more constituents remain, return a union of those. - if (type.flags & TypeFlags.Union) { - const assignableType = filterType( - type, - t => isRelated(t, candidate) - ); - if (!(assignableType.flags & TypeFlags.Never)) { - return assignableType; - } - } - // If the candidate type is a subtype of the target type, narrow to the candidate type. - // Otherwise, if the target type is assignable to the candidate type, keep the target type. - // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate - // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the - // two types. - return isTypeSubtypeOf(candidate, type) - ? candidate - : isTypeAssignableTo(type, candidate) - ? type - : isTypeAssignableTo(candidate, type) - ? candidate - : getIntersectionType([type, candidate]); - } - - function narrowTypeByCallExpression( - type: Type, - callExpression: CallExpression, - assumeTrue: boolean - ): Type { - if (hasMatchingArgument(callExpression, reference)) { - const signature = - assumeTrue || !isCallChain(callExpression) - ? getEffectsSignature(callExpression) - : undefined; - const predicate = signature - && getTypePredicateOfSignature(signature); - if (predicate - && (predicate.kind === TypePredicateKind.This - || predicate.kind - === TypePredicateKind.Identifier)) - { - return narrowTypeByTypePredicate( - type, - predicate, - callExpression, - assumeTrue - ); - } - } - return type; - } - - function narrowTypeByTypePredicate( - type: Type, - predicate: TypePredicate, - callExpression: CallExpression, - assumeTrue: boolean - ): Type { - // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' - if (predicate.type - && !(isTypeAny(type) - && (predicate.type === globalObjectType - || predicate.type === globalFunctionType))) - { - const predicateArgument = getTypePredicateArgument( - predicate, - callExpression - ); - if (predicateArgument) { - if (isMatchingReference( - reference, - predicateArgument - )) { - return getNarrowedType( - type, - predicate.type, - assumeTrue, - isTypeSubtypeOf - ); - } - if (strictNullChecks && assumeTrue - && optionalChainContainsReference( - predicateArgument, - reference - ) - && !(getTypeFacts(predicate.type) - & TypeFacts.EQUndefined)) - { - return getTypeWithFacts( - type, - TypeFacts.NEUndefinedOrNull - ); - } - if (containsMatchingReference( - reference, - predicateArgument - )) { - return declaredType; - } - } - } - return type; - } - - // Narrow the given type based on the given expression having the assumed boolean value. The returned type - // will be a subtype or the same type as the argument. - function narrowType( - type: Type, - expr: Expression, - assumeTrue: boolean - ): Type { - // for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a` - if (isExpressionOfOptionalChainRoot(expr) - || isBinaryExpression(expr.parent) - && expr.parent.operatorToken.kind - === SyntaxKind.QuestionQuestionToken - && expr.parent.left === expr) - { - return narrowTypeByOptionality(type, expr, assumeTrue); - } - switch (expr.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.ThisKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return narrowTypeByTruthiness(type, expr, assumeTrue); - case SyntaxKind.CallExpression: - return narrowTypeByCallExpression( - type, - expr, - assumeTrue - ); - case SyntaxKind.ParenthesizedExpression: - return narrowType( - type, - ( expr).expression, - assumeTrue - ); - case SyntaxKind.BinaryExpression: - return narrowTypeByBinaryExpression( - type, - expr, - assumeTrue - ); - case SyntaxKind.PrefixUnaryExpression: - if (( expr).operator - === SyntaxKind.ExclamationToken) - { - return narrowType( - type, - ( expr).operand, - !assumeTrue - ); - } - break; - } - return type; - } - - function narrowTypeByOptionality( - type: Type, - expr: Expression, - assumePresent: boolean - ): Type { - if (isMatchingReference(reference, expr)) { - return getTypeWithFacts( - type, - assumePresent - ? TypeFacts.NEUndefinedOrNull - : TypeFacts.EQUndefinedOrNull - ); - } - if (isMatchingReferenceDiscriminant(expr, declaredType)) { - return narrowTypeByDiscriminant( - type, - expr, - t => getTypeWithFacts( - t, - assumePresent - ? TypeFacts.NEUndefinedOrNull - : TypeFacts.EQUndefinedOrNull - ) - ); - } - if (containsMatchingReferenceDiscriminant(reference, expr)) { - return declaredType; - } - return type; - } - } - - function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { - symbol = symbol.exportSymbol || symbol; - - // If we have an identifier or a property access at the given location, if the location is - // an dotted name expression, and if the location is not an assignment target, obtain the type - // of the expression (which will reflect control flow analysis). If the expression indeed - // resolved to the given symbol, return the narrowed type. - if (location.kind === SyntaxKind.Identifier) { - if (isRightSideOfQualifiedNameOrPropertyAccess(location)) { - location = location.parent; - } - if (isExpressionNode(location) - && !isAssignmentTarget(location)) - { - const type = getTypeOfExpression( location); - if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location) - .resolvedSymbol) === symbol) - { - return type; - } - } - } - // The location isn't a reference to the given symbol, meaning we're being asked - // a hypothetical question of what type the symbol would have if there was a reference - // to it at the given location. Since we have no control flow information for the - // hypothetical reference (control flow information is created and attached by the - // binder), we simply return the declared type of the symbol. - return getTypeOfSymbol(symbol); - } - - function getControlFlowContainer(node: Node): Node { - return findAncestor( - node.parent, - node => isFunctionLike(node) - && !getImmediatelyInvokedFunctionExpression(node) - || node.kind === SyntaxKind.ModuleBlock - || node.kind === SyntaxKind.SourceFile - || node.kind === SyntaxKind.PropertyDeclaration - )!; - } - - // Check if a parameter is assigned anywhere within its declaring function. - function isParameterAssigned(symbol: Symbol) { - const func = - getRootDeclaration(symbol - .valueDeclaration).parent; - const links = getNodeLinks(func); - if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) { - links.flags |= NodeCheckFlags.AssignmentsMarked; - if (!hasParentWithAssignmentsMarked(func)) { - markParameterAssignments(func); - } - } - return symbol.isAssigned || false; - } - - function hasParentWithAssignmentsMarked(node: Node) { - return !!findAncestor( - node.parent, - node => isFunctionLike(node) - && !!(getNodeLinks(node).flags - & NodeCheckFlags.AssignmentsMarked) - ); - } - - function markParameterAssignments(node: Node) { - if (node.kind === SyntaxKind.Identifier) { - if (isAssignmentTarget(node)) { - const symbol = getResolvedSymbol( node); - if (symbol.valueDeclaration - && getRootDeclaration(symbol.valueDeclaration).kind - === SyntaxKind.Parameter) - { - symbol.isAssigned = true; - } - } - } else { - forEachChild(node, markParameterAssignments); - } - } - - function isConstVariable(symbol: Symbol) { - return symbol.flags & SymbolFlags.Variable - && (getDeclarationNodeFlagsFromSymbol(symbol) - & NodeFlags.Const) !== 0 - && getTypeOfSymbol(symbol) !== autoArrayType; - } - - /** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */ - function removeOptionalityFromDeclaredType( - declaredType: Type, - declaration: VariableLikeDeclaration - ): Type { - const annotationIncludesUndefined = strictNullChecks - && declaration.kind === SyntaxKind.Parameter - && declaration.initializer - && getFalsyFlags(declaredType) & TypeFlags.Undefined - && !(getFalsyFlags(checkExpression(declaration.initializer)) - & TypeFlags.Undefined); - return annotationIncludesUndefined - ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) - : declaredType; - } - - function isConstraintPosition(node: Node) { - const parent = node.parent; - return parent.kind === SyntaxKind.PropertyAccessExpression - || parent.kind === SyntaxKind.CallExpression - && ( parent).expression === node - || parent.kind === SyntaxKind.ElementAccessExpression - && ( parent).expression === node - || parent.kind === SyntaxKind.BindingElement - && ( parent).name === node - && !!( parent).initializer; - } - - function typeHasNullableConstraint(type: Type) { - return type.flags & TypeFlags.InstantiableNonPrimitive - && maybeTypeOfKind( - getBaseConstraintOfType(type) || unknownType, - TypeFlags.Nullable - ); - } - - function getConstraintForLocation(type: Type, node: Node): Type; - function getConstraintForLocation( - type: Type | undefined, - node: Node - ): Type | undefined; - function getConstraintForLocation(type: Type, node: Node): Type - | undefined - { - // When a node is the left hand expression of a property access, element access, or call expression, - // and the type of the node includes type variables with constraints that are nullable, we fetch the - // apparent type of the node *before* performing control flow analysis such that narrowings apply to - // the constraint type. - if (type && isConstraintPosition(node) - && forEachType(type, typeHasNullableConstraint)) - { - return mapType(getWidenedType(type), getBaseConstraintOrType); - } - return type; - } - - function isExportOrExportExpression(location: Node) { - return !!findAncestor( - location, - e => e.parent && isExportAssignment(e.parent) - && e.parent.expression === e && isEntityNameExpression(e) - ); - } - - function markAliasReferenced(symbol: Symbol, location: Node) { - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) - && !isInTypeQuery(location) - && ((compilerOptions.preserveConstEnums - && isExportOrExportExpression(location)) - || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) - { - markAliasSymbolAsReferenced(symbol); - } - } - - function checkIdentifier(node: Identifier): Type { - const symbol = getResolvedSymbol(node); - if (symbol === unknownSymbol) { - return errorType; - } - - // As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects. - // Although in down-level emit of arrow function, we emit it using function expression which means that - // arguments objects will be bound to the inner object; emitting arrow function natively in ES6, arguments objects - // will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior. - // To avoid that we will give an error to users if they use arguments objects in arrow function so that they - // can explicitly bound arguments objects - if (symbol === argumentsSymbol) { - const container = getContainingFunction(node)!; - if (languageVersion < ScriptTarget.ES2015) { - if (container.kind === SyntaxKind.ArrowFunction) { - error( - node, - Diagnostics - .The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression - ); - } else if (hasModifier(container, ModifierFlags.Async)) { - error( - node, - Diagnostics - .The_arguments_object_cannot_be_referenced_in_an_async_function_or_method_in_ES3_and_ES5_Consider_using_a_standard_function_or_method - ); - } - } - - getNodeLinks(container).flags |= NodeCheckFlags - .CaptureArguments; - return getTypeOfSymbol(symbol); - } - - // We should only mark aliases as referenced if there isn't a local value declaration - // for the symbol. Also, don't mark any property access expression LHS - checkPropertyAccessExpression will handle that - if (!(node.parent && isPropertyAccessExpression(node.parent) - && node.parent.expression === node)) - { - markAliasReferenced(symbol, node); - } - - const localOrExportSymbol = - getExportSymbolOfValueSymbolIfExported(symbol); - let declaration: Declaration | undefined = localOrExportSymbol - .valueDeclaration; - - if (localOrExportSymbol.flags & SymbolFlags.Class) { - // Due to the emit for class decorators, any reference to the class from inside of the class body - // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind - // behavior of class names in ES6. - if (declaration.kind === SyntaxKind.ClassDeclaration - && nodeIsDecorated(declaration as ClassDeclaration)) - { - let container = getContainingClass(node); - while (container !== undefined) { - if (container === declaration - && container.name !== node) - { - getNodeLinks(declaration).flags |= NodeCheckFlags - .ClassWithConstructorReference; - getNodeLinks(node).flags |= NodeCheckFlags - .ConstructorReferenceInClass; - break; - } - - container = getContainingClass(container); - } - } else if (declaration.kind === SyntaxKind.ClassExpression) { - // When we emit a class expression with static members that contain a reference - // to the constructor in the initializer, we will need to substitute that - // binding with an alias as the class name is not in scope. - let container = getThisContainer( - node, /*includeArrowFunctions*/ - false - ); - while (container.kind !== SyntaxKind.SourceFile) { - if (container.parent === declaration) { - if (container.kind - === SyntaxKind.PropertyDeclaration - && hasModifier( - container, - ModifierFlags.Static - )) - { - getNodeLinks(declaration) - .flags |= NodeCheckFlags - .ClassWithConstructorReference; - getNodeLinks(node).flags |= NodeCheckFlags - .ConstructorReferenceInClass; - } - break; - } - - container = getThisContainer( - container, /*includeArrowFunctions*/ - false - ); - } - } - } - - checkNestedBlockScopedBinding(node, symbol); - - const type = getConstraintForLocation( - getTypeOfSymbol(localOrExportSymbol), - node - ); - const assignmentKind = getAssignmentTargetKind(node); - - if (assignmentKind) { - if (!(localOrExportSymbol.flags & SymbolFlags.Variable) - && !(isInJSFile(node) - && localOrExportSymbol.flags - & SymbolFlags.ValueModule)) - { - error( - node, - Diagnostics - .Cannot_assign_to_0_because_it_is_not_a_variable, - symbolToString(symbol) - ); - return errorType; - } - if (isReadonlySymbol(localOrExportSymbol)) { - if (localOrExportSymbol.flags & SymbolFlags.Variable) { - error( - node, - Diagnostics - .Cannot_assign_to_0_because_it_is_a_constant, - symbolToString(symbol) - ); - } else { - error( - node, - Diagnostics - .Cannot_assign_to_0_because_it_is_a_read_only_property, - symbolToString(symbol) - ); - } - return errorType; - } - } - - const isAlias = localOrExportSymbol.flags & SymbolFlags.Alias; - - // We only narrow variables and parameters occurring in a non-assignment position. For all other - // entities we simply return the declared type. - if (localOrExportSymbol.flags & SymbolFlags.Variable) { - if (assignmentKind === AssignmentKind.Definite) { - return type; - } - } else if (isAlias) { - declaration = find( - symbol.declarations, - isSomeImportDeclaration - ); - } else { - return type; - } - - if (!declaration) { - return type; - } - - // The declaration container is the innermost function that encloses the declaration of the variable - // or parameter. The flow container is the innermost function starting with which we analyze the control - // flow graph to determine the control flow based type. - const isParameter = - getRootDeclaration(declaration).kind === SyntaxKind.Parameter; - const declarationContainer = getControlFlowContainer(declaration); - let flowContainer = getControlFlowContainer(node); - const isOuterVariable = flowContainer !== declarationContainer; - const isSpreadDestructuringAssignmentTarget = node.parent - && node.parent.parent && isSpreadAssignment(node.parent) - && isDestructuringAssignmentTarget(node.parent.parent); - const isModuleExports = symbol.flags & SymbolFlags.ModuleExports; - // When the control flow originates in a function expression or arrow function and we are referencing - // a const variable or parameter from an outer function, we extend the origin of the control flow - // analysis to include the immediately enclosing function. - while (flowContainer !== declarationContainer - && (flowContainer.kind === SyntaxKind.FunctionExpression - || flowContainer.kind === SyntaxKind.ArrowFunction - || isObjectLiteralOrClassExpressionMethod(flowContainer)) - && (isConstVariable(localOrExportSymbol) || isParameter - && !isParameterAssigned(localOrExportSymbol))) - { - flowContainer = getControlFlowContainer(flowContainer); - } - // We only look for uninitialized variables in strict null checking mode, and only when we can analyze - // the entire control flow graph from the variable's declaration (i.e. when the flow container and - // declaration container are the same). - const assumeInitialized = isParameter || isAlias || isOuterVariable - || isSpreadDestructuringAssignmentTarget || isModuleExports - || isBindingElement(declaration) - || type !== autoType && type !== autoArrayType - && (!strictNullChecks - || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) - !== 0 - || isInTypeQuery(node) - || node.parent.kind === SyntaxKind.ExportSpecifier) - || node.parent.kind === SyntaxKind.NonNullExpression - || declaration.kind === SyntaxKind.VariableDeclaration - && ( declaration).exclamationToken - || declaration.flags & NodeFlags.Ambient; - const initialType = assumeInitialized - ? (isParameter - ? removeOptionalityFromDeclaredType( - type, - declaration as VariableLikeDeclaration - ) - : type) - : type === autoType || type === autoArrayType - ? undefinedType - : getOptionalType(type); - const flowType = getFlowTypeOfReference( - node, - type, - initialType, - flowContainer, - !assumeInitialized - ); - // A variable is considered uninitialized when it is possible to analyze the entire control flow graph - // from declaration to use, and when the variable's declared type doesn't include undefined but the - // control flow based type does include undefined. - if (!isEvolvingArrayOperationTarget(node) - && (type === autoType || type === autoArrayType)) - { - if (flowType === autoType || flowType === autoArrayType) { - if (noImplicitAny) { - error( - getNameOfDeclaration(declaration), - Diagnostics - .Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, - symbolToString(symbol), - typeToString(flowType) - ); - error( - node, - Diagnostics.Variable_0_implicitly_has_an_1_type, - symbolToString(symbol), - typeToString(flowType) - ); - } - return convertAutoToAny(flowType); - } - } else if (!assumeInitialized - && !(getFalsyFlags(type) & TypeFlags.Undefined) - && getFalsyFlags(flowType) & TypeFlags.Undefined) - { - const diag = error( - node, - Diagnostics.Variable_0_is_used_before_being_assigned, - symbolToString(symbol) - ); - - // See GH:32846 - if the user is using a variable whose type is () => T1 | ... | undefined - // they may have meant to specify the type as (() => T1 | ...) | undefined - // This is assumed if: the type is a FunctionType, the return type is a Union, the last constituent of - // the union is `undefined` - if (type.symbol && type.symbol.declarations.length === 1 - && isFunctionTypeNode(type.symbol.declarations[0])) - { - const funcTypeNode = type.symbol - .declarations[0]; - const returnType = - getReturnTypeFromAnnotation(funcTypeNode); - if (returnType && returnType.flags & TypeFlags.Union) { - const unionTypes = ( funcTypeNode.type) - .types; - if (unionTypes - && unionTypes[unionTypes.length - 1].kind - === SyntaxKind.UndefinedKeyword) - { - const parenedFuncType = - getMutableClone(funcTypeNode); - // Highlight to the end of the second to last constituent of the union - parenedFuncType.end = unionTypes[unionTypes.length - - 2].end; - addRelatedInfo( - diag, - createDiagnosticForNode( - parenedFuncType, - Diagnostics - .Did_you_mean_to_parenthesize_this_function_type - ) - ); - } - } - } - - // Return the declared type to reduce follow-on errors - return type; - } - return assignmentKind - ? getBaseTypeOfLiteralType(flowType) - : flowType; - } - - function isInsideFunction(node: Node, threshold: Node): boolean { - return !!findAncestor( - node, - n => n === threshold ? 'quit' : isFunctionLike(n) - ); - } - - function getPartOfForStatementContainingNode( - node: Node, - container: ForStatement - ) { - return findAncestor( - node, - n => n === container - ? 'quit' - : n === container.initializer || n === container.condition - || n === container.incrementor - || n === container.statement - ); - } - - function checkNestedBlockScopedBinding( - node: Identifier, - symbol: Symbol - ): void { - if (languageVersion >= ScriptTarget.ES2015 - || (symbol.flags - & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) - === 0 - || isSourceFile(symbol.valueDeclaration) - || symbol.valueDeclaration.parent.kind - === SyntaxKind.CatchClause) - { - return; - } - - // 1. walk from the use site up to the declaration and check - // if there is anything function like between declaration and use-site (is binding/class is captured in function). - // 2. walk from the declaration up to the boundary of lexical environment and check - // if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement) - - const container = - getEnclosingBlockScopeContainer(symbol.valueDeclaration); - const usedInFunction = isInsideFunction(node.parent, container); - let current = container; - - let containedInIterationStatement = false; - while (current && !nodeStartsNewLexicalEnvironment(current)) { - if (isIterationStatement( - current, /*lookInLabeledStatements*/ - false - )) { - containedInIterationStatement = true; - break; - } - current = current.parent; - } - - if (containedInIterationStatement) { - if (usedInFunction) { - // mark iteration statement as containing block-scoped binding captured in some function - let capturesBlockScopeBindingInLoopBody = true; - if (isForStatement(container)) { - const varDeclList = getAncestor( - symbol.valueDeclaration, - SyntaxKind.VariableDeclarationList - ); - if (varDeclList && varDeclList.parent === container) { - const part = getPartOfForStatementContainingNode( - node.parent, - container - ); - if (part) { - const links = getNodeLinks(part); - links.flags |= NodeCheckFlags - .ContainsCapturedBlockScopeBinding; - - const capturedBindings = - links.capturedBlockScopeBindings - || (links - .capturedBlockScopeBindings = []); - pushIfUnique(capturedBindings, symbol); - - if (part === container.initializer) { - capturesBlockScopeBindingInLoopBody = false; // Initializer is outside of loop body - } - } - } - } - if (capturesBlockScopeBindingInLoopBody) { - getNodeLinks(current).flags |= NodeCheckFlags - .LoopWithCapturedBlockScopedBinding; - } - } - - // mark variables that are declared in loop initializer and reassigned inside the body of ForStatement. - // if body of ForStatement will be converted to function then we'll need a extra machinery to propagate reassigned values back. - if (isForStatement(container)) { - const varDeclList = getAncestor( - symbol.valueDeclaration, - SyntaxKind.VariableDeclarationList - ); - if (varDeclList && varDeclList.parent === container - && isAssignedInBodyOfForStatement(node, container)) - { - getNodeLinks(symbol.valueDeclaration) - .flags |= NodeCheckFlags.NeedsLoopOutParameter; - } - } - - // set 'declared inside loop' bit on the block-scoped binding - getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags - .BlockScopedBindingInLoop; - } - - if (usedInFunction) { - getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags - .CapturedBlockScopedBinding; - } - } - - function isBindingCapturedByNode( - node: Node, - decl: VariableDeclaration | BindingElement - ) { - const links = getNodeLinks(node); - return !!links - && contains( - links.capturedBlockScopeBindings, - getSymbolOfNode(decl) - ); - } - - function isAssignedInBodyOfForStatement( - node: Identifier, - container: ForStatement - ): boolean { - // skip parenthesized nodes - let current: Node = node; - while (current.parent.kind - === SyntaxKind.ParenthesizedExpression) - { - current = current.parent; - } - - // check if node is used as LHS in some assignment expression - let isAssigned = false; - if (isAssignmentTarget(current)) { - isAssigned = true; - } else if ((current.parent.kind - === SyntaxKind.PrefixUnaryExpression - || current.parent.kind === SyntaxKind.PostfixUnaryExpression)) - { - const expr = current.parent; - isAssigned = expr.operator === SyntaxKind.PlusPlusToken - || expr.operator === SyntaxKind.MinusMinusToken; - } - - if (!isAssigned) { - return false; - } - - // at this point we know that node is the target of assignment - // now check that modification happens inside the statement part of the ForStatement - return !!findAncestor( - current, - n => n === container ? 'quit' : n === container.statement - ); - } - - function captureLexicalThis(node: Node, container: Node): void { - getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis; - if (container.kind === SyntaxKind.PropertyDeclaration - || container.kind === SyntaxKind.Constructor) - { - const classNode = container.parent; - getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis; - } else { - getNodeLinks(container).flags |= NodeCheckFlags.CaptureThis; - } - } - - function findFirstSuperCall(n: Node): SuperCall | undefined { - if (isSuperCall(n)) { - return n; - } else if (isFunctionLike(n)) { - return undefined; - } - return forEachChild(n, findFirstSuperCall); - } - - /** - * Return a cached result if super-statement is already found. - * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor - * - * @param constructor constructor-function to look for super statement - */ - function getSuperCallInConstructor(constructor: - ConstructorDeclaration): SuperCall | undefined - { - const links = getNodeLinks(constructor); - - // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result - if (links.hasSuperCall === undefined) { - links.superCall = findFirstSuperCall(constructor.body!); - links.hasSuperCall = links.superCall ? true : false; - } - return links.superCall!; - } - - /** - * Check if the given class-declaration extends null then return true. - * Otherwise, return false - * @param classDecl a class declaration to check if it extends null - */ - function classDeclarationExtendsNull(classDecl: - ClassDeclaration): boolean - { - const classSymbol = getSymbolOfNode(classDecl); - const classInstanceType = - getDeclaredTypeOfSymbol(classSymbol); - const baseConstructorType = - getBaseConstructorTypeOfClass(classInstanceType); - - return baseConstructorType === nullWideningType; - } - - function checkThisBeforeSuper( - node: Node, - container: Node, - diagnosticMessage: DiagnosticMessage - ) { - const containingClassDecl = container.parent; - const baseTypeNode = - getClassExtendsHeritageElement(containingClassDecl); - - // If a containing class does not have extends clause or the class extends null - // skip checking whether super statement is called before "this" accessing. - if (baseTypeNode - && !classDeclarationExtendsNull(containingClassDecl)) - { - const superCall = - getSuperCallInConstructor( container); - - // We should give an error in the following cases: - // - No super-call - // - "this" is accessing before super-call. - // i.e super(this) - // this.x; super(); - // We want to make sure that super-call is done before accessing "this" so that - // "this" is not accessed as a parameter of the super-call. - if (!superCall || superCall.end > node.pos) { - // In ES6, super inside constructor of class-declaration has to precede "this" accessing - error(node, diagnosticMessage); - } - } - } - - function checkThisExpression(node: Node): Type { - // Stop at the first arrow function so that we can - // tell whether 'this' needs to be captured. - let container = getThisContainer( - node, /* includeArrowFunctions */ - true - ); - let capturedByArrowFunction = false; - - if (container.kind === SyntaxKind.Constructor) { - checkThisBeforeSuper( - node, - container, - Diagnostics - .super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class - ); - } - - // Now skip arrow functions to get the "real" owner of 'this'. - if (container.kind === SyntaxKind.ArrowFunction) { - container = getThisContainer( - container, /* includeArrowFunctions */ - false - ); - capturedByArrowFunction = true; - } - - switch (container.kind) { - case SyntaxKind.ModuleDeclaration: - error( - node, - Diagnostics - .this_cannot_be_referenced_in_a_module_or_namespace_body - ); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - break; - case SyntaxKind.EnumDeclaration: - error( - node, - Diagnostics - .this_cannot_be_referenced_in_current_location - ); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - break; - case SyntaxKind.Constructor: - if (isInConstructorArgumentInitializer(node, container)) { - error( - node, - Diagnostics - .this_cannot_be_referenced_in_constructor_arguments - ); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - } - break; - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - if (hasModifier(container, ModifierFlags.Static)) { - error( - node, - Diagnostics - .this_cannot_be_referenced_in_a_static_property_initializer - ); - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - } - break; - case SyntaxKind.ComputedPropertyName: - error( - node, - Diagnostics - .this_cannot_be_referenced_in_a_computed_property_name - ); - break; - } - - // When targeting es6, mark that we'll need to capture `this` in its lexically bound scope. - if (capturedByArrowFunction - && languageVersion < ScriptTarget.ES2015) - { - captureLexicalThis(node, container); - } - - const type = tryGetThisTypeAt( - node, /*includeGlobalThis*/ - true, - container - ); - if (noImplicitThis) { - const globalThisType = getTypeOfSymbol(globalThisSymbol); - if (type === globalThisType && capturedByArrowFunction) { - error( - node, - Diagnostics - .The_containing_arrow_function_captures_the_global_value_of_this - ); - } else if (!type) { - // With noImplicitThis, functions may not reference 'this' if it has type 'any' - const diag = error( - node, - Diagnostics - .this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation - ); - if (!isSourceFile(container)) { - const outsideThis = tryGetThisTypeAt(container); - if (outsideThis && outsideThis !== globalThisType) { - addRelatedInfo( - diag, - createDiagnosticForNode( - container, - Diagnostics - .An_outer_value_of_this_is_shadowed_by_this_container - ) - ); - } - } - } - } - return type || anyType; - } - - function tryGetThisTypeAt( - node: Node, - includeGlobalThis = true, - container = getThisContainer(node, false) - ): /*includeArrowFunctions*/ Type | undefined { - const isInJS = isInJSFile(node); - if (isFunctionLike(container) - && (!isInParameterInitializerBeforeContainingFunction(node) - || getThisParameter(container))) - { - // Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated. - // If this is a function in a JS file, it might be a class method. - const className = getClassNameFromPrototypeMethod(container); - if (isInJS && className) { - const classSymbol = checkExpression(className).symbol; - if (classSymbol && classSymbol.members - && (classSymbol.flags & SymbolFlags.Function)) - { - const classType = - (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType) - .thisType!; - return getFlowTypeOfReference(node, classType); - } - } // Check if it's a constructor definition, can be either a variable decl or function decl - // i.e. - // * /** @constructor */ function [name]() { ... } - // * /** @constructor */ var x = function() { ... } - else if (isInJS - && (container.kind === SyntaxKind.FunctionExpression - || container.kind === SyntaxKind.FunctionDeclaration) - && getJSDocClassTag(container)) - { - const classType = - (getDeclaredTypeOfSymbol(getMergedSymbol(container - .symbol)) as InterfaceType).thisType!; - return getFlowTypeOfReference(node, classType); - } - - const thisType = getThisTypeOfDeclaration(container) - || getContextualThisParameterType(container); - if (thisType) { - return getFlowTypeOfReference(node, thisType); - } - } - - if (isClassLike(container.parent)) { - const symbol = getSymbolOfNode(container.parent); - const type = hasModifier(container, ModifierFlags.Static) - ? getTypeOfSymbol(symbol) - : (getDeclaredTypeOfSymbol(symbol) as InterfaceType) - .thisType!; - return getFlowTypeOfReference(node, type); - } - - if (isInJS) { - const type = getTypeForThisExpressionFromJSDoc(container); - if (type && type !== errorType) { - return getFlowTypeOfReference(node, type); - } - } - if (isSourceFile(container)) { - // look up in the source file's locals or exports - if (container.commonJsModuleIndicator) { - const fileSymbol = getSymbolOfNode(container); - return fileSymbol && getTypeOfSymbol(fileSymbol); - } else if (includeGlobalThis) { - return getTypeOfSymbol(globalThisSymbol); - } - } - } - - function getExplicitThisType(node: Expression) { - const container = getThisContainer( - node, /*includeArrowFunctions*/ - false - ); - if (isFunctionLike(container)) { - const signature = getSignatureFromDeclaration(container); - if (signature.thisParameter) { - return getExplicitTypeOfSymbol(signature.thisParameter); - } - } - if (isClassLike(container.parent)) { - const symbol = getSymbolOfNode(container.parent); - return hasModifier(container, ModifierFlags.Static) - ? getTypeOfSymbol(symbol) - : (getDeclaredTypeOfSymbol(symbol) as InterfaceType) - .thisType!; - } - } - - function getClassNameFromPrototypeMethod(container: Node) { - // Check if it's the RHS of a x.prototype.y = function [name]() { .... } - if (container.kind === SyntaxKind.FunctionExpression - && isBinaryExpression(container.parent) - && getAssignmentDeclarationKind(container.parent) - === AssignmentDeclarationKind.PrototypeProperty) - { - // Get the 'x' of 'x.prototype.y = container' - return ((container.parent // x.prototype.y = container - .left as PropertyAccessExpression) // x.prototype.y - .expression as PropertyAccessExpression) // x.prototype - .expression; // x - } // x.prototype = { method() { } } - else if (container.kind === SyntaxKind.MethodDeclaration - && container.parent.kind === SyntaxKind.ObjectLiteralExpression - && isBinaryExpression(container.parent.parent) - && getAssignmentDeclarationKind(container.parent.parent) - === AssignmentDeclarationKind.Prototype) - { - return (container.parent.parent - .left as PropertyAccessExpression).expression; - } // x.prototype = { method: function() { } } - else if (container.kind === SyntaxKind.FunctionExpression - && container.parent.kind === SyntaxKind.PropertyAssignment - && container.parent.parent.kind - === SyntaxKind.ObjectLiteralExpression - && isBinaryExpression(container.parent.parent.parent) - && getAssignmentDeclarationKind(container.parent.parent.parent) - === AssignmentDeclarationKind.Prototype) - { - return (container.parent.parent.parent - .left as PropertyAccessExpression).expression; - } // Object.defineProperty(x, "method", { value: function() { } }); - // Object.defineProperty(x, "method", { set: (x: () => void) => void }); - // Object.defineProperty(x, "method", { get: () => function() { }) }); - else if (container.kind === SyntaxKind.FunctionExpression - && isPropertyAssignment(container.parent) - && isIdentifier(container.parent.name) - && (container.parent.name.escapedText === 'value' - || container.parent.name.escapedText === 'get' - || container.parent.name.escapedText === 'set') - && isObjectLiteralExpression(container.parent.parent) - && isCallExpression(container.parent.parent.parent) - && container.parent.parent.parent.arguments[2] - === container.parent.parent - && getAssignmentDeclarationKind(container.parent.parent.parent) - === AssignmentDeclarationKind - .ObjectDefinePrototypeProperty) - { - return (container.parent.parent.parent.arguments - [0] as PropertyAccessExpression).expression; - } // Object.defineProperty(x, "method", { value() { } }); - // Object.defineProperty(x, "method", { set(x: () => void) {} }); - // Object.defineProperty(x, "method", { get() { return () => {} } }); - else if (isMethodDeclaration(container) - && isIdentifier(container.name) - && (container.name.escapedText === 'value' - || container.name.escapedText === 'get' - || container.name.escapedText === 'set') - && isObjectLiteralExpression(container.parent) - && isCallExpression(container.parent.parent) - && container.parent.parent.arguments[2] === container.parent - && getAssignmentDeclarationKind(container.parent.parent) - === AssignmentDeclarationKind - .ObjectDefinePrototypeProperty) - { - return (container.parent.parent.arguments - [0] as PropertyAccessExpression).expression; - } - } - - function getTypeForThisExpressionFromJSDoc(node: Node) { - const jsdocType = getJSDocType(node); - if (jsdocType && jsdocType.kind === SyntaxKind.JSDocFunctionType) { - const jsDocFunctionType = jsdocType; - if (jsDocFunctionType.parameters.length > 0 - && jsDocFunctionType.parameters[0].name - && (jsDocFunctionType.parameters[0].name as Identifier) - .escapedText === InternalSymbolName.This) - { - return getTypeFromTypeNode(jsDocFunctionType.parameters[0] - .type!); - } - } - const thisTag = getJSDocThisTag(node); - if (thisTag && thisTag.typeExpression) { - return getTypeFromTypeNode(thisTag.typeExpression); - } - } - - function isInConstructorArgumentInitializer( - node: Node, - constructorDecl: Node - ): boolean { - return !!findAncestor( - node, - n => isFunctionLikeDeclaration(n) - ? 'quit' - : n.kind === SyntaxKind.Parameter - && n.parent === constructorDecl - ); - } - - function checkSuperExpression(node: Node): Type { - const isCallExpression = - node.parent.kind === SyntaxKind.CallExpression - && ( node.parent).expression === node; - - let container = getSuperContainer(node, /*stopOnFunctions*/ true); - let needToCaptureLexicalThis = false; - - // adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting - if (!isCallExpression) { - while (container - && container.kind === SyntaxKind.ArrowFunction) - { - container = getSuperContainer( - container, /*stopOnFunctions*/ - true - ); - needToCaptureLexicalThis = languageVersion - < ScriptTarget.ES2015; - } - } - - const canUseSuperExpression = - isLegalUsageOfSuperExpression(container); - let nodeCheckFlag: NodeCheckFlags = 0; - - if (!canUseSuperExpression) { - // issue more specific error if super is used in computed property name - // class A { foo() { return "1" }} - // class B { - // [super.foo()]() {} - // } - const current = findAncestor( - node, - n => n === container - ? 'quit' - : n.kind === SyntaxKind.ComputedPropertyName - ); - if (current - && current.kind === SyntaxKind.ComputedPropertyName) - { - error( - node, - Diagnostics - .super_cannot_be_referenced_in_a_computed_property_name - ); - } else if (isCallExpression) { - error( - node, - Diagnostics - .Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors - ); - } else if (!container || !container.parent - || !(isClassLike(container.parent) - || container.parent.kind - === SyntaxKind.ObjectLiteralExpression)) - { - error( - node, - Diagnostics - .super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions - ); - } else { - error( - node, - Diagnostics - .super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class - ); - } - return errorType; - } - - if (!isCallExpression - && container.kind === SyntaxKind.Constructor) - { - checkThisBeforeSuper( - node, - container, - Diagnostics - .super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class - ); - } - - if (hasModifier(container, ModifierFlags.Static) - || isCallExpression) - { - nodeCheckFlag = NodeCheckFlags.SuperStatic; - } else { - nodeCheckFlag = NodeCheckFlags.SuperInstance; - } - - getNodeLinks(node).flags |= nodeCheckFlag; - - // Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference. - // This is due to the fact that we emit the body of an async function inside of a generator function. As generator - // functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper - // uses an arrow function, which is permitted to reference `super`. - // - // There are two primary ways we can access `super` from within an async method. The first is getting the value of a property - // or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value - // of a property or indexed access, either as part of an assignment expression or destructuring assignment. - // - // The simplest case is reading a value, in which case we will emit something like the following: - // - // // ts - // ... - // async asyncMethod() { - // let x = await super.asyncMethod(); - // return x; - // } - // ... - // - // // js - // ... - // asyncMethod() { - // const _super = Object.create(null, { - // asyncMethod: { get: () => super.asyncMethod }, - // }); - // return __awaiter(this, arguments, Promise, function *() { - // let x = yield _super.asyncMethod.call(this); - // return x; - // }); - // } - // ... - // - // The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases - // are legal in ES6, but also likely less frequent, we only emit setters if there is an assignment: - // - // // ts - // ... - // async asyncMethod(ar: Promise) { - // [super.a, super.b] = await ar; - // } - // ... - // - // // js - // ... - // asyncMethod(ar) { - // const _super = Object.create(null, { - // a: { get: () => super.a, set: (v) => super.a = v }, - // b: { get: () => super.b, set: (v) => super.b = v } - // }; - // return __awaiter(this, arguments, Promise, function *() { - // [_super.a, _super.b] = yield ar; - // }); - // } - // ... - // - // Creating an object that has getter and setters instead of just an accessor function is required for destructuring assignments - // as a call expression cannot be used as the target of a destructuring assignment while a property access can. - // - // For element access expressions (`super[x]`), we emit a generic helper that forwards the element access in both situations. - if (container.kind === SyntaxKind.MethodDeclaration - && hasModifier(container, ModifierFlags.Async)) - { - if (isSuperProperty(node.parent) - && isAssignmentTarget(node.parent)) - { - getNodeLinks(container).flags |= NodeCheckFlags - .AsyncMethodWithSuperBinding; - } else { - getNodeLinks(container).flags |= NodeCheckFlags - .AsyncMethodWithSuper; - } - } - - if (needToCaptureLexicalThis) { - // call expressions are allowed only in constructors so they should always capture correct 'this' - // super property access expressions can also appear in arrow functions - - // in this case they should also use correct lexical this - captureLexicalThis(node.parent, container); - } - - if (container.parent.kind === SyntaxKind.ObjectLiteralExpression) { - if (languageVersion < ScriptTarget.ES2015) { - error( - node, - Diagnostics - .super_is_only_allowed_in_members_of_object_literal_expressions_when_option_target_is_ES2015_or_higher - ); - return errorType; - } else { - // for object literal assume that type of 'super' is 'any' - return anyType; - } - } - - // at this point the only legal case for parent is ClassLikeDeclaration - const classLikeDeclaration = container - .parent; - if (!getClassExtendsHeritageElement(classLikeDeclaration)) { - error( - node, - Diagnostics.super_can_only_be_referenced_in_a_derived_class - ); - return errorType; - } - - const classType = - getDeclaredTypeOfSymbol(getSymbolOfNode(classLikeDeclaration)); - const baseClassType = classType && getBaseTypes(classType)[0]; - if (!baseClassType) { - return errorType; - } - - if (container.kind === SyntaxKind.Constructor - && isInConstructorArgumentInitializer(node, container)) - { - // issue custom error message for super property access in constructor arguments (to be aligned with old compiler) - error( - node, - Diagnostics - .super_cannot_be_referenced_in_constructor_arguments - ); - return errorType; - } - - return nodeCheckFlag === NodeCheckFlags.SuperStatic - ? getBaseConstructorTypeOfClass(classType) - : getTypeWithThisArgument(baseClassType, classType.thisType); - - function isLegalUsageOfSuperExpression(container: Node): boolean { - if (!container) { - return false; - } - - if (isCallExpression) { - // TS 1.0 SPEC (April 2014): 4.8.1 - // Super calls are only permitted in constructors of derived classes - return container.kind === SyntaxKind.Constructor; - } else { - // TS 1.0 SPEC (April 2014) - // 'super' property access is allowed - // - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance - // - In a static member function or static member accessor - - // topmost container must be something that is directly nested in the class declaration\object literal expression - if (isClassLike(container.parent) - || container.parent.kind - === SyntaxKind.ObjectLiteralExpression) - { - if (hasModifier(container, ModifierFlags.Static)) { - return container.kind - === SyntaxKind.MethodDeclaration - || container.kind - === SyntaxKind.MethodSignature - || container.kind === SyntaxKind.GetAccessor - || container.kind === SyntaxKind.SetAccessor; - } else { - return container.kind - === SyntaxKind.MethodDeclaration - || container.kind - === SyntaxKind.MethodSignature - || container.kind === SyntaxKind.GetAccessor - || container.kind === SyntaxKind.SetAccessor - || container.kind - === SyntaxKind.PropertyDeclaration - || container.kind - === SyntaxKind.PropertySignature - || container.kind === SyntaxKind.Constructor; - } - } - } - - return false; - } - } - - function getContainingObjectLiteral(func: - SignatureDeclaration): ObjectLiteralExpression | undefined - { - return (func.kind === SyntaxKind.MethodDeclaration - || func.kind === SyntaxKind.GetAccessor - || func.kind === SyntaxKind.SetAccessor) - && func.parent.kind === SyntaxKind.ObjectLiteralExpression - ? func.parent - : func.kind === SyntaxKind.FunctionExpression - && func.parent.kind === SyntaxKind.PropertyAssignment - ? func.parent.parent - : undefined; - } - - function getThisTypeArgument(type: Type): Type | undefined { - return getObjectFlags(type) & ObjectFlags.Reference - && ( type).target === globalThisType - ? getTypeArguments( type)[0] - : undefined; - } - - function getThisTypeFromContextualType(type: Type): Type | undefined { - return mapType(type, t => { - return t.flags & TypeFlags.Intersection - ? forEach( - ( t).types, - getThisTypeArgument - ) - : getThisTypeArgument(t); - }); - } - - function getContextualThisParameterType(func: - SignatureDeclaration): Type | undefined - { - if (func.kind === SyntaxKind.ArrowFunction) { - return undefined; - } - if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - const contextualSignature = getContextualSignature(func); - if (contextualSignature) { - const thisParameter = contextualSignature.thisParameter; - if (thisParameter) { - return getTypeOfSymbol(thisParameter); - } - } - } - const inJs = isInJSFile(func); - if (noImplicitThis || inJs) { - const containingLiteral = getContainingObjectLiteral(func); - if (containingLiteral) { - // We have an object literal method. Check if the containing object literal has a contextual type - // that includes a ThisType. If so, T is the contextual type for 'this'. We continue looking in - // any directly enclosing object literals. - const contextualType = - getApparentTypeOfContextualType(containingLiteral); - let literal = containingLiteral; - let type = contextualType; - while (type) { - const thisType = getThisTypeFromContextualType(type); - if (thisType) { - return instantiateType( - thisType, - getMapperFromContext(getInferenceContext(containingLiteral)) - ); - } - if (literal.parent.kind - !== SyntaxKind.PropertyAssignment) - { - break; - } - literal = literal.parent - .parent; - type = getApparentTypeOfContextualType(literal); - } - // There was no contextual ThisType for the containing object literal, so the contextual type - // for 'this' is the non-null form of the contextual type for the containing object literal or - // the type of the object literal itself. - return getWidenedType(contextualType - ? getNonNullableType(contextualType) - : checkExpressionCached(containingLiteral)); - } - // In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the - // contextual type for 'this' is 'obj'. - const parent = func.parent; - if (parent.kind === SyntaxKind.BinaryExpression - && ( parent).operatorToken.kind - === SyntaxKind.EqualsToken) - { - const target = ( parent).left; - if (isAccessExpression(target)) { - const { expression } = target; - // Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }` - if (inJs && isIdentifier(expression)) { - const sourceFile = getSourceFileOfNode(parent); - if (sourceFile.commonJsModuleIndicator - && getResolvedSymbol(expression) - === sourceFile.symbol) - { - return undefined; - } - } - - return getWidenedType(checkExpressionCached(expression)); - } - } - } - return undefined; - } - - // Return contextual type of parameter or undefined if no contextual type is available - function getContextuallyTypedParameterType( - parameter: ParameterDeclaration, - forCache: boolean - ): Type | undefined { - const func = parameter.parent; - if (!isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - return undefined; - } - const iife = getImmediatelyInvokedFunctionExpression(func); - if (iife && iife.arguments) { - const args = getEffectiveCallArguments(iife); - const indexOfParameter = func.parameters.indexOf(parameter); - if (parameter.dotDotDotToken) { - return getSpreadArgumentType( - args, - indexOfParameter, - args.length, - anyType, /*context*/ - undefined - ); - } - const links = getNodeLinks(iife); - const cached = links.resolvedSignature; - links.resolvedSignature = anySignature; - const type = indexOfParameter < args.length - ? getWidenedLiteralType(checkExpression(args - [indexOfParameter])) - : parameter.initializer - ? undefined - : undefinedWideningType; - links.resolvedSignature = cached; - return type; - } - let contextualSignature = getContextualSignature(func); - if (contextualSignature) { - if (forCache) { - // Calling the below guarantees the types are primed and assigned in the same way - // as when the parameter is reached via `checkFunctionExpressionOrObjectLiteralMethod`. - // This should prevent any uninstantiated inference variables in the contextual signature - // from leaking, and should lock in cached parameter types via `assignContextualParameterTypes` - // which we will then immediately use the results of below. - contextuallyCheckFunctionExpressionOrObjectLiteralMethod(func); - const type = getTypeOfSymbol(getMergedSymbol(func.symbol)); - if (isTypeAny(type)) { - return type; - } - contextualSignature = getSignaturesOfType( - type, - SignatureKind.Call - )[0]; - } - const index = func.parameters.indexOf(parameter) - - (getThisParameter(func) ? 1 : 0); - return parameter.dotDotDotToken - && lastOrUndefined(func.parameters) === parameter - ? getRestTypeAtPosition(contextualSignature, index) - : tryGetTypeAtPosition(contextualSignature, index); - } - } - - function getContextualTypeForVariableLikeDeclaration(declaration: - VariableLikeDeclaration): Type | undefined - { - const typeNode = getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - switch (declaration.kind) { - case SyntaxKind.Parameter: - return getContextuallyTypedParameterType( - declaration, /*forCache*/ - false - ); - case SyntaxKind.BindingElement: - return getContextualTypeForBindingElement(declaration); - // By default, do nothing and return undefined - only parameters and binding elements have context implied by a parent - } - } - - function getContextualTypeForBindingElement(declaration: - BindingElement): Type | undefined - { - const parentDeclaration = declaration.parent.parent; - const name = declaration.propertyName || declaration.name; - const parentType = - getContextualTypeForVariableLikeDeclaration(parentDeclaration); - if (parentType && !isBindingPattern(name) - && !isComputedNonLiteralName(name)) - { - const nameType = getLiteralTypeFromPropertyName(name); - if (isTypeUsableAsPropertyName(nameType)) { - const text = getPropertyNameFromType(nameType); - return getTypeOfPropertyOfType(parentType, text); - } - } - } - - // In a variable, parameter or property declaration with a type annotation, - // the contextual type of an initializer expression is the type of the variable, parameter or property. - // Otherwise, in a parameter declaration of a contextually typed function expression, - // the contextual type of an initializer expression is the contextual type of the parameter. - // Otherwise, in a variable or parameter declaration with a binding pattern name, - // the contextual type of an initializer expression is the type implied by the binding pattern. - // Otherwise, in a binding pattern inside a variable or parameter declaration, - // the contextual type of an initializer expression is the type annotation of the containing declaration, if present. - function getContextualTypeForInitializerExpression(node: - Expression): Type | undefined - { - const declaration = node.parent; - if (hasInitializer(declaration) - && node === declaration.initializer) - { - const result = - getContextualTypeForVariableLikeDeclaration(declaration); - if (result) { - return result; - } - if (isBindingPattern(declaration - .name)) - { // This is less a contextual type and more an implied shape - in some cases, this may be undesirable - return getTypeFromBindingPattern( - declaration.name, /*includePatternInType*/ - true, /*reportErrors*/ - false - ); - } - } - return undefined; - } - - function getContextualTypeForReturnExpression(node: Expression): Type - | undefined - { - const func = getContainingFunction(node); - if (func) { - const functionFlags = getFunctionFlags(func); - if (functionFlags - & FunctionFlags - .Generator) - { // AsyncGenerator function or Generator function - return undefined; - } - - const contextualReturnType = getContextualReturnType(func); - if (contextualReturnType) { - if (functionFlags - & FunctionFlags.Async) - { // Async function - const contextualAwaitedType = - getAwaitedTypeOfPromise(contextualReturnType); - return contextualAwaitedType - && getUnionType([contextualAwaitedType, - createPromiseLikeType(contextualAwaitedType)]); - } - return contextualReturnType; // Regular function - } - } - return undefined; - } - - function getContextualTypeForAwaitOperand(node: AwaitExpression): Type - | undefined - { - const contextualType = getContextualType(node); - if (contextualType) { - const contextualAwaitedType = getAwaitedType(contextualType); - return contextualAwaitedType - && getUnionType([contextualAwaitedType, - createPromiseLikeType(contextualAwaitedType)]); - } - return undefined; - } - - function getContextualTypeForYieldOperand(node: YieldExpression): Type - | undefined - { - const func = getContainingFunction(node); - if (func) { - const functionFlags = getFunctionFlags(func); - const contextualReturnType = getContextualReturnType(func); - if (contextualReturnType) { - return node.asteriskToken - ? contextualReturnType - : getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Yield, - contextualReturnType, - (functionFlags & FunctionFlags.Async) !== 0 - ); - } - } - - return undefined; - } - - function isInParameterInitializerBeforeContainingFunction(node: Node) { - let inBindingInitializer = false; - while (node.parent && !isFunctionLike(node.parent)) { - if (isParameter(node.parent) - && (inBindingInitializer - || node.parent.initializer === node)) - { - return true; - } - if (isBindingElement(node.parent) - && node.parent.initializer === node) - { - inBindingInitializer = true; - } - - node = node.parent; - } - - return false; - } - - function getContextualIterationType( - kind: IterationTypeKind, - functionDecl: SignatureDeclaration - ): Type | undefined { - const isAsync = - !!(getFunctionFlags(functionDecl) & FunctionFlags.Async); - const contextualReturnType = getContextualReturnType(functionDecl); - if (contextualReturnType) { - return getIterationTypeOfGeneratorFunctionReturnType( - kind, - contextualReturnType, - isAsync - ) - || undefined; - } - - return undefined; - } - - function getContextualReturnType(functionDecl: - SignatureDeclaration): Type | undefined - { - // If the containing function has a return type annotation, is a constructor, or is a get accessor whose - // corresponding set accessor has a type annotation, return statements in the function are contextually typed - const returnType = getReturnTypeFromAnnotation(functionDecl); - if (returnType) { - return returnType; - } - // Otherwise, if the containing function is contextually typed by a function type with exactly one call signature - // and that call signature is non-generic, return statements are contextually typed by the return type of the signature - const signature = - getContextualSignatureForFunctionLikeDeclaration( functionDecl); - if (signature && !isResolvingReturnTypeOfSignature(signature)) { - return getReturnTypeOfSignature(signature); - } - return undefined; - } - - // In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter. - function getContextualTypeForArgument( - callTarget: CallLikeExpression, - arg: Expression, - contextFlags?: ContextFlags - ): Type | undefined { - const args = getEffectiveCallArguments(callTarget); - const argIndex = args - .indexOf(arg); // -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression - return argIndex === -1 - ? undefined - : getContextualTypeForArgumentAtIndex( - callTarget, - argIndex, - contextFlags - ); - } - - function getContextualTypeForArgumentAtIndex( - callTarget: CallLikeExpression, - argIndex: number, - contextFlags?: ContextFlags - ): Type { - // If we're already in the process of resolving the given signature, don't resolve again as - // that could cause infinite recursion. Instead, return anySignature. - let signature = - getNodeLinks(callTarget).resolvedSignature - === resolvingSignature - ? resolvingSignature - : getResolvedSignature(callTarget); - if (contextFlags && contextFlags & ContextFlags.BaseConstraint - && signature.target && !hasTypeArguments(callTarget)) - { - signature = getBaseSignature(signature.target); - } - - if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) { - return getEffectiveFirstArgumentForJsxSignature( - signature, - callTarget - ); - } - return getTypeAtPosition(signature, argIndex); - } - - function getContextualTypeForSubstitutionExpression( - template: TemplateExpression, - substitutionExpression: Expression - ) { - if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) { - return getContextualTypeForArgument( - template.parent, - substitutionExpression - ); - } - - return undefined; - } - - function getContextualTypeForBinaryOperand( - node: Expression, - contextFlags?: ContextFlags - ): Type | undefined { - const binaryExpression = node.parent; - const { left, operatorToken, right } = binaryExpression; - switch (operatorToken.kind) { - case SyntaxKind.EqualsToken: - if (node !== right) { - return undefined; - } - const contextSensitive = - getIsContextSensitiveAssignmentOrContextType(binaryExpression); - if (!contextSensitive) { - return undefined; - } - return contextSensitive === true - ? getTypeOfExpression(left) - : contextSensitive; - case SyntaxKind.BarBarToken: - case SyntaxKind.QuestionQuestionToken: - // When an || expression has a contextual type, the operands are contextually typed by that type, except - // when that type originates in a binding pattern, the right operand is contextually typed by the type of - // the left operand. When an || expression has no contextual type, the right operand is contextually typed - // by the type of the left operand, except for the special case of Javascript declarations of the form - // `namespace.prop = namespace.prop || {}`. - const type = getContextualType( - binaryExpression, - contextFlags - ); - return node === right - && (type && type.pattern || !type - && !isDefaultedExpandoInitializer(binaryExpression)) - ? getTypeOfExpression(left) - : type; - case SyntaxKind.AmpersandAmpersandToken: - case SyntaxKind.CommaToken: - return node === right - ? getContextualType(binaryExpression, contextFlags) - : undefined; - default: - return undefined; - } - } - - // In an assignment expression, the right operand is contextually typed by the type of the left operand. - // Don't do this for assignment declarations unless there is a type tag on the assignment, to avoid circularity from checking the right operand. - function getIsContextSensitiveAssignmentOrContextType(binaryExpression: - BinaryExpression): boolean | Type - { - const kind = getAssignmentDeclarationKind(binaryExpression); - switch (kind) { - case AssignmentDeclarationKind.None: - return true; - case AssignmentDeclarationKind.Property: - case AssignmentDeclarationKind.ExportsProperty: - case AssignmentDeclarationKind.Prototype: - case AssignmentDeclarationKind.PrototypeProperty: - // If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration. - // See `bindStaticPropertyAssignment` in `binder.ts`. - if (!binaryExpression.left.symbol) { - return true; - } else { - const decl = binaryExpression.left.symbol - .valueDeclaration; - if (!decl) { - return false; - } - const lhs = cast( - binaryExpression.left, - isAccessExpression - ); - const overallAnnotation = - getEffectiveTypeAnnotationNode(decl); - if (overallAnnotation) { - return getTypeFromTypeNode(overallAnnotation); - } else if (isIdentifier(lhs.expression)) { - const id = lhs.expression; - const parentSymbol = resolveName( - id, - id.escapedText, - SymbolFlags.Value, - undefined, - id.escapedText, /*isUse*/ - true - ); - if (parentSymbol) { - const annotated = - getEffectiveTypeAnnotationNode(parentSymbol - .valueDeclaration); - if (annotated) { - const nameStr = - getElementOrPropertyAccessName(lhs); - if (nameStr !== undefined) { - const type = - getTypeOfPropertyOfContextualType( - getTypeFromTypeNode(annotated), - nameStr - ); - return type || false; - } - } - return false; - } - } - return !isInJSFile(decl); - } - case AssignmentDeclarationKind.ModuleExports: - case AssignmentDeclarationKind.ThisProperty: - if (!binaryExpression.symbol) return true; - if (binaryExpression.symbol.valueDeclaration) { - const annotated = - getEffectiveTypeAnnotationNode(binaryExpression - .symbol.valueDeclaration); - if (annotated) { - const type = getTypeFromTypeNode(annotated); - if (type) { - return type; - } - } - } - if (kind - === AssignmentDeclarationKind.ModuleExports) - return false; - const thisAccess = cast( - binaryExpression.left, - isAccessExpression - ); - if (!isObjectLiteralMethod(getThisContainer( - thisAccess.expression, /*includeArrowFunctions*/ - false - ))) { - return false; - } - const thisType = - checkThisExpression(thisAccess.expression); - const nameStr = getElementOrPropertyAccessName(thisAccess); - return nameStr !== undefined && thisType - && getTypeOfPropertyOfContextualType(thisType, nameStr) - || false; - case AssignmentDeclarationKind.ObjectDefinePropertyValue: - case AssignmentDeclarationKind.ObjectDefinePropertyExports: - case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: - return Debug.fail('Does not apply'); - default: - return Debug.assertNever(kind); - } - } - - function getTypeOfPropertyOfContextualType( - type: Type, - name: __String - ) { - return mapType(type, t => { - if (isGenericMappedType(t)) { - const constraint = getConstraintTypeFromMappedType(t); - const constraintOfConstraint = - getBaseConstraintOfType(constraint) || constraint; - const propertyNameType = - getLiteralType(unescapeLeadingUnderscores(name)); - if (isTypeAssignableTo( - propertyNameType, - constraintOfConstraint - )) { - return substituteIndexedMappedType( - t, - propertyNameType - ); - } - } else if (t.flags & TypeFlags.StructuredType) { - const prop = getPropertyOfType(t, name); - if (prop) { - return getTypeOfSymbol(prop); - } - if (isTupleType(t)) { - const restType = getRestTypeOfTupleType(t); - if (restType && isNumericLiteralName(name) - && +name >= 0) - { - return restType; - } - } - return isNumericLiteralName(name) - && getIndexTypeOfContextualType(t, IndexKind.Number) - || getIndexTypeOfContextualType(t, IndexKind.String); - } - return undefined; - }, /*noReductions*/ true); - } - - function getIndexTypeOfContextualType(type: Type, kind: IndexKind) { - return mapType( - type, - t => getIndexTypeOfStructuredType(t, kind), /*noReductions*/ - true - ); - } - - // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of - // the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one - // exists. Otherwise, it is the type of the string index signature in T, if one exists. - function getContextualTypeForObjectLiteralMethod( - node: MethodDeclaration, - contextFlags?: ContextFlags - ): Type | undefined { - Debug.assert(isObjectLiteralMethod(node)); - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return undefined; - } - return getContextualTypeForObjectLiteralElement( - node, - contextFlags - ); - } - - function getContextualTypeForObjectLiteralElement( - element: ObjectLiteralElementLike, - contextFlags?: ContextFlags - ) { - const objectLiteral = element.parent; - const type = getApparentTypeOfContextualType( - objectLiteral, - contextFlags - ); - if (type) { - if (!hasNonBindableDynamicName(element)) { - // For a (non-symbol) computed property, there is no reason to look up the name - // in the type. It will just be "__computed", which does not appear in any - // SymbolTable. - const symbolName = getSymbolOfNode(element).escapedName; - const propertyType = getTypeOfPropertyOfContextualType( - type, - symbolName - ); - if (propertyType) { - return propertyType; - } - } - return isNumericName(element.name!) - && getIndexTypeOfContextualType(type, IndexKind.Number) - || getIndexTypeOfContextualType(type, IndexKind.String); - } - return undefined; - } - - // In an array literal contextually typed by a type T, the contextual type of an element expression at index N is - // the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature, - // it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated - // type of T. - function getContextualTypeForElementExpression( - arrayContextualType: Type | undefined, - index: number - ): Type | undefined { - return arrayContextualType && ( - getTypeOfPropertyOfContextualType( - arrayContextualType, - '' + index as __String - ) - || getIteratedTypeOrElementType( - IterationUse.Element, - arrayContextualType, - undefinedType, /*errorNode*/ - undefined, /*checkAssignability*/ - false - ) - ); - } - - // In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. - function getContextualTypeForConditionalOperand( - node: Expression, - contextFlags?: ContextFlags - ): Type | undefined { - const conditional = node.parent; - return node === conditional.whenTrue - || node === conditional.whenFalse - ? getContextualType(conditional, contextFlags) - : undefined; - } - - function getContextualTypeForChildJsxExpression( - node: JsxElement, - child: JsxChild - ) { - const attributesType = - getApparentTypeOfContextualType(node.openingElement.tagName); - // JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty) - const jsxChildrenPropertyName = - getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - if (!(attributesType && !isTypeAny(attributesType) - && jsxChildrenPropertyName && jsxChildrenPropertyName !== '')) - { - return undefined; - } - const realChildren = getSemanticJsxChildren(node.children); - const childIndex = realChildren.indexOf(child); - const childFieldType = getTypeOfPropertyOfContextualType( - attributesType, - jsxChildrenPropertyName - ); - return childFieldType - && (realChildren.length === 1 - ? childFieldType - : mapType(childFieldType, t => { - if (isArrayLikeType(t)) { - return getIndexedAccessType( - t, - getLiteralType(childIndex) - ); - } else { - return t; - } - }, /*noReductions*/ true)); - } - - function getContextualTypeForJsxExpression(node: JsxExpression): Type - | undefined - { - const exprParent = node.parent; - return isJsxAttributeLike(exprParent) - ? getContextualType(node) - : isJsxElement(exprParent) - ? getContextualTypeForChildJsxExpression(exprParent, node) - : undefined; - } - - function getContextualTypeForJsxAttribute(attribute: JsxAttribute - | JsxSpreadAttribute): Type | undefined - { - // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type - // which is a type of the parameter of the signature we are trying out. - // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName - if (isJsxAttribute(attribute)) { - const attributesType = - getApparentTypeOfContextualType(attribute.parent); - if (!attributesType || isTypeAny(attributesType)) { - return undefined; - } - return getTypeOfPropertyOfContextualType( - attributesType, - attribute.name.escapedText - ); - } else { - return getContextualType(attribute.parent); - } - } - - // Return true if the given expression is possibly a discriminant value. We limit the kinds of - // expressions we check to those that don't depend on their contextual type in order not to cause - // recursive (and possibly infinite) invocations of getContextualType. - function isPossiblyDiscriminantValue(node: Expression): boolean { - switch (node.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.Identifier: - case SyntaxKind.UndefinedKeyword: - return true; - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ParenthesizedExpression: - return isPossiblyDiscriminantValue(( node).expression); - case SyntaxKind.JsxExpression: - return !(node as JsxExpression).expression - || isPossiblyDiscriminantValue((node as JsxExpression) - .expression!); - } - return false; - } - - function discriminateContextualTypeByObjectMembers( - node: ObjectLiteralExpression, - contextualType: UnionType - ) { - return discriminateTypeByDiscriminableItems(contextualType, map( - filter( - node.properties, - p => !!p.symbol && p.kind === SyntaxKind.PropertyAssignment - && isPossiblyDiscriminantValue(p.initializer) - && isDiscriminantProperty( - contextualType, - p.symbol.escapedName - ) - ), - prop => ([() => checkExpression((prop as PropertyAssignment) - .initializer), prop.symbol.escapedName] as [() => Type, - __String]) - ), isTypeAssignableTo, contextualType); - } - - function discriminateContextualTypeByJSXAttributes( - node: JsxAttributes, - contextualType: UnionType - ) { - return discriminateTypeByDiscriminableItems(contextualType, map( - filter( - node.properties, - p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute - && isDiscriminantProperty( - contextualType, - p.symbol.escapedName - ) - && (!p.initializer - || isPossiblyDiscriminantValue(p.initializer)) - ), - prop => ([!(prop as JsxAttribute).initializer - ? (() => trueType) - : (() => checkExpression((prop as JsxAttribute) - .initializer!)), - prop.symbol.escapedName] as [() => Type, __String]) - ), isTypeAssignableTo, contextualType); - } - - // Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily - // be "pushed" onto a node using the contextualType property. - function getApparentTypeOfContextualType( - node: Expression | MethodDeclaration, - contextFlags?: ContextFlags - ): Type | undefined { - const contextualType = isObjectLiteralMethod(node) - ? getContextualTypeForObjectLiteralMethod(node, contextFlags) - : getContextualType(node, contextFlags); - const instantiatedType = instantiateContextualType( - contextualType, - node, - contextFlags - ); - if (instantiatedType - && !(contextFlags && contextFlags & ContextFlags.NoConstraints - && instantiatedType.flags & TypeFlags.TypeVariable)) - { - const apparentType = mapType( - instantiatedType, - getApparentType, /*noReductions*/ - true - ); - if (apparentType.flags & TypeFlags.Union) { - if (isObjectLiteralExpression(node)) { - return discriminateContextualTypeByObjectMembers( - node, - apparentType as UnionType - ); - } else if (isJsxAttributes(node)) { - return discriminateContextualTypeByJSXAttributes( - node, - apparentType as UnionType - ); - } - } - return apparentType; - } - } - - // If the given contextual type contains instantiable types and if a mapper representing - // return type inferences is available, instantiate those types using that mapper. - function instantiateContextualType( - contextualType: Type | undefined, - node: Node, - contextFlags?: ContextFlags - ): Type | undefined { - if (contextualType - && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) - { - const inferenceContext = getInferenceContext(node); - // If no inferences have been made, nothing is gained from instantiating as type parameters - // would just be replaced with their defaults similar to the apparent type. - if (inferenceContext - && some( - inferenceContext.inferences, - hasInferenceCandidates - )) - { - // For contextual signatures we incorporate all inferences made so far, e.g. from return - // types as well as arguments to the left in a function call. - if (contextFlags - && contextFlags & ContextFlags.Signature) - { - return instantiateInstantiableTypes( - contextualType, - inferenceContext.nonFixingMapper - ); - } - // For other purposes (e.g. determining whether to produce literal types) we only - // incorporate inferences made from the return type in a function call. - if (inferenceContext.returnMapper) { - return instantiateInstantiableTypes( - contextualType, - inferenceContext.returnMapper - ); - } - } - } - return contextualType; - } - - // This function is similar to instantiateType, except that (a) it only instantiates types that - // are classified as instantiable (i.e. it doesn't instantiate object types), and (b) it performs - // no reductions on instantiated union types. - function instantiateInstantiableTypes( - type: Type, - mapper: TypeMapper - ): Type { - if (type.flags & TypeFlags.Instantiable) { - return instantiateType(type, mapper); - } - if (type.flags & TypeFlags.Union) { - return getUnionType( - map( - ( type).types, - t => instantiateInstantiableTypes(t, mapper) - ), - UnionReduction.None - ); - } - if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map( - ( type).types, - t => instantiateInstantiableTypes(t, mapper) - )); - } - return type; - } - - /** - * Whoa! Do you really want to use this function? - * - * Unless you're trying to get the *non-apparent* type for a - * value-literal type or you're authoring relevant portions of this algorithm, - * you probably meant to use 'getApparentTypeOfContextualType'. - * Otherwise this may not be very useful. - * - * In cases where you *are* working on this function, you should understand - * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'. - * - * - Use 'getContextualType' when you are simply going to propagate the result to the expression. - * - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type. - * - * @param node the expression whose contextual type will be returned. - * @returns the contextual type of an expression. - */ - function getContextualType( - node: Expression, - contextFlags?: ContextFlags - ): Type | undefined { - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return undefined; - } - if (node.contextualType) { - return node.contextualType; - } - const { parent } = node; - switch (parent.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.BindingElement: - return getContextualTypeForInitializerExpression(node); - case SyntaxKind.ArrowFunction: - case SyntaxKind.ReturnStatement: - return getContextualTypeForReturnExpression(node); - case SyntaxKind.YieldExpression: - return getContextualTypeForYieldOperand( parent); - case SyntaxKind.AwaitExpression: - return getContextualTypeForAwaitOperand( parent); - case SyntaxKind.CallExpression: - if (( parent).expression.kind - === SyntaxKind.ImportKeyword) - { - return stringType; - } - /* falls through */ - case SyntaxKind.NewExpression: - return getContextualTypeForArgument( - parent, - node, - contextFlags - ); - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return isConstTypeReference(( parent) - .type) - ? undefined - : getTypeFromTypeNode(( parent) - .type); - case SyntaxKind.BinaryExpression: - return getContextualTypeForBinaryOperand( - node, - contextFlags - ); - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - return getContextualTypeForObjectLiteralElement( - parent, - contextFlags - ); - case SyntaxKind.SpreadAssignment: - return getApparentTypeOfContextualType( - parent.parent as ObjectLiteralExpression, - contextFlags - ); - case SyntaxKind.ArrayLiteralExpression: { - const arrayLiteral = parent; - const type = getApparentTypeOfContextualType( - arrayLiteral, - contextFlags - ); - return getContextualTypeForElementExpression( - type, - indexOfNode(arrayLiteral.elements, node) - ); - } - case SyntaxKind.ConditionalExpression: - return getContextualTypeForConditionalOperand( - node, - contextFlags - ); - case SyntaxKind.TemplateSpan: - Debug - .assert(parent.parent.kind - === SyntaxKind.TemplateExpression); - return getContextualTypeForSubstitutionExpression( - parent.parent, - node - ); - case SyntaxKind.ParenthesizedExpression: { - // Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast. - const tag = isInJSFile(parent) - ? getJSDocTypeTag(parent) - : undefined; - return tag - ? getTypeFromTypeNode(tag.typeExpression.type) - : getContextualType( - parent, - contextFlags - ); - } - case SyntaxKind.JsxExpression: - return getContextualTypeForJsxExpression( parent); - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxSpreadAttribute: - return getContextualTypeForJsxAttribute( parent); - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSelfClosingElement: - return getContextualJsxElementAttributesType( - parent, - contextFlags - ); - } - return undefined; - } - - function getInferenceContext(node: Node) { - const ancestor = findAncestor(node, n => !!n.inferenceContext); - return ancestor && ancestor.inferenceContext!; - } - - function getContextualJsxElementAttributesType( - node: JsxOpeningLikeElement, - contextFlags?: ContextFlags - ) { - if (isJsxOpeningElement(node) && node.parent.contextualType - && contextFlags !== ContextFlags.BaseConstraint) - { - // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit - // _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type - // (as below) instead! - return node.parent.contextualType; - } - return getContextualTypeForArgumentAtIndex(node, 0, contextFlags); - } - - function getEffectiveFirstArgumentForJsxSignature( - signature: Signature, - node: JsxOpeningLikeElement - ) { - return getJsxReferenceKind(node) !== JsxReferenceKind.Component - ? getJsxPropsTypeFromCallSignature(signature, node) - : getJsxPropsTypeFromClassType(signature, node); - } - - function getJsxPropsTypeFromCallSignature( - sig: Signature, - context: JsxOpeningLikeElement - ) { - let propsType = getTypeOfFirstParameterOfSignatureWithFallback( - sig, - unknownType - ); - propsType = getJsxManagedAttributesFromLocatedAttributes( - context, - getJsxNamespaceAt(context), - propsType - ); - const intrinsicAttribs = getJsxType( - JsxNames.IntrinsicAttributes, - context - ); - if (intrinsicAttribs !== errorType) { - propsType = intersectTypes(intrinsicAttribs, propsType); - } - return propsType; - } - - function getJsxPropsTypeForSignatureFromMember( - sig: Signature, - forcedLookupLocation: __String - ) { - if (sig.unionSignatures) { - // JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input - // instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature, - // get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur - // for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input. - // The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane. - const results: Type[] = []; - for (const signature of sig.unionSignatures) { - const instance = getReturnTypeOfSignature(signature); - if (isTypeAny(instance)) { - return instance; - } - const propType = getTypeOfPropertyOfType( - instance, - forcedLookupLocation - ); - if (!propType) { - return; - } - results.push(propType); - } - return getIntersectionType(results); - } - const instanceType = getReturnTypeOfSignature(sig); - return isTypeAny(instanceType) - ? instanceType - : getTypeOfPropertyOfType(instanceType, forcedLookupLocation); - } - - function getStaticTypeOfReferencedJsxConstructor(context: - JsxOpeningLikeElement) - { - if (isJsxIntrinsicIdentifier(context.tagName)) { - const result = - getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context); - const fakeSignature = createSignatureForJSXIntrinsic( - context, - result - ); - return getOrCreateTypeFromSignature(fakeSignature); - } - const tagType = checkExpressionCached(context.tagName); - if (tagType.flags & TypeFlags.StringLiteral) { - const result = getIntrinsicAttributesTypeFromStringLiteralType( - tagType as StringLiteralType, - context - ); - if (!result) { - return errorType; - } - const fakeSignature = createSignatureForJSXIntrinsic( - context, - result - ); - return getOrCreateTypeFromSignature(fakeSignature); - } - return tagType; - } - - function getJsxManagedAttributesFromLocatedAttributes( - context: JsxOpeningLikeElement, - ns: Symbol, - attributesType: Type - ) { - const managedSym = getJsxLibraryManagedAttributes(ns); - if (managedSym) { - const declaredManagedType = - getDeclaredTypeOfSymbol(managedSym); - const ctorType = - getStaticTypeOfReferencedJsxConstructor(context); - if (length((declaredManagedType as GenericType).typeParameters) - >= 2) - { - const args = fillMissingTypeArguments( - [ctorType, attributesType], - (declaredManagedType as GenericType).typeParameters, - 2, - isInJSFile(context) - ); - return createTypeReference( - (declaredManagedType as GenericType), - args - ); - } else if (length(declaredManagedType.aliasTypeArguments) - >= 2) - { - const args = fillMissingTypeArguments( - [ctorType, attributesType], - declaredManagedType.aliasTypeArguments, - 2, - isInJSFile(context) - ); - return getTypeAliasInstantiation( - declaredManagedType.aliasSymbol!, - args - ); - } - } - return attributesType; - } - - function getJsxPropsTypeFromClassType( - sig: Signature, - context: JsxOpeningLikeElement - ) { - const ns = getJsxNamespaceAt(context); - const forcedLookupLocation = getJsxElementPropertiesName(ns); - let attributesType = forcedLookupLocation === undefined - ? // If there is no type ElementAttributesProperty, return the type of the first parameter of the signature, which should be the props type - getTypeOfFirstParameterOfSignatureWithFallback( - sig, - unknownType - ) - : forcedLookupLocation === '' - ? // If there is no e.g. 'props' member in ElementAttributesProperty, use the element class type instead - getReturnTypeOfSignature(sig) - : // Otherwise get the type of the property on the signature return type - getJsxPropsTypeForSignatureFromMember( - sig, - forcedLookupLocation - ); - - if (!attributesType) { - // There is no property named 'props' on this instance type - if (!!forcedLookupLocation - && !!length(context.attributes.properties)) - { - error( - context, - Diagnostics - .JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, - unescapeLeadingUnderscores(forcedLookupLocation) - ); - } - return unknownType; - } - - attributesType = getJsxManagedAttributesFromLocatedAttributes( - context, - ns, - attributesType - ); - - if (isTypeAny(attributesType)) { - // Props is of type 'any' or unknown - return attributesType; - } else { - // Normal case -- add in IntrinsicClassElements and IntrinsicElements - let apparentAttributesType = attributesType; - const intrinsicClassAttribs = getJsxType( - JsxNames.IntrinsicClassAttributes, - context - ); - if (intrinsicClassAttribs !== errorType) { - const typeParams = - getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs - .symbol); - const hostClassType = getReturnTypeOfSignature(sig); - apparentAttributesType = intersectTypes( - typeParams - ? createTypeReference( - intrinsicClassAttribs, - fillMissingTypeArguments( - [hostClassType], - typeParams, - getMinTypeArgumentCount(typeParams), - isInJSFile(context) - ) - ) - : intrinsicClassAttribs, - apparentAttributesType - ); - } - - const intrinsicAttribs = getJsxType( - JsxNames.IntrinsicAttributes, - context - ); - if (intrinsicAttribs !== errorType) { - apparentAttributesType = intersectTypes( - intrinsicAttribs, - apparentAttributesType - ); - } - - return apparentAttributesType; - } - } - - // If the given type is an object or union type with a single signature, and if that signature has at - // least as many parameters as the given function, return the signature. Otherwise return undefined. - function getContextualCallSignature( - type: Type, - node: SignatureDeclaration - ): Signature | undefined { - const signatures = getSignaturesOfType(type, SignatureKind.Call); - if (signatures.length === 1) { - const signature = signatures[0]; - if (!isAritySmaller(signature, node)) { - return signature; - } - } - } - - /** If the contextual signature has fewer parameters than the function expression, do not use it */ - function isAritySmaller( - signature: Signature, - target: SignatureDeclaration - ) { - let targetParameterCount = 0; - for (; targetParameterCount < target.parameters.length; - targetParameterCount++) - { - const param = target.parameters[targetParameterCount]; - if (param.initializer || param.questionToken - || param.dotDotDotToken || isJSDocOptionalParameter(param)) - { - break; - } - } - if (target.parameters.length - && parameterIsThisKeyword(target.parameters[0])) - { - targetParameterCount--; - } - return !hasEffectiveRestParameter(signature) - && getParameterCount(signature) < targetParameterCount; - } - - function isFunctionExpressionOrArrowFunction(node: - Node): node is FunctionExpression | ArrowFunction - { - return node.kind === SyntaxKind.FunctionExpression - || node.kind === SyntaxKind.ArrowFunction; - } - - function getContextualSignatureForFunctionLikeDeclaration(node: - FunctionLikeDeclaration): Signature | undefined - { - // Only function expressions, arrow functions, and object literal methods are contextually typed. - return isFunctionExpressionOrArrowFunction(node) - || isObjectLiteralMethod(node) - ? getContextualSignature( node) - : undefined; - } - - // Return the contextual signature for a given expression node. A contextual type provides a - // contextual signature if it has a single call signature and if that call signature is non-generic. - // If the contextual type is a union type, get the signature from each type possible and if they are - // all identical ignoring their return type, the result is same signature but with return type as - // union type of return types from these signatures - function getContextualSignature(node: FunctionExpression - | ArrowFunction | MethodDeclaration): Signature | undefined - { - Debug - .assert(node.kind !== SyntaxKind.MethodDeclaration - || isObjectLiteralMethod(node)); - const typeTagSignature = getSignatureOfTypeTag(node); - if (typeTagSignature) { - return typeTagSignature; - } - const type = getApparentTypeOfContextualType( - node, - ContextFlags.Signature - ); - if (!type) { - return undefined; - } - if (!(type.flags & TypeFlags.Union)) { - return getContextualCallSignature(type, node); - } - let signatureList: Signature[] | undefined; - const types = ( type).types; - for (const current of types) { - const signature = getContextualCallSignature(current, node); - if (signature) { - if (!signatureList) { - // This signature will contribute to contextual union signature - signatureList = [signature]; - } else if (!compareSignaturesIdentical( - signatureList[0], - signature, /*partialMatch*/ - false, /*ignoreThisTypes*/ - true, /*ignoreReturnTypes*/ - true, - compareTypesIdentical - )) { - // Signatures aren't identical, do not use - return undefined; - } else { - // Use this signature for contextual union signature - signatureList.push(signature); - } - } - } - // Result is union of signatures collected (return type is union of return types of this signature set) - if (signatureList) { - return signatureList.length === 1 - ? signatureList[0] - : createUnionSignature(signatureList[0], signatureList); - } - } - - function checkSpreadExpression( - node: SpreadElement, - checkMode?: CheckMode - ): Type { - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers( - node, - compilerOptions.downlevelIteration - ? ExternalEmitHelpers.SpreadIncludes - : ExternalEmitHelpers.SpreadArrays - ); - } - - const arrayOrIterableType = checkExpression( - node.expression, - checkMode - ); - return checkIteratedTypeOrElementType( - IterationUse.Spread, - arrayOrIterableType, - undefinedType, - node.expression - ); - } - - function hasDefaultValue(node: BindingElement | Expression): boolean { - return (node.kind === SyntaxKind.BindingElement - && !!( node).initializer) - || (node.kind === SyntaxKind.BinaryExpression - && ( node).operatorToken.kind - === SyntaxKind.EqualsToken); - } - - function checkArrayLiteral( - node: ArrayLiteralExpression, - checkMode: CheckMode | undefined, - forceTuple: boolean | undefined - ): Type { - const elements = node.elements; - const elementCount = elements.length; - let hasNonEndingSpreadElement = false; - const elementTypes: Type[] = []; - const inDestructuringPattern = isAssignmentTarget(node); - const contextualType = getApparentTypeOfContextualType(node); - const inConstContext = isConstContext(node); - for (let index = 0; index < elementCount; index++) { - const e = elements[index]; - if (inDestructuringPattern - && e.kind === SyntaxKind.SpreadElement) - { - // Given the following situation: - // var c: {}; - // [...c] = ["", 0]; - // - // c is represented in the tree as a spread element in an array literal. - // But c really functions as a rest element, and its purpose is to provide - // a contextual type for the right hand side of the assignment. Therefore, - // instead of calling checkExpression on "...c", which will give an error - // if c is not iterable/array-like, we need to act as if we are trying to - // get the contextual element type from it. So we do something similar to - // getContextualTypeForElementExpression, which will crucially not error - // if there is no index type / iterated type. - const restArrayType = checkExpression( - ( e).expression, - checkMode, - forceTuple - ); - const restElementType = - getIndexTypeOfType(restArrayType, IndexKind.Number) - || getIteratedTypeOrElementType( - IterationUse.Destructuring, - restArrayType, - undefinedType, /*errorNode*/ - undefined, /*checkAssignability*/ - false - ); - if (restElementType) { - elementTypes.push(restElementType); - } - } else { - const elementContextualType = - getContextualTypeForElementExpression( - contextualType, - index - ); - const type = checkExpressionForMutableLocation( - e, - checkMode, - elementContextualType, - forceTuple - ); - elementTypes.push(type); - } - if (index < elementCount - 1 - && e.kind === SyntaxKind.SpreadElement) - { - hasNonEndingSpreadElement = true; - } - } - if (!hasNonEndingSpreadElement) { - const hasRestElement = elementCount > 0 - && elements[elementCount - 1].kind - === SyntaxKind.SpreadElement; - const minLength = elementCount - (hasRestElement ? 1 : 0); - // If array literal is actually a destructuring pattern, mark it as an implied type. We do this such - // that we get the same behavior for "var [x, y] = []" and "[x, y] = []". - let tupleResult; - if (inDestructuringPattern && minLength > 0) { - const type = - cloneTypeReference( createTupleType( - elementTypes, - minLength, - hasRestElement - )); - type.pattern = node; - return type; - } else if (tupleResult = getArrayLiteralTupleTypeIfApplicable( - elementTypes, - contextualType, - hasRestElement, - elementCount, - inConstContext - )) { - return createArrayLiteralType(tupleResult); - } else if (forceTuple) { - return createArrayLiteralType(createTupleType( - elementTypes, - minLength, - hasRestElement - )); - } - } - return createArrayLiteralType(createArrayType( - elementTypes.length - ? getUnionType(elementTypes, UnionReduction.Subtype) - : strictNullChecks - ? implicitNeverType - : undefinedWideningType, - inConstContext - )); - } - - function createArrayLiteralType(type: ObjectType) { - if (!(getObjectFlags(type) & ObjectFlags.Reference)) { - return type; - } - let literalType = ( type).literalType; - if (!literalType) { - literalType = ( type) - .literalType = cloneTypeReference( type); - literalType - .objectFlags |= ObjectFlags.ArrayLiteral - | ObjectFlags.ContainsObjectOrArrayLiteral; - } - return literalType; - } - - function getArrayLiteralTupleTypeIfApplicable( - elementTypes: Type[], - contextualType: Type | undefined, - hasRestElement: boolean, - elementCount = elementTypes.length, - readonly = false - ) { - // Infer a tuple type when the contextual type is or contains a tuple-like type - if (readonly - || (contextualType - && forEachType(contextualType, isTupleLikeType))) - { - return createTupleType( - elementTypes, - elementCount - (hasRestElement ? 1 : 0), - hasRestElement, - readonly - ); - } - } - - function isNumericName(name: DeclarationName): boolean { - switch (name.kind) { - case SyntaxKind.ComputedPropertyName: - return isNumericComputedName(name); - case SyntaxKind.Identifier: - return isNumericLiteralName(name.escapedText); - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - return isNumericLiteralName(name.text); - default: - return false; - } - } - - function isNumericComputedName(name: ComputedPropertyName): boolean { - // It seems odd to consider an expression of type Any to result in a numeric name, - // but this behavior is consistent with checkIndexedAccess - return isTypeAssignableToKind( - checkComputedPropertyName(name), - TypeFlags.NumberLike - ); - } - - function isInfinityOrNaNString(name: string | __String): boolean { - return name === 'Infinity' || name === '-Infinity' - || name === 'NaN'; - } - - function isNumericLiteralName(name: string | __String) { - // The intent of numeric names is that - // - they are names with text in a numeric form, and that - // - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit', - // acquired by applying the abstract 'ToNumber' operation on the name's text. - // - // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name. - // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold. - // - // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)' - // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'. - // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names - // because their 'ToString' representation is not equal to their original text. - // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1. - // - // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'. - // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation. - // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number. - // - // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional. - // This is desired behavior, because when indexing with them as numeric entities, you are indexing - // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively. - return (+name).toString() === name; - } - - function checkComputedPropertyName(node: ComputedPropertyName): Type { - const links = getNodeLinks(node.expression); - if (!links.resolvedType) { - links.resolvedType = checkExpression(node.expression); - // This will allow types number, string, symbol or any. It will also allow enums, the unknown - // type, and any union of these types (like string | number). - if (links.resolvedType.flags & TypeFlags.Nullable - || !isTypeAssignableToKind( - links.resolvedType, - TypeFlags.StringLike | TypeFlags.NumberLike - | TypeFlags.ESSymbolLike - ) - && !isTypeAssignableTo( - links.resolvedType, - stringNumberSymbolType - )) - { - error( - node, - Diagnostics - .A_computed_property_name_must_be_of_type_string_number_symbol_or_any - ); - } else { - checkThatExpressionIsProperSymbolReference( - node.expression, - links.resolvedType, /*reportError*/ - true - ); - } - } - - return links.resolvedType; - } - - function getObjectLiteralIndexInfo( - node: ObjectLiteralExpression, - offset: number, - properties: Symbol[], - kind: IndexKind - ): IndexInfo { - const propTypes: Type[] = []; - for (let i = 0; i < properties.length; i++) { - if (kind === IndexKind.String - || isNumericName(node.properties[i + offset].name!)) - { - propTypes.push(getTypeOfSymbol(properties[i])); - } - } - const unionType = propTypes.length - ? getUnionType(propTypes, UnionReduction.Subtype) - : undefinedType; - return createIndexInfo(unionType, isConstContext(node)); - } - - function getImmediateAliasedSymbol(symbol: Symbol): Symbol - | undefined - { - Debug.assert( - (symbol.flags & SymbolFlags.Alias) !== 0, - 'Should only get Alias here.' - ); - const links = getSymbolLinks(symbol); - if (!links.immediateTarget) { - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - links.immediateTarget = getTargetOfAliasDeclaration( - node, /*dontRecursivelyResolve*/ - true - ); - } - - return links.immediateTarget; - } - - function checkObjectLiteral( - node: ObjectLiteralExpression, - checkMode?: CheckMode - ): Type { - const inDestructuringPattern = isAssignmentTarget(node); - // Grammar checking - checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - - let propertiesTable: SymbolTable; - let propertiesArray: Symbol[] = []; - let spread: Type = emptyObjectType; - - const contextualType = getApparentTypeOfContextualType(node); - const contextualTypeHasPattern = contextualType - && contextualType.pattern - && (contextualType.pattern.kind - === SyntaxKind.ObjectBindingPattern - || contextualType.pattern.kind - === SyntaxKind.ObjectLiteralExpression); - const inConstContext = isConstContext(node); - const checkFlags = inConstContext ? CheckFlags.Readonly : 0; - const isInJavascript = isInJSFile(node) && !isInJsonFile(node); - const enumTag = getJSDocEnumTag(node); - const isJSObjectLiteral = !contextualType && isInJavascript - && !enumTag; - let objectFlags: ObjectFlags = freshObjectLiteralFlag; - let patternWithComputedProperties = false; - let hasComputedStringProperty = false; - let hasComputedNumberProperty = false; - propertiesTable = createSymbolTable(); - - let offset = 0; - for (let i = 0; i < node.properties.length; i++) { - const memberDecl = node.properties[i]; - let member = getSymbolOfNode(memberDecl); - const computedNameType = - memberDecl.name - && memberDecl.name.kind - === SyntaxKind.ComputedPropertyName - && !isWellKnownSymbolSyntactically(memberDecl.name - .expression) - ? checkComputedPropertyName(memberDecl.name) - : undefined; - if (memberDecl.kind === SyntaxKind.PropertyAssignment - || memberDecl.kind - === SyntaxKind.ShorthandPropertyAssignment - || isObjectLiteralMethod(memberDecl)) - { - let type = - memberDecl.kind === SyntaxKind.PropertyAssignment - ? checkPropertyAssignment(memberDecl, checkMode) - : memberDecl.kind - === SyntaxKind.ShorthandPropertyAssignment - ? checkExpressionForMutableLocation( - memberDecl.name, - checkMode - ) - : checkObjectLiteralMethod( - memberDecl, - checkMode - ); - if (isInJavascript) { - const jsDocType = - getTypeForDeclarationFromJSDocComment(memberDecl); - if (jsDocType) { - checkTypeAssignableTo(type, jsDocType, memberDecl); - type = jsDocType; - } else if (enumTag && enumTag.typeExpression) { - checkTypeAssignableTo( - type, - getTypeFromTypeNode(enumTag.typeExpression), - memberDecl - ); - } - } - objectFlags |= getObjectFlags(type) - & ObjectFlags.PropagatingFlags; - const nameType = - computedNameType - && isTypeUsableAsPropertyName(computedNameType) - ? computedNameType - : undefined; - const prop = nameType - ? createSymbol( - SymbolFlags.Property | member.flags, - getPropertyNameFromType(nameType), - checkFlags | CheckFlags.Late - ) - : createSymbol( - SymbolFlags.Property | member.flags, - member.escapedName, - checkFlags - ); - if (nameType) { - prop.nameType = nameType; - } - - if (inDestructuringPattern) { - // If object literal is an assignment pattern and if the assignment pattern specifies a default value - // for the property, make the property optional. - const isOptional = - (memberDecl.kind === SyntaxKind.PropertyAssignment - && hasDefaultValue(memberDecl.initializer)) - || (memberDecl.kind - === SyntaxKind.ShorthandPropertyAssignment - && memberDecl.objectAssignmentInitializer); - if (isOptional) { - prop.flags |= SymbolFlags.Optional; - } - } else if (contextualTypeHasPattern - && !(getObjectFlags(contextualType!) - & ObjectFlags - .ObjectLiteralPatternWithComputedProperties)) - { - // If object literal is contextually typed by the implied type of a binding pattern, and if the - // binding pattern specifies a default value for the property, make the property optional. - const impliedProp = getPropertyOfType( - contextualType!, - member.escapedName - ); - if (impliedProp) { - prop - .flags |= impliedProp.flags - & SymbolFlags.Optional; - } else if (!compilerOptions - .suppressExcessPropertyErrors - && !getIndexInfoOfType( - contextualType!, - IndexKind.String - )) - { - error( - memberDecl.name, - Diagnostics - .Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - symbolToString(member), - typeToString(contextualType!) - ); - } - } - - prop.declarations = member.declarations; - prop.parent = member.parent; - if (member.valueDeclaration) { - prop.valueDeclaration = member.valueDeclaration; - } - - prop.type = type; - prop.target = member; - member = prop; - } else if (memberDecl.kind === SyntaxKind.SpreadAssignment) { - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers( - memberDecl, - ExternalEmitHelpers.Assign - ); - } - if (propertiesArray.length > 0) { - spread = getSpreadType( - spread, - createObjectLiteralType(), - node.symbol, - objectFlags, - inConstContext - ); - propertiesArray = []; - propertiesTable = createSymbolTable(); - hasComputedStringProperty = false; - hasComputedNumberProperty = false; - } - const type = checkExpression(memberDecl.expression); - if (!isValidSpreadType(type)) { - error( - memberDecl, - Diagnostics - .Spread_types_may_only_be_created_from_object_types - ); - return errorType; - } - spread = getSpreadType( - spread, - type, - node.symbol, - objectFlags, - inConstContext - ); - offset = i + 1; - continue; - } else { - // TypeScript 1.0 spec (April 2014) - // A get accessor declaration is processed in the same manner as - // an ordinary function declaration(section 6.1) with no parameters. - // A set accessor declaration is processed in the same manner - // as an ordinary function declaration with a single parameter and a Void return type. - Debug - .assert(memberDecl.kind === SyntaxKind.GetAccessor - || memberDecl.kind === SyntaxKind.SetAccessor); - checkNodeDeferred(memberDecl); - } - - if (computedNameType - && !(computedNameType.flags - & TypeFlags.StringOrNumberLiteralOrUnique)) - { - if (isTypeAssignableTo( - computedNameType, - stringNumberSymbolType - )) { - if (isTypeAssignableTo(computedNameType, numberType)) { - hasComputedNumberProperty = true; - } else { - hasComputedStringProperty = true; - } - if (inDestructuringPattern) { - patternWithComputedProperties = true; - } - } - } else { - propertiesTable.set(member.escapedName, member); - } - propertiesArray.push(member); - } - - // If object literal is contextually typed by the implied type of a binding pattern, augment the result - // type with those properties for which the binding pattern specifies a default value. - if (contextualTypeHasPattern) { - for (const prop of getPropertiesOfType(contextualType!)) { - if (!propertiesTable.get(prop.escapedName) - && !(spread - && getPropertyOfType(spread, prop.escapedName))) - { - if (!(prop.flags & SymbolFlags.Optional)) { - error( - prop.valueDeclaration - || ( prop).bindingElement, - Diagnostics - .Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value - ); - } - propertiesTable.set(prop.escapedName, prop); - propertiesArray.push(prop); - } - } - } - - if (spread !== emptyObjectType) { - if (propertiesArray.length > 0) { - spread = getSpreadType( - spread, - createObjectLiteralType(), - node.symbol, - objectFlags, - inConstContext - ); - propertiesArray = []; - propertiesTable = createSymbolTable(); - hasComputedStringProperty = false; - hasComputedNumberProperty = false; - } - // remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site - return mapType( - spread, - t => t === emptyObjectType ? createObjectLiteralType() : t - ); - } - - return createObjectLiteralType(); - - function createObjectLiteralType() { - const stringIndexInfo = hasComputedStringProperty - ? getObjectLiteralIndexInfo( - node, - offset, - propertiesArray, - IndexKind.String - ) - : undefined; - const numberIndexInfo = hasComputedNumberProperty - ? getObjectLiteralIndexInfo( - node, - offset, - propertiesArray, - IndexKind.Number - ) - : undefined; - const result = createAnonymousType( - node.symbol, - propertiesTable, - emptyArray, - emptyArray, - stringIndexInfo, - numberIndexInfo - ); - result - .objectFlags |= objectFlags | ObjectFlags.ObjectLiteral - | ObjectFlags.ContainsObjectOrArrayLiteral; - if (isJSObjectLiteral) { - result.objectFlags |= ObjectFlags.JSLiteral; - } - if (patternWithComputedProperties) { - result.objectFlags |= ObjectFlags - .ObjectLiteralPatternWithComputedProperties; - } - if (inDestructuringPattern) { - result.pattern = node; - } - return result; - } - } - - function isValidSpreadType(type: Type): boolean { - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type); - if (constraint !== undefined) { - return isValidSpreadType(constraint); - } - } - return !!(type.flags - & (TypeFlags.Any | TypeFlags.NonPrimitive | TypeFlags.Object - | TypeFlags.InstantiableNonPrimitive) - || getFalsyFlags(type) & TypeFlags.DefinitelyFalsy - && isValidSpreadType(removeDefinitelyFalsyTypes(type)) - || type.flags & TypeFlags.UnionOrIntersection - && every( - ( type).types, - isValidSpreadType - )); - } - - function checkJsxSelfClosingElementDeferred(node: - JsxSelfClosingElement) - { - checkJsxOpeningLikeElementOrOpeningFragment(node); - } - - function checkJsxSelfClosingElement( - node: JsxSelfClosingElement, - _checkMode: CheckMode | undefined - ): Type { - checkNodeDeferred(node); - return getJsxElementTypeAt(node) || anyType; - } - - function checkJsxElementDeferred(node: JsxElement) { - // Check attributes - checkJsxOpeningLikeElementOrOpeningFragment(node.openingElement); - - // Perform resolution on the closing tag so that rename/go to definition/etc work - if (isJsxIntrinsicIdentifier(node.closingElement.tagName)) { - getIntrinsicTagSymbol(node.closingElement); - } else { - checkExpression(node.closingElement.tagName); - } - - checkJsxChildren(node); - } - - function checkJsxElement( - node: JsxElement, - _checkMode: CheckMode | undefined - ): Type { - checkNodeDeferred(node); - - return getJsxElementTypeAt(node) || anyType; - } - - function checkJsxFragment(node: JsxFragment): Type { - checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment); - - if (compilerOptions.jsx === JsxEmit.React - && (compilerOptions.jsxFactory - || getSourceFileOfNode(node).pragmas.has('jsx'))) - { - error(node, compilerOptions.jsxFactory - ? Diagnostics - .JSX_fragment_is_not_supported_when_using_jsxFactory - : Diagnostics - .JSX_fragment_is_not_supported_when_using_an_inline_JSX_factory_pragma); - } - - checkJsxChildren(node); - return getJsxElementTypeAt(node) || anyType; - } - - /** - * Returns true iff the JSX element name would be a valid JS identifier, ignoring restrictions about keywords not being identifiers - */ - function isUnhyphenatedJsxName(name: string | __String) { - // - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers - return !stringContains(name as string, '-'); - } - - /** - * Returns true iff React would emit this tag name as a string rather than an identifier or qualified name - */ - function isJsxIntrinsicIdentifier(tagName: - JsxTagNameExpression): boolean - { - return tagName.kind === SyntaxKind.Identifier - && isIntrinsicJsxName(tagName.escapedText); - } - - function checkJsxAttribute(node: JsxAttribute, checkMode?: CheckMode) { - return node.initializer - ? checkExpressionForMutableLocation( - node.initializer, - checkMode - ) - : trueType; // is sugar for - } - - /** - * Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element. - * - * @param openingLikeElement a JSX opening-like element - * @param filter a function to remove attributes that will not participate in checking whether attributes are assignable - * @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property. - * @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, - * which also calls getSpreadType. - */ - function createJsxAttributesTypeFromAttributesProperty( - openingLikeElement: JsxOpeningLikeElement, - checkMode: CheckMode | undefined - ) { - const attributes = openingLikeElement.attributes; - let attributesTable = createSymbolTable(); - let spread: Type = emptyJsxObjectType; - let hasSpreadAnyType = false; - let typeToIntersect: Type | undefined; - let explicitlySpecifyChildrenAttribute = false; - let objectFlags: ObjectFlags = ObjectFlags.JsxAttributes; - const jsxChildrenPropertyName = - getJsxElementChildrenPropertyName(getJsxNamespaceAt(openingLikeElement)); - - for (const attributeDecl of attributes.properties) { - const member = attributeDecl.symbol; - if (isJsxAttribute(attributeDecl)) { - const exprType = checkJsxAttribute( - attributeDecl, - checkMode - ); - objectFlags |= getObjectFlags(exprType) - & ObjectFlags.PropagatingFlags; - - const attributeSymbol = createSymbol( - SymbolFlags.Property | SymbolFlags.Transient - | member.flags, - member.escapedName - ); - attributeSymbol.declarations = member.declarations; - attributeSymbol.parent = member.parent; - if (member.valueDeclaration) { - attributeSymbol.valueDeclaration = member - .valueDeclaration; - } - attributeSymbol.type = exprType; - attributeSymbol.target = member; - attributesTable.set( - attributeSymbol.escapedName, - attributeSymbol - ); - if (attributeDecl.name.escapedText - === jsxChildrenPropertyName) - { - explicitlySpecifyChildrenAttribute = true; - } - } else { - Debug - .assert(attributeDecl.kind - === SyntaxKind.JsxSpreadAttribute); - if (attributesTable.size > 0) { - spread = getSpreadType( - spread, - createJsxAttributesType(), - attributes.symbol, - objectFlags, /*readonly*/ - false - ); - attributesTable = createSymbolTable(); - } - const exprType = checkExpressionCached( - attributeDecl.expression, - checkMode - ); - if (isTypeAny(exprType)) { - hasSpreadAnyType = true; - } - if (isValidSpreadType(exprType)) { - spread = getSpreadType( - spread, - exprType, - attributes.symbol, - objectFlags, /*readonly*/ - false - ); - } else { - typeToIntersect = typeToIntersect - ? getIntersectionType([typeToIntersect, exprType]) - : exprType; - } - } - } - - if (!hasSpreadAnyType) { - if (attributesTable.size > 0) { - spread = getSpreadType( - spread, - createJsxAttributesType(), - attributes.symbol, - objectFlags, /*readonly*/ - false - ); - } - } - - // Handle children attribute - const parent = - openingLikeElement.parent.kind === SyntaxKind.JsxElement - ? openingLikeElement.parent as JsxElement - : undefined; - // We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement - if (parent && parent.openingElement === openingLikeElement - && parent.children.length > 0) - { - const childrenTypes: Type[] = checkJsxChildren( - parent, - checkMode - ); - - if (!hasSpreadAnyType && jsxChildrenPropertyName - && jsxChildrenPropertyName !== '') - { - // Error if there is a attribute named "children" explicitly specified and children element. - // This is because children element will overwrite the value from attributes. - // Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread. - if (explicitlySpecifyChildrenAttribute) { - error( - attributes, - Diagnostics - ._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, - unescapeLeadingUnderscores(jsxChildrenPropertyName) - ); - } - - const contextualType = - getApparentTypeOfContextualType(openingLikeElement - .attributes); - const childrenContextualType = contextualType - && getTypeOfPropertyOfContextualType( - contextualType, - jsxChildrenPropertyName - ); - // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process - const childrenPropSymbol = createSymbol( - SymbolFlags.Property | SymbolFlags.Transient, - jsxChildrenPropertyName - ); - childrenPropSymbol.type = childrenTypes.length === 1 - ? childrenTypes[0] - : (getArrayLiteralTupleTypeIfApplicable( - childrenTypes, - childrenContextualType, /*hasRestElement*/ - false - ) || createArrayType(getUnionType(childrenTypes))); - // Fake up a property declaration for the children - childrenPropSymbol - .valueDeclaration = createPropertySignature( - /*modifiers*/ undefined, - unescapeLeadingUnderscores(jsxChildrenPropertyName), /*questionToken*/ - undefined, /*type*/ - undefined, /*initializer*/ - undefined - ); - childrenPropSymbol.valueDeclaration.parent = attributes; - childrenPropSymbol.valueDeclaration - .symbol = childrenPropSymbol; - const childPropMap = createSymbolTable(); - childPropMap.set( - jsxChildrenPropertyName, - childrenPropSymbol - ); - spread = getSpreadType( - spread, - createAnonymousType( - attributes.symbol, - childPropMap, - emptyArray, - emptyArray, /*stringIndexInfo*/ - undefined, /*numberIndexInfo*/ - undefined - ), - attributes.symbol, - objectFlags, /*readonly*/ - false - ); - } - } - - if (hasSpreadAnyType) { - return anyType; - } - if (typeToIntersect && spread !== emptyJsxObjectType) { - return getIntersectionType([typeToIntersect, spread]); - } - return typeToIntersect - || (spread === emptyJsxObjectType - ? createJsxAttributesType() - : spread); - - /** - * Create anonymous type from given attributes symbol table. - * @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable - * @param attributesTable a symbol table of attributes property - */ - function createJsxAttributesType() { - objectFlags |= freshObjectLiteralFlag; - const result = createAnonymousType( - attributes.symbol, - attributesTable, - emptyArray, - emptyArray, /*stringIndexInfo*/ - undefined, /*numberIndexInfo*/ - undefined - ); - result - .objectFlags |= objectFlags | ObjectFlags.ObjectLiteral - | ObjectFlags.ContainsObjectOrArrayLiteral; - return result; - } - } - - function checkJsxChildren( - node: JsxElement | JsxFragment, - checkMode?: CheckMode - ) { - const childrenTypes: Type[] = []; - for (const child of node.children) { - // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that - // because then type of children property will have constituent of string type. - if (child.kind === SyntaxKind.JsxText) { - if (!child.containsOnlyTriviaWhiteSpaces) { - childrenTypes.push(stringType); - } - } else { - childrenTypes - .push(checkExpressionForMutableLocation( - child, - checkMode - )); - } - } - return childrenTypes; - } - - /** - * Check attributes property of opening-like element. This function is called during chooseOverload to get call signature of a JSX opening-like element. - * (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used) - * @param node a JSXAttributes to be resolved of its type - */ - function checkJsxAttributes( - node: JsxAttributes, - checkMode: CheckMode | undefined - ) { - return createJsxAttributesTypeFromAttributesProperty( - node.parent, - checkMode - ); - } - - function getJsxType(name: __String, location: Node | undefined) { - const namespace = getJsxNamespaceAt(location); - const exports = namespace && getExportsOfSymbol(namespace); - const typeSymbol = exports - && getSymbol(exports, name, SymbolFlags.Type); - return typeSymbol - ? getDeclaredTypeOfSymbol(typeSymbol) - : errorType; - } - - /** - * Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic - * property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic - * string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). - * May also return unknownSymbol if both of these lookups fail. - */ - function getIntrinsicTagSymbol(node: JsxOpeningLikeElement - | JsxClosingElement): Symbol - { - const links = getNodeLinks(node); - if (!links.resolvedSymbol) { - const intrinsicElementsType = getJsxType( - JsxNames.IntrinsicElements, - node - ); - if (intrinsicElementsType !== errorType) { - // Property case - if (!isIdentifier(node.tagName)) return Debug.fail(); - const intrinsicProp = getPropertyOfType( - intrinsicElementsType, - node.tagName.escapedText - ); - if (intrinsicProp) { - links.jsxFlags |= JsxFlags.IntrinsicNamedElement; - return links.resolvedSymbol = intrinsicProp; - } - - // Intrinsic string indexer case - const indexSignatureType = getIndexTypeOfType( - intrinsicElementsType, - IndexKind.String - ); - if (indexSignatureType) { - links.jsxFlags |= JsxFlags.IntrinsicIndexedElement; - return links.resolvedSymbol = intrinsicElementsType - .symbol; - } - - // Wasn't found - error( - node, - Diagnostics.Property_0_does_not_exist_on_type_1, - idText(node.tagName), - 'JSX.' + JsxNames.IntrinsicElements - ); - return links.resolvedSymbol = unknownSymbol; - } else { - if (noImplicitAny) { - error( - node, - Diagnostics - .JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, - unescapeLeadingUnderscores(JsxNames - .IntrinsicElements) - ); - } - return links.resolvedSymbol = unknownSymbol; - } - } - return links.resolvedSymbol; - } - - function getJsxNamespaceAt(location: Node | undefined): Symbol { - const links = location && getNodeLinks(location); - if (links && links.jsxNamespace) { - return links.jsxNamespace; - } - if (!links || links.jsxNamespace !== false) { - const namespaceName = getJsxNamespace(location); - const resolvedNamespace = resolveName( - location, - namespaceName, - SymbolFlags.Namespace, /*diagnosticMessage*/ - undefined, - namespaceName, /*isUse*/ - false - ); - if (resolvedNamespace) { - const candidate = - resolveSymbol(getSymbol( - getExportsOfSymbol(resolveSymbol(resolvedNamespace)), - JsxNames.JSX, - SymbolFlags.Namespace - )); - if (candidate) { - if (links) { - links.jsxNamespace = candidate; - } - return candidate; - } - if (links) { - links.jsxNamespace = false; - } - } - } - // JSX global fallback - return getGlobalSymbol( - JsxNames.JSX, - SymbolFlags.Namespace, /*diagnosticMessage*/ - undefined - )!; // TODO: GH#18217 - } - - /** - * Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer. - * Get a single property from that container if existed. Report an error if there are more than one property. - * - * @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer - * if other string is given or the container doesn't exist, return undefined. - */ - function getNameFromJsxElementAttributesContainer( - nameOfAttribPropContainer: __String, - jsxNamespace: Symbol - ): __String | undefined { - // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol] - const jsxElementAttribPropInterfaceSym = jsxNamespace - && getSymbol( - jsxNamespace.exports!, - nameOfAttribPropContainer, - SymbolFlags.Type - ); - // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [type] - const jsxElementAttribPropInterfaceType = - jsxElementAttribPropInterfaceSym - && getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym); - // The properties of JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute - const propertiesOfJsxElementAttribPropInterface = - jsxElementAttribPropInterfaceType - && getPropertiesOfType(jsxElementAttribPropInterfaceType); - if (propertiesOfJsxElementAttribPropInterface) { - // Element Attributes has zero properties, so the element attributes type will be the class instance type - if (propertiesOfJsxElementAttribPropInterface.length === 0) { - return '' as __String; - } // Element Attributes has one property, so the element attributes type will be the type of the corresponding - // property of the class instance type - else if (propertiesOfJsxElementAttribPropInterface.length - === 1) - { - return propertiesOfJsxElementAttribPropInterface[0] - .escapedName; - } else if (propertiesOfJsxElementAttribPropInterface.length - > 1) - { - // More than one property on ElementAttributesProperty is an error - error( - jsxElementAttribPropInterfaceSym!.declarations[0], - Diagnostics - .The_global_type_JSX_0_may_not_have_more_than_one_property, - unescapeLeadingUnderscores(nameOfAttribPropContainer) - ); - } - } - return undefined; - } - - function getJsxLibraryManagedAttributes(jsxNamespace: Symbol) { - // JSX.LibraryManagedAttributes [symbol] - return jsxNamespace - && getSymbol( - jsxNamespace.exports!, - JsxNames.LibraryManagedAttributes, - SymbolFlags.Type - ); - } - - /// e.g. "props" for React.d.ts, - /// or 'undefined' if ElementAttributesProperty doesn't exist (which means all - /// non-intrinsic elements' attributes type is 'any'), - /// or '' if it has 0 properties (which means every - /// non-intrinsic elements' attributes type is the element instance type) - function getJsxElementPropertiesName(jsxNamespace: Symbol) { - return getNameFromJsxElementAttributesContainer( - JsxNames.ElementAttributesPropertyNameContainer, - jsxNamespace - ); - } - - function getJsxElementChildrenPropertyName(jsxNamespace: - Symbol): __String | undefined - { - return getNameFromJsxElementAttributesContainer( - JsxNames.ElementChildrenAttributeNameContainer, - jsxNamespace - ); - } - - function getUninstantiatedJsxSignaturesOfType( - elementType: Type, - caller: JsxOpeningLikeElement - ): readonly Signature[] { - if (elementType.flags & TypeFlags.String) { - return [anySignature]; - } else if (elementType.flags & TypeFlags.StringLiteral) { - const intrinsicType = - getIntrinsicAttributesTypeFromStringLiteralType( - elementType as StringLiteralType, - caller - ); - if (!intrinsicType) { - error( - caller, - Diagnostics.Property_0_does_not_exist_on_type_1, - (elementType as StringLiteralType).value, - 'JSX.' + JsxNames.IntrinsicElements - ); - return emptyArray; - } else { - const fakeSignature = createSignatureForJSXIntrinsic( - caller, - intrinsicType - ); - return [fakeSignature]; - } - } - const apparentElemType = getApparentType(elementType); - // Resolve the signatures, preferring constructor - let signatures = getSignaturesOfType( - apparentElemType, - SignatureKind.Construct - ); - if (signatures.length === 0) { - // No construct signatures, try call signatures - signatures = getSignaturesOfType( - apparentElemType, - SignatureKind.Call - ); - } - if (signatures.length === 0 - && apparentElemType.flags & TypeFlags.Union) - { - // If each member has some combination of new/call signatures; make a union signature list for those - signatures = getUnionSignatures(map( - (apparentElemType as UnionType).types, - t => getUninstantiatedJsxSignaturesOfType(t, caller) - )); - } - return signatures; - } - - function getIntrinsicAttributesTypeFromStringLiteralType( - type: StringLiteralType, - location: Node - ): Type | undefined { - // If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type - // For example: - // var CustomTag: "h1" = "h1"; - // Hello World - const intrinsicElementsType = getJsxType( - JsxNames.IntrinsicElements, - location - ); - if (intrinsicElementsType !== errorType) { - const stringLiteralTypeName = type.value; - const intrinsicProp = getPropertyOfType( - intrinsicElementsType, - escapeLeadingUnderscores(stringLiteralTypeName) - ); - if (intrinsicProp) { - return getTypeOfSymbol(intrinsicProp); - } - const indexSignatureType = getIndexTypeOfType( - intrinsicElementsType, - IndexKind.String - ); - if (indexSignatureType) { - return indexSignatureType; - } - return undefined; - } - // If we need to report an error, we already done so here. So just return any to prevent any more error downstream - return anyType; - } - - function checkJsxReturnAssignableToAppropriateBound( - refKind: JsxReferenceKind, - elemInstanceType: Type, - openingLikeElement: Node - ) { - if (refKind === JsxReferenceKind.Function) { - const sfcReturnConstraint = - getJsxStatelessElementTypeAt(openingLikeElement); - if (sfcReturnConstraint) { - checkTypeRelatedTo( - elemInstanceType, - sfcReturnConstraint, - assignableRelation, - openingLikeElement, - Diagnostics - .JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements - ); - } - } else if (refKind === JsxReferenceKind.Component) { - const classConstraint = - getJsxElementClassTypeAt(openingLikeElement); - if (classConstraint) { - // Issue an error if this return type isn't assignable to JSX.ElementClass or JSX.Element, failing that - checkTypeRelatedTo( - elemInstanceType, - classConstraint, - assignableRelation, - openingLikeElement, - Diagnostics - .JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements - ); - } - } else { // Mixed - const sfcReturnConstraint = - getJsxStatelessElementTypeAt(openingLikeElement); - const classConstraint = - getJsxElementClassTypeAt(openingLikeElement); - if (!sfcReturnConstraint || !classConstraint) { - return; - } - const combined = - getUnionType([sfcReturnConstraint, classConstraint]); - checkTypeRelatedTo( - elemInstanceType, - combined, - assignableRelation, - openingLikeElement, - Diagnostics - .JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements - ); - } - } - - /** - * Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name. - * The function is intended to be called from a function which has checked that the opening element is an intrinsic element. - * @param node an intrinsic JSX opening-like element - */ - function getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node: - JsxOpeningLikeElement): Type - { - Debug.assert(isJsxIntrinsicIdentifier(node.tagName)); - const links = getNodeLinks(node); - if (!links.resolvedJsxElementAttributesType) { - const symbol = getIntrinsicTagSymbol(node); - if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) { - return links - .resolvedJsxElementAttributesType = getTypeOfSymbol(symbol); - } else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) { - return links - .resolvedJsxElementAttributesType = getIndexTypeOfType( - getDeclaredTypeOfSymbol(symbol), - IndexKind.String - )!; - } else { - return links.resolvedJsxElementAttributesType = errorType; - } - } - return links.resolvedJsxElementAttributesType; - } - - function getJsxElementClassTypeAt(location: Node): Type | undefined { - const type = getJsxType(JsxNames.ElementClass, location); - if (type === errorType) return undefined; - return type; - } - - function getJsxElementTypeAt(location: Node): Type { - return getJsxType(JsxNames.Element, location); - } - - function getJsxStatelessElementTypeAt(location: Node): Type - | undefined - { - const jsxElementType = getJsxElementTypeAt(location); - if (jsxElementType) { - return getUnionType([jsxElementType, nullType]); - } - } - - /** - * Returns all the properties of the Jsx.IntrinsicElements interface - */ - function getJsxIntrinsicTagNamesAt(location: Node): Symbol[] { - const intrinsics = getJsxType( - JsxNames.IntrinsicElements, - location - ); - return intrinsics ? getPropertiesOfType(intrinsics) : emptyArray; - } - - function checkJsxPreconditions(errorNode: Node) { - // Preconditions for using JSX - if ((compilerOptions.jsx || JsxEmit.None) === JsxEmit.None) { - error( - errorNode, - Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided - ); - } - - if (getJsxElementTypeAt(errorNode) === undefined) { - if (noImplicitAny) { - error( - errorNode, - Diagnostics - .JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist - ); - } - } - } - - function checkJsxOpeningLikeElementOrOpeningFragment(node: - JsxOpeningLikeElement | JsxOpeningFragment) - { - const isNodeOpeningLikeElement = isJsxOpeningLikeElement(node); - - if (isNodeOpeningLikeElement) { - checkGrammarJsxElement( node); - } - checkJsxPreconditions(node); - // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. - // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. - const reactRefErr = - diagnostics && compilerOptions.jsx === JsxEmit.React - ? Diagnostics.Cannot_find_name_0 - : undefined; - const reactNamespace = getJsxNamespace(node); - const reactLocation = isNodeOpeningLikeElement - ? ( node).tagName - : node; - const reactSym = resolveName( - reactLocation, - reactNamespace, - SymbolFlags.Value, - reactRefErr, - reactNamespace, /*isUse*/ - true - ); - if (reactSym) { - // Mark local symbol as referenced here because it might not have been marked - // if jsx emit was not react as there wont be error being emitted - reactSym.isReferenced = SymbolFlags.All; - - // If react symbol is alias, mark it as refereced - if (reactSym.flags & SymbolFlags.Alias) { - markAliasSymbolAsReferenced(reactSym); - } - } - - if (isNodeOpeningLikeElement) { - const sig = - getResolvedSignature(node as JsxOpeningLikeElement); - checkJsxReturnAssignableToAppropriateBound( - getJsxReferenceKind(node as JsxOpeningLikeElement), - getReturnTypeOfSignature(sig), - node - ); - } - } - - /** - * Check if a property with the given name is known anywhere in the given type. In an object type, a property - * is considered known if - * 1. the object type is empty and the check is for assignability, or - * 2. if the object type has index signatures, or - * 3. if the property is actually declared in the object type - * (this means that 'toString', for example, is not usually a known property). - * 4. In a union or intersection type, - * a property is considered known if it is known in any constituent type. - * @param targetType a type to search a given name in - * @param name a property name to search - * @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType - */ - function isKnownProperty( - targetType: Type, - name: __String, - isComparingJsxAttributes: boolean - ): boolean { - if (targetType.flags & TypeFlags.Object) { - const resolved = - resolveStructuredTypeMembers(targetType as ObjectType); - if (resolved.stringIndexInfo - || resolved.numberIndexInfo && isNumericLiteralName(name) - || getPropertyOfObjectType(targetType, name) - || isComparingJsxAttributes - && !isUnhyphenatedJsxName(name)) - { - // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known. - return true; - } - } else if (targetType.flags & TypeFlags.UnionOrIntersection - && isExcessPropertyCheckTarget(targetType)) - { - for (const t of (targetType as UnionOrIntersectionType) - .types) - { - if (isKnownProperty(t, name, isComparingJsxAttributes)) { - return true; - } - } - } - return false; - } - - function isExcessPropertyCheckTarget(type: Type): boolean { - return !!(type.flags & TypeFlags.Object - && !(getObjectFlags(type) - & ObjectFlags.ObjectLiteralPatternWithComputedProperties) - || type.flags & TypeFlags.NonPrimitive - || type.flags & TypeFlags.Union - && some(( type).types, isExcessPropertyCheckTarget) - || type.flags & TypeFlags.Intersection - && every( - ( type).types, - isExcessPropertyCheckTarget - )); - } - - function checkJsxExpression( - node: JsxExpression, - checkMode?: CheckMode - ) { - checkGrammarJsxExpression(node); - if (node.expression) { - const type = checkExpression(node.expression, checkMode); - if (node.dotDotDotToken && type !== anyType - && !isArrayType(type)) - { - error( - node, - Diagnostics.JSX_spread_child_must_be_an_array_type - ); - } - return type; - } else { - return errorType; - } - } - - function getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags { - return s.valueDeclaration - ? getCombinedNodeFlags(s.valueDeclaration) - : 0; - } - - /** - * Return whether this symbol is a member of a prototype somewhere - * Note that this is not tracked well within the compiler, so the answer may be incorrect. - */ - function isPrototypeProperty(symbol: Symbol) { - if (symbol.flags & SymbolFlags.Method - || getCheckFlags(symbol) & CheckFlags.SyntheticMethod) - { - return true; - } - if (isInJSFile(symbol.valueDeclaration)) { - const parent = symbol.valueDeclaration.parent; - return parent && isBinaryExpression(parent) - && getAssignmentDeclarationKind(parent) - === AssignmentDeclarationKind.PrototypeProperty; - } - } - - /** - * Check whether the requested property access is valid. - * Returns true if node is a valid property access, and false otherwise. - * @param node The node to be checked. - * @param isSuper True if the access is from `super.`. - * @param type The type of the object whose property is being accessed. (Not the type of the property.) - * @param prop The symbol for the property being accessed. - */ - function checkPropertyAccessibility( - node: PropertyAccessExpression | QualifiedName - | PropertyAccessExpression | VariableDeclaration - | ParameterDeclaration | ImportTypeNode | PropertyAssignment - | ShorthandPropertyAssignment | BindingElement, - isSuper: boolean, - type: Type, - prop: Symbol - ): boolean { - const flags = getDeclarationModifierFlagsFromSymbol(prop); - const errorNode = node.kind === SyntaxKind.QualifiedName - ? node.right - : node.kind === SyntaxKind.ImportType ? node : node.name; - - if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) { - // Synthetic property with private constituent property - error( - errorNode, - Diagnostics - .Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, - symbolToString(prop), - typeToString(type) - ); - return false; - } - - if (isSuper) { - // TS 1.0 spec (April 2014): 4.8.2 - // - In a constructor, instance member function, instance member accessor, or - // instance member variable initializer where this references a derived class instance, - // a super property access is permitted and must specify a public instance member function of the base class. - // - In a static member function or static member accessor - // where this references the constructor function object of a derived class, - // a super property access is permitted and must specify a public static member function of the base class. - if (languageVersion < ScriptTarget.ES2015) { - if (symbolHasNonMethodDeclaration(prop)) { - error( - errorNode, - Diagnostics - .Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword - ); - return false; - } - } - if (flags & ModifierFlags.Abstract) { - // A method cannot be accessed in a super property access if the method is abstract. - // This error could mask a private property access error. But, a member - // cannot simultaneously be private and abstract, so this will trigger an - // additional error elsewhere. - error( - errorNode, - Diagnostics - .Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, - symbolToString(prop), - typeToString(getDeclaringClass(prop)!) - ); - return false; - } - } - - // Referencing abstract properties within their own constructors is not allowed - if ((flags & ModifierFlags.Abstract) && isThisProperty(node) - && symbolHasNonMethodDeclaration(prop)) - { - const declaringClassDeclaration = - getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!); - if (declaringClassDeclaration - && isNodeUsedDuringClassInitialization(node)) - { - error( - errorNode, - Diagnostics - .Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, - symbolToString(prop), - getTextOfIdentifierOrLiteral(declaringClassDeclaration - .name!) - ); // TODO: GH#18217 - return false; - } - } - - if (isPropertyAccessExpression(node) - && isPrivateIdentifier(node.name)) - { - if (!getContainingClass(node)) { - error( - errorNode, - Diagnostics - .Private_identifiers_are_not_allowed_outside_class_bodies - ); - return false; - } - return true; - } - - // Public properties are otherwise accessible. - if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) { - return true; - } - - // Property is known to be private or protected at this point - - // Private property is accessible if the property is within the declaring class - if (flags & ModifierFlags.Private) { - const declaringClassDeclaration = - getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!)!; - if (!isNodeWithinClass(node, declaringClassDeclaration)) { - error( - errorNode, - Diagnostics - .Property_0_is_private_and_only_accessible_within_class_1, - symbolToString(prop), - typeToString(getDeclaringClass(prop)!) - ); - return false; - } - return true; - } - - // Property is known to be protected at this point - - // All protected properties of a supertype are accessible in a super access - if (isSuper) { - return true; - } - - // Find the first enclosing class that has the declaring classes of the protected constituents - // of the property as base classes - let enclosingClass = forEachEnclosingClass( - node, - enclosingDeclaration => { - const enclosingClass = - getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration)!); - return isClassDerivedFromDeclaringClasses( - enclosingClass, - prop - ) - ? enclosingClass - : undefined; - } - ); - // A protected property is accessible if the property is within the declaring class or classes derived from it - if (!enclosingClass) { - // allow PropertyAccessibility if context is in function with this parameter - // static member access is disallow - let thisParameter: ParameterDeclaration | undefined; - if (flags & ModifierFlags.Static - || !(thisParameter = getThisParameterFromNodeContext(node)) - || !thisParameter.type) - { - error( - errorNode, - Diagnostics - .Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, - symbolToString(prop), - typeToString(getDeclaringClass(prop) || type) - ); - return false; - } - - const thisType = getTypeFromTypeNode(thisParameter.type); - enclosingClass = ((thisType.flags & TypeFlags.TypeParameter) - ? getConstraintOfTypeParameter( thisType) - : thisType) as InterfaceType; - } - // No further restrictions for static properties - if (flags & ModifierFlags.Static) { - return true; - } - if (type.flags & TypeFlags.TypeParameter) { - // get the original type -- represented as the type constraint of the 'this' type - type = (type as TypeParameter).isThisType - ? getConstraintOfTypeParameter( type)! - : getBaseConstraintOfType( type)!; // TODO: GH#18217 Use a different variable that's allowed to be undefined - } - if (!type || !hasBaseType(type, enclosingClass)) { - error( - errorNode, - Diagnostics - .Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, - symbolToString(prop), - typeToString(enclosingClass) - ); - return false; - } - return true; - } - - function getThisParameterFromNodeContext(node: Node) { - const thisContainer = getThisContainer( - node, /* includeArrowFunctions */ - false - ); - return thisContainer && isFunctionLike(thisContainer) - ? getThisParameter(thisContainer) - : undefined; - } - - function symbolHasNonMethodDeclaration(symbol: Symbol) { - return !!forEachProperty( - symbol, - prop => !(prop.flags & SymbolFlags.Method) - ); - } - - function checkNonNullExpression(node: Expression | QualifiedName) { - return checkNonNullType(checkExpression(node), node); - } - - function isNullableType(type: Type) { - return !!((strictNullChecks ? getFalsyFlags(type) : type.flags) - & TypeFlags.Nullable); - } - - function getNonNullableTypeIfNeeded(type: Type) { - return isNullableType(type) ? getNonNullableType(type) : type; - } - - function reportObjectPossiblyNullOrUndefinedError( - node: Node, - flags: TypeFlags - ) { - error(node, flags & TypeFlags.Undefined - ? flags & TypeFlags.Null - ? Diagnostics.Object_is_possibly_null_or_undefined - : Diagnostics.Object_is_possibly_undefined - : Diagnostics.Object_is_possibly_null); - } - - function reportCannotInvokePossiblyNullOrUndefinedError( - node: Node, - flags: TypeFlags - ) { - error(node, flags & TypeFlags.Undefined - ? flags & TypeFlags.Null - ? Diagnostics - .Cannot_invoke_an_object_which_is_possibly_null_or_undefined - : Diagnostics - .Cannot_invoke_an_object_which_is_possibly_undefined - : Diagnostics.Cannot_invoke_an_object_which_is_possibly_null); - } - - function checkNonNullTypeWithReporter( - type: Type, - node: Node, - reportError: (node: Node, kind: TypeFlags) => void - ): Type { - if (strictNullChecks && type.flags & TypeFlags.Unknown) { - error(node, Diagnostics.Object_is_of_type_unknown); - return errorType; - } - const kind = - (strictNullChecks ? getFalsyFlags(type) : type.flags) - & TypeFlags.Nullable; - if (kind) { - reportError(node, kind); - const t = getNonNullableType(type); - return t.flags & (TypeFlags.Nullable | TypeFlags.Never) - ? errorType - : t; - } - return type; - } - - function checkNonNullType(type: Type, node: Node) { - return checkNonNullTypeWithReporter( - type, - node, - reportObjectPossiblyNullOrUndefinedError - ); - } - - function checkNonNullNonVoidType(type: Type, node: Node): Type { - const nonNullType = checkNonNullType(type, node); - if (nonNullType !== errorType - && nonNullType.flags & TypeFlags.Void) - { - error(node, Diagnostics.Object_is_possibly_undefined); - } - return nonNullType; - } - - function checkPropertyAccessExpression(node: - PropertyAccessExpression) - { - return node.flags & NodeFlags.OptionalChain - ? checkPropertyAccessChain(node as PropertyAccessChain) - : checkPropertyAccessExpressionOrQualifiedName( - node, - node.expression, - checkNonNullExpression(node.expression), - node.name - ); - } - - function checkPropertyAccessChain(node: PropertyAccessChain) { - const leftType = checkExpression(node.expression); - const nonOptionalType = getOptionalExpressionType( - leftType, - node.expression - ); - return propagateOptionalTypeMarker( - checkPropertyAccessExpressionOrQualifiedName( - node, - node.expression, - checkNonNullType(nonOptionalType, node.expression), - node.name - ), - node, - nonOptionalType !== leftType - ); - } - - function checkQualifiedName(node: QualifiedName) { - return checkPropertyAccessExpressionOrQualifiedName( - node, - node.left, - checkNonNullExpression(node.left), - node.right - ); - } - - function isMethodAccessForCall(node: Node) { - while (node.parent.kind === SyntaxKind.ParenthesizedExpression) { - node = node.parent; - } - return isCallOrNewExpression(node.parent) - && node.parent.expression === node; - } - - // Lookup the private identifier lexically. - function lookupSymbolForPrivateIdentifierDeclaration( - propName: __String, - location: Node - ): Symbol | undefined { - for (let containingClass = getContainingClass(location); - !!containingClass; - containingClass = getContainingClass(containingClass)) - { - const { symbol } = containingClass; - const name = getSymbolNameForPrivateIdentifier( - symbol, - propName - ); - const prop = (symbol.members && symbol.members.get(name)) - || (symbol.exports && symbol.exports.get(name)); - if (prop) { - return prop; - } - } - } - - function getPrivateIdentifierPropertyOfType( - leftType: Type, - lexicallyScopedIdentifier: Symbol - ): Symbol | undefined { - return getPropertyOfType( - leftType, - lexicallyScopedIdentifier.escapedName - ); - } - - function checkPrivateIdentifierPropertyAccess( - leftType: Type, - right: PrivateIdentifier, - lexicallyScopedIdentifier: Symbol | undefined - ): boolean { - // Either the identifier could not be looked up in the lexical scope OR the lexically scoped identifier did not exist on the type. - // Find a private identifier with the same description on the type. - let propertyOnType: Symbol | undefined; - const properties = getPropertiesOfType(leftType); - if (properties) { - forEach(properties, (symbol: Symbol) => { - const decl = symbol.valueDeclaration; - if (decl && isNamedDeclaration(decl) - && isPrivateIdentifier(decl.name) - && decl.name.escapedText === right.escapedText) - { - propertyOnType = symbol; - return true; - } - }); - } - const diagName = diagnosticName(right); - if (propertyOnType) { - const typeValueDecl = propertyOnType.valueDeclaration; - const typeClass = getContainingClass(typeValueDecl); - Debug.assert(!!typeClass); - // We found a private identifier property with the same description. - // Either: - // - There is a lexically scoped private identifier AND it shadows the one we found on the type. - // - It is an attempt to access the private identifier outside of the class. - if (lexicallyScopedIdentifier) { - const lexicalValueDecl = lexicallyScopedIdentifier - .valueDeclaration; - const lexicalClass = getContainingClass(lexicalValueDecl); - Debug.assert(!!lexicalClass); - if (findAncestor(lexicalClass, n => typeClass === n)) { - const diagnostic = error( - right, - Diagnostics - .The_property_0_cannot_be_accessed_on_type_1_within_this_class_because_it_is_shadowed_by_another_private_identifier_with_the_same_spelling, - diagName, - typeToString(leftType) - ); - - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - lexicalValueDecl, - Diagnostics - .The_shadowing_declaration_of_0_is_defined_here, - diagName - ), - createDiagnosticForNode( - typeValueDecl, - Diagnostics - .The_declaration_of_0_that_you_probably_intended_to_use_is_defined_here, - diagName - ) - ); - return true; - } - } - error( - right, - Diagnostics - .Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier, - diagName, - diagnosticName(typeClass!.name || anon) - ); - return true; - } - return false; - } - - function checkPropertyAccessExpressionOrQualifiedName( - node: PropertyAccessExpression | QualifiedName, - left: Expression | QualifiedName, - leftType: Type, - right: Identifier | PrivateIdentifier - ) { - const parentSymbol = getNodeLinks(left).resolvedSymbol; - const assignmentKind = getAssignmentTargetKind(node); - const apparentType = - getApparentType(assignmentKind !== AssignmentKind.None - || isMethodAccessForCall(node) - ? getWidenedType(leftType) - : leftType); - if (isPrivateIdentifier(right)) { - if (isOptionalChain(node)) { - grammarErrorOnNode( - right, - Diagnostics - .An_optional_chain_cannot_contain_private_identifiers - ); - return anyType; - } - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.ClassPrivateFieldGet - ); - } - const isAnyLike = isTypeAny(apparentType) - || apparentType === silentNeverType; - let prop: Symbol | undefined; - if (isPrivateIdentifier(right)) { - const lexicallyScopedSymbol = - lookupSymbolForPrivateIdentifierDeclaration( - right.escapedText, - right - ); - if (isAnyLike) { - if (lexicallyScopedSymbol) { - return apparentType; - } - if (!getContainingClass(right)) { - grammarErrorOnNode( - right, - Diagnostics - .Private_identifiers_are_not_allowed_outside_class_bodies - ); - return anyType; - } - } - prop = lexicallyScopedSymbol - ? getPrivateIdentifierPropertyOfType( - leftType, - lexicallyScopedSymbol - ) - : undefined; - // Check for private-identifier-specific shadowing and lexical-scoping errors. - if (!prop - && checkPrivateIdentifierPropertyAccess( - leftType, - right, - lexicallyScopedSymbol - )) - { - return errorType; - } - } else { - if (isAnyLike) { - if (isIdentifier(left) && parentSymbol) { - markAliasReferenced(parentSymbol, node); - } - return apparentType; - } - prop = getPropertyOfType(apparentType, right.escapedText); - } - if (isIdentifier(left) && parentSymbol - && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) - { - markAliasReferenced(parentSymbol, node); - } - - let propType: Type; - if (!prop) { - const indexInfo = - !isPrivateIdentifier(right) - && (assignmentKind === AssignmentKind.None - || !isGenericObjectType(leftType) - || isThisTypeParameter(leftType)) - ? getIndexInfoOfType(apparentType, IndexKind.String) - : undefined; - if (!(indexInfo && indexInfo.type)) { - if (isJSLiteralType(leftType)) { - return anyType; - } - if (leftType.symbol === globalThisSymbol) { - if (globalThisSymbol.exports!.has(right.escapedText) - && (globalThisSymbol.exports! - .get(right.escapedText)!.flags - & SymbolFlags.BlockScoped)) - { - error( - right, - Diagnostics - .Property_0_does_not_exist_on_type_1, - unescapeLeadingUnderscores(right.escapedText), - typeToString(leftType) - ); - } else if (noImplicitAny) { - error( - right, - Diagnostics - .Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, - typeToString(leftType) - ); - } - return anyType; - } - if (right.escapedText - && !checkAndReportErrorForExtendingInterface(node)) - { - reportNonexistentProperty( - right, - isThisTypeParameter(leftType) - ? apparentType - : leftType - ); - } - return errorType; - } - if (indexInfo.isReadonly - && (isAssignmentTarget(node) || isDeleteTarget(node))) - { - error( - node, - Diagnostics - .Index_signature_in_type_0_only_permits_reading, - typeToString(apparentType) - ); - } - propType = indexInfo.type; - } else { - checkPropertyNotUsedBeforeDeclaration(prop, node, right); - markPropertyAsReferenced( - prop, - node, - left.kind === SyntaxKind.ThisKeyword - ); - getNodeLinks(node).resolvedSymbol = prop; - checkPropertyAccessibility( - node, - left.kind === SyntaxKind.SuperKeyword, - apparentType, - prop - ); - if (isAssignmentToReadonlyEntity( - node as Expression, - prop, - assignmentKind - )) { - error( - right, - Diagnostics - .Cannot_assign_to_0_because_it_is_a_read_only_property, - idText(right) - ); - return errorType; - } - propType = getConstraintForLocation( - getTypeOfSymbol(prop), - node - ); - } - return getFlowTypeOfAccessExpression(node, prop, propType, right); - } - - function getFlowTypeOfAccessExpression( - node: ElementAccessExpression | PropertyAccessExpression - | QualifiedName, - prop: Symbol | undefined, - propType: Type, - errorNode: Node - ) { - // Only compute control flow type if this is a property access expression that isn't an - // assignment target, and the referenced property was declared as a variable, property, - // accessor, or optional method. - const assignmentKind = getAssignmentTargetKind(node); - if (!isAccessExpression(node) - || assignmentKind === AssignmentKind.Definite - || prop - && !(prop.flags - & (SymbolFlags.Variable | SymbolFlags.Property - | SymbolFlags.Accessor)) - && !(prop.flags & SymbolFlags.Method - && propType.flags & TypeFlags.Union)) - { - return propType; - } - // If strict null checks and strict property initialization checks are enabled, if we have - // a this.xxx property access, if the property is an instance property without an initializer, - // and if we are in a constructor of the same class as the property declaration, assume that - // the property is uninitialized at the top of the control flow. - let assumeUninitialized = false; - if (strictNullChecks && strictPropertyInitialization - && node.expression.kind === SyntaxKind.ThisKeyword) - { - const declaration = prop && prop.valueDeclaration; - if (declaration - && isInstancePropertyWithoutInitializer(declaration)) - { - const flowContainer = getControlFlowContainer(node); - if (flowContainer.kind === SyntaxKind.Constructor - && flowContainer.parent === declaration.parent) - { - assumeUninitialized = true; - } - } - } else if (strictNullChecks && prop && prop.valueDeclaration - && isPropertyAccessExpression(prop.valueDeclaration) - && getAssignmentDeclarationPropertyAccessKind(prop - .valueDeclaration) - && getControlFlowContainer(node) - === getControlFlowContainer(prop.valueDeclaration)) - { - assumeUninitialized = true; - } - const flowType = getFlowTypeOfReference( - node, - propType, - assumeUninitialized ? getOptionalType(propType) : propType - ); - if (assumeUninitialized - && !(getFalsyFlags(propType) & TypeFlags.Undefined) - && getFalsyFlags(flowType) & TypeFlags.Undefined) - { - error( - errorNode, - Diagnostics.Property_0_is_used_before_being_assigned, - symbolToString(prop!) - ); // TODO: GH#18217 - // Return the declared type to reduce follow-on errors - return propType; - } - return assignmentKind - ? getBaseTypeOfLiteralType(flowType) - : flowType; - } - - function checkPropertyNotUsedBeforeDeclaration( - prop: Symbol, - node: PropertyAccessExpression | QualifiedName, - right: Identifier | PrivateIdentifier - ): void { - const { valueDeclaration } = prop; - if (!valueDeclaration - || getSourceFileOfNode(node).isDeclarationFile) - { - return; - } - - let diagnosticMessage; - const declarationName = idText(right); - if (isInPropertyInitializer(node) - && !(isAccessExpression(node) - && isAccessExpression(node.expression)) - && !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) - && !isPropertyDeclaredInAncestorClass(prop)) - { - diagnosticMessage = error( - right, - Diagnostics.Property_0_is_used_before_its_initialization, - declarationName - ); - } else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration - && node.parent.kind !== SyntaxKind.TypeReference - && !(valueDeclaration.flags & NodeFlags.Ambient) - && !isBlockScopedNameDeclaredBeforeUse( - valueDeclaration, - right - )) - { - diagnosticMessage = error( - right, - Diagnostics.Class_0_used_before_its_declaration, - declarationName - ); - } - - if (diagnosticMessage) { - addRelatedInfo( - diagnosticMessage, - createDiagnosticForNode( - valueDeclaration, - Diagnostics._0_is_declared_here, - declarationName - ) - ); - } - } - - function isInPropertyInitializer(node: Node): boolean { - return !!findAncestor(node, node => { - switch (node.kind) { - case SyntaxKind.PropertyDeclaration: - return true; - case SyntaxKind.PropertyAssignment: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.SpreadAssignment: - case SyntaxKind.ComputedPropertyName: - case SyntaxKind.TemplateSpan: - case SyntaxKind.JsxExpression: - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxAttributes: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.ExpressionWithTypeArguments: - case SyntaxKind.HeritageClause: - return false; - default: - return isExpressionNode(node) ? false : 'quit'; - } - }); - } - - /** - * It's possible that "prop.valueDeclaration" is a local declaration, but the property was also declared in a superclass. - * In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration. - */ - function isPropertyDeclaredInAncestorClass(prop: Symbol): boolean { - if (!(prop.parent!.flags & SymbolFlags.Class)) { - return false; - } - let classType: InterfaceType | undefined = - getTypeOfSymbol(prop.parent!) as InterfaceType; - while (true) { - classType = classType.symbol - && getSuperClass(classType) as InterfaceType | undefined; - if (!classType) { - return false; - } - const superProperty = getPropertyOfType( - classType, - prop.escapedName - ); - if (superProperty && superProperty.valueDeclaration) { - return true; - } - } - } - - function getSuperClass(classType: InterfaceType): Type | undefined { - const x = getBaseTypes(classType); - if (x.length === 0) { - return undefined; - } - return getIntersectionType(x); - } - - function reportNonexistentProperty( - propNode: Identifier | PrivateIdentifier, - containingType: Type - ) { - let errorInfo: DiagnosticMessageChain | undefined; - let relatedInfo: Diagnostic | undefined; - if (!isPrivateIdentifier(propNode) - && containingType.flags & TypeFlags.Union - && !(containingType.flags & TypeFlags.Primitive)) - { - for (const subtype of (containingType as UnionType).types) { - if (!getPropertyOfType(subtype, propNode.escapedText) - && !getIndexInfoOfType(subtype, IndexKind.String)) - { - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics.Property_0_does_not_exist_on_type_1, - declarationNameToString(propNode), - typeToString(subtype) - ); - break; - } - } - } - if (typeHasStaticProperty(propNode.escapedText, containingType)) { - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics.Property_0_is_a_static_member_of_type_1, - declarationNameToString(propNode), - typeToString(containingType) - ); - } else { - const promisedType = getPromisedTypeOfPromise(containingType); - if (promisedType - && getPropertyOfType(promisedType, propNode.escapedText)) - { - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics.Property_0_does_not_exist_on_type_1, - declarationNameToString(propNode), - typeToString(containingType) - ); - relatedInfo = createDiagnosticForNode( - propNode, - Diagnostics.Did_you_forget_to_use_await - ); - } else { - const suggestion = - getSuggestedSymbolForNonexistentProperty( - propNode, - containingType - ); - if (suggestion !== undefined) { - const suggestedName = symbolName(suggestion); - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics - .Property_0_does_not_exist_on_type_1_Did_you_mean_2, - declarationNameToString(propNode), - typeToString(containingType), - suggestedName - ); - relatedInfo = suggestion.valueDeclaration - && createDiagnosticForNode( - suggestion.valueDeclaration, - Diagnostics._0_is_declared_here, - suggestedName - ); - } else { - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics.Property_0_does_not_exist_on_type_1, - declarationNameToString(propNode), - typeToString(containingType) - ); - } - } - } - const resultDiagnostic = createDiagnosticForNodeFromMessageChain( - propNode, - errorInfo - ); - if (relatedInfo) { - addRelatedInfo(resultDiagnostic, relatedInfo); - } - diagnostics.add(resultDiagnostic); - } - - function typeHasStaticProperty( - propName: __String, - containingType: Type - ): boolean { - const prop = containingType.symbol - && getPropertyOfType( - getTypeOfSymbol(containingType.symbol), - propName - ); - return prop !== undefined && prop.valueDeclaration - && hasModifier(prop.valueDeclaration, ModifierFlags.Static); - } - - function getSuggestedSymbolForNonexistentProperty( - name: Identifier | PrivateIdentifier | string, - containingType: Type - ): Symbol | undefined { - return getSpellingSuggestionForName( - isString(name) ? name : idText(name), - getPropertiesOfType(containingType), - SymbolFlags.Value - ); - } - - function getSuggestionForNonexistentProperty( - name: Identifier | PrivateIdentifier | string, - containingType: Type - ): string | undefined { - const suggestion = getSuggestedSymbolForNonexistentProperty( - name, - containingType - ); - return suggestion && symbolName(suggestion); - } - - function getSuggestedSymbolForNonexistentSymbol( - location: Node | undefined, - outerName: __String, - meaning: SymbolFlags - ): Symbol | undefined { - Debug.assert( - outerName !== undefined, - 'outername should always be defined' - ); - const result = resolveNameHelper( - location, - outerName, - meaning, /*nameNotFoundMessage*/ - undefined, - outerName, /*isUse*/ - false, /*excludeGlobals*/ - false, - (symbols, name, meaning) => { - Debug.assertEqual( - outerName, - name, - 'name should equal outerName' - ); - const symbol = getSymbol(symbols, name, meaning); - // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function - // So the table *contains* `x` but `x` isn't actually in scope. - // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. - return symbol - || getSpellingSuggestionForName( - unescapeLeadingUnderscores(name), - arrayFrom(symbols.values()), - meaning - ); - } - ); - return result; - } - - function getSuggestionForNonexistentSymbol( - location: Node | undefined, - outerName: __String, - meaning: SymbolFlags - ): string | undefined { - const symbolResult = getSuggestedSymbolForNonexistentSymbol( - location, - outerName, - meaning - ); - return symbolResult && symbolName(symbolResult); - } - - function getSuggestedSymbolForNonexistentModule( - name: Identifier, - targetModule: Symbol - ): Symbol | undefined { - return targetModule.exports - && getSpellingSuggestionForName( - idText(name), - getExportsOfModuleAsArray(targetModule), - SymbolFlags.ModuleMember - ); - } - - function getSuggestionForNonexistentExport( - name: Identifier, - targetModule: Symbol - ): string | undefined { - const suggestion = getSuggestedSymbolForNonexistentModule( - name, - targetModule - ); - return suggestion && symbolName(suggestion); - } - - function getSuggestionForNonexistentIndexSignature( - objectType: Type, - expr: ElementAccessExpression, - keyedType: Type - ): string | undefined { - // check if object type has setter or getter - function hasProp(name: 'set' | 'get') { - const prop = getPropertyOfObjectType( - objectType, - <__String> name - ); - if (prop) { - const s = getSingleCallSignature(getTypeOfSymbol(prop)); - return !!s && getMinArgumentCount(s) >= 1 - && isTypeAssignableTo( - keyedType, - getTypeAtPosition(s, 0) - ); - } - return false; - } - ; - - const suggestedMethod = isAssignmentTarget(expr) ? 'set' : 'get'; - if (!hasProp(suggestedMethod)) { - return undefined; - } - - let suggestion = - tryGetPropertyAccessOrIdentifierToString(expr.expression); - if (suggestion === undefined) { - suggestion = suggestedMethod; - } else { - suggestion += '.' + suggestedMethod; - } - - return suggestion; - } - - /** - * Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. - * Names less than length 3 only check for case-insensitive equality, not levenshtein distance. - * - * If there is a candidate that's the same except for case, return that. - * If there is a candidate that's within one edit of the name, return that. - * Otherwise, return the candidate with the smallest Levenshtein distance, - * except for candidates: - * * With no name - * * Whose meaning doesn't match the `meaning` parameter. - * * Whose length differs from the target name by more than 0.34 of the length of the name. - * * Whose levenshtein distance is more than 0.4 of the length of the name - * (0.4 allows 1 substitution/transposition for every 5 characters, - * and 1 insertion/deletion at 3 characters) - */ - function getSpellingSuggestionForName( - name: string, - symbols: Symbol[], - meaning: SymbolFlags - ): Symbol | undefined { - return getSpellingSuggestion(name, symbols, getCandidateName); - function getCandidateName(candidate: Symbol) { - const candidateName = symbolName(candidate); - return !startsWith(candidateName, '"') - && candidate.flags & meaning - ? candidateName - : undefined; - } - } - - function markPropertyAsReferenced( - prop: Symbol, - nodeForCheckWriteOnly: Node | undefined, - isThisAccess: boolean - ) { - const valueDeclaration = prop - && (prop.flags & SymbolFlags.ClassMember) - && prop.valueDeclaration; - if (!valueDeclaration) { - return; - } - const hasPrivateModifier = hasModifier( - valueDeclaration, - ModifierFlags.Private - ); - const hasPrivateIdentifier = - isNamedDeclaration(prop.valueDeclaration) - && isPrivateIdentifier(prop.valueDeclaration.name); - if (!hasPrivateModifier && !hasPrivateIdentifier) { - return; - } - if (nodeForCheckWriteOnly - && isWriteOnlyAccess(nodeForCheckWriteOnly) - && !(prop.flags & SymbolFlags.SetAccessor - && !(prop.flags & SymbolFlags.GetAccessor))) - { - return; - } - - if (isThisAccess) { - // Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters). - const containingMethod = findAncestor( - nodeForCheckWriteOnly, - isFunctionLikeDeclaration - ); - if (containingMethod && containingMethod.symbol === prop) { - return; - } - } - - (getCheckFlags(prop) & CheckFlags.Instantiated - ? getSymbolLinks(prop).target - : prop)!.isReferenced = SymbolFlags.All; - } - - function isValidPropertyAccess( - node: PropertyAccessExpression | QualifiedName | ImportTypeNode, - propertyName: __String - ): boolean { - switch (node.kind) { - case SyntaxKind.PropertyAccessExpression: - return isValidPropertyAccessWithType( - node, - node.expression.kind === SyntaxKind.SuperKeyword, - propertyName, - getWidenedType(checkExpression(node.expression)) - ); - case SyntaxKind.QualifiedName: - return isValidPropertyAccessWithType( - node, /*isSuper*/ - false, - propertyName, - getWidenedType(checkExpression(node.left)) - ); - case SyntaxKind.ImportType: - return isValidPropertyAccessWithType( - node, /*isSuper*/ - false, - propertyName, - getTypeFromTypeNode(node) - ); - } - } - - function isValidPropertyAccessForCompletions( - node: PropertyAccessExpression | ImportTypeNode | QualifiedName, - type: Type, - property: Symbol - ): boolean { - return isValidPropertyAccessWithType( - node, - node.kind === SyntaxKind.PropertyAccessExpression - && node.expression.kind === SyntaxKind.SuperKeyword, - property.escapedName, - type - ); - // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. - } - - function isValidPropertyAccessWithType( - node: PropertyAccessExpression | QualifiedName | ImportTypeNode, - isSuper: boolean, - propertyName: __String, - type: Type - ): boolean { - if (type === errorType || isTypeAny(type)) { - return true; - } - const prop = getPropertyOfType(type, propertyName); - if (prop) { - if (isPropertyAccessExpression(node) && prop.valueDeclaration - && isPrivateIdentifierPropertyDeclaration(prop - .valueDeclaration)) - { - const declClass = - getContainingClass(prop.valueDeclaration); - return !isOptionalChain(node) - && !!findAncestor( - node, - parent => parent === declClass - ); - } - return checkPropertyAccessibility(node, isSuper, type, prop); - } - // In js files properties of unions are allowed in completion - return isInJSFile(node) && (type.flags & TypeFlags.Union) !== 0 - && ( type).types - .some(elementType => isValidPropertyAccessWithType( - node, - isSuper, - propertyName, - elementType - )); - } - - /** - * Return the symbol of the for-in variable declared or referenced by the given for-in statement. - */ - function getForInVariableSymbol(node: ForInStatement): Symbol - | undefined - { - const initializer = node.initializer; - if (initializer.kind === SyntaxKind.VariableDeclarationList) { - const variable = ( initializer) - .declarations[0]; - if (variable && !isBindingPattern(variable.name)) { - return getSymbolOfNode(variable); - } - } else if (initializer.kind === SyntaxKind.Identifier) { - return getResolvedSymbol( initializer); - } - return undefined; - } - - /** - * Return true if the given type is considered to have numeric property names. - */ - function hasNumericPropertyNames(type: Type) { - return getIndexTypeOfType(type, IndexKind.Number) - && !getIndexTypeOfType(type, IndexKind.String); - } - - /** - * Return true if given node is an expression consisting of an identifier (possibly parenthesized) - * that references a for-in variable for an object with numeric property names. - */ - function isForInVariableForNumericPropertyNames(expr: Expression) { - const e = skipParentheses(expr); - if (e.kind === SyntaxKind.Identifier) { - const symbol = getResolvedSymbol( e); - if (symbol.flags & SymbolFlags.Variable) { - let child: Node = expr; - let node = expr.parent; - while (node) { - if (node.kind === SyntaxKind.ForInStatement - && child === ( node).statement - && getForInVariableSymbol( node) - === symbol - && hasNumericPropertyNames(getTypeOfExpression(( node) - .expression))) - { - return true; - } - child = node; - node = node.parent; - } - } - } - return false; - } - - function checkIndexedAccess(node: ElementAccessExpression): Type { - return node.flags & NodeFlags.OptionalChain - ? checkElementAccessChain(node as ElementAccessChain) - : checkElementAccessExpression( - node, - checkNonNullExpression(node.expression) - ); - } - - function checkElementAccessChain(node: ElementAccessChain) { - const exprType = checkExpression(node.expression); - const nonOptionalType = getOptionalExpressionType( - exprType, - node.expression - ); - return propagateOptionalTypeMarker( - checkElementAccessExpression( - node, - checkNonNullType(nonOptionalType, node.expression) - ), - node, - nonOptionalType !== exprType - ); - } - - function checkElementAccessExpression( - node: ElementAccessExpression, - exprType: Type - ): Type { - const objectType = - getAssignmentTargetKind(node) !== AssignmentKind.None - || isMethodAccessForCall(node) - ? getWidenedType(exprType) - : exprType; - const indexExpression = node.argumentExpression; - const indexType = checkExpression(indexExpression); - - if (objectType === errorType || objectType === silentNeverType) { - return objectType; - } - - if (isConstEnumObjectType(objectType) - && !isStringLiteralLike(indexExpression)) - { - error( - indexExpression, - Diagnostics - .A_const_enum_member_can_only_be_accessed_using_a_string_literal - ); - return errorType; - } - - const effectiveIndexType = - isForInVariableForNumericPropertyNames(indexExpression) - ? numberType - : indexType; - const accessFlags = isAssignmentTarget(node) - ? AccessFlags.Writing - | (isGenericObjectType(objectType) - && !isThisTypeParameter(objectType) - ? AccessFlags.NoIndexSignatures - : 0) - : AccessFlags.None; - const indexedAccessType = - getIndexedAccessTypeOrUndefined( - objectType, - effectiveIndexType, - node, - accessFlags - ) || errorType; - return checkIndexedAccessIndexType( - getFlowTypeOfAccessExpression( - node, - indexedAccessType.symbol, - indexedAccessType, - indexExpression - ), - node - ); - } - - function checkThatExpressionIsProperSymbolReference( - expression: Expression, - expressionType: Type, - reportError: boolean - ): boolean { - if (expressionType === errorType) { - // There is already an error, so no need to report one. - return false; - } - - if (!isWellKnownSymbolSyntactically(expression)) { - return false; - } - - // Make sure the property type is the primitive symbol type - if ((expressionType.flags & TypeFlags.ESSymbolLike) === 0) { - if (reportError) { - error( - expression, - Diagnostics - .A_computed_property_name_of_the_form_0_must_be_of_type_symbol, - getTextOfNode(expression) - ); - } - return false; - } - - // The name is Symbol., so make sure Symbol actually resolves to the - // global Symbol object - const leftHandSide = - ( expression) - .expression; - const leftHandSideSymbol = getResolvedSymbol(leftHandSide); - if (!leftHandSideSymbol) { - return false; - } - - const globalESSymbol = - getGlobalESSymbolConstructorSymbol(/*reportErrors*/ true); - if (!globalESSymbol) { - // Already errored when we tried to look up the symbol - return false; - } - - if (leftHandSideSymbol !== globalESSymbol) { - if (reportError) { - error( - leftHandSide, - Diagnostics - .Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object - ); - } - return false; - } - - return true; - } - - function callLikeExpressionMayHaveTypeArguments(node: - CallLikeExpression - ): node is CallExpression | NewExpression | TaggedTemplateExpression - | JsxOpeningElement - { - return isCallOrNewExpression(node) - || isTaggedTemplateExpression(node) - || isJsxOpeningLikeElement(node); - } - - function resolveUntypedCall(node: CallLikeExpression): Signature { - if (callLikeExpressionMayHaveTypeArguments(node)) { - // Check type arguments even though we will give an error that untyped calls may not accept type arguments. - // This gets us diagnostics for the type arguments and marks them as referenced. - forEach(node.typeArguments, checkSourceElement); - } - - if (node.kind === SyntaxKind.TaggedTemplateExpression) { - checkExpression(node.template); - } else if (isJsxOpeningLikeElement(node)) { - checkExpression(node.attributes); - } else if (node.kind !== SyntaxKind.Decorator) { - forEach(( node).arguments, argument => { - checkExpression(argument); - }); - } - return anySignature; - } - - function resolveErrorCall(node: CallLikeExpression): Signature { - resolveUntypedCall(node); - return unknownSignature; - } - - // Re-order candidate signatures into the result array. Assumes the result array to be empty. - // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order - // A nit here is that we reorder only signatures that belong to the same symbol, - // so order how inherited signatures are processed is still preserved. - // interface A { (x: string): void } - // interface B extends A { (x: 'foo'): string } - // const b: B; - // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] - function reorderCandidates( - signatures: readonly Signature[], - result: Signature[], - callChainFlags: SignatureFlags - ): void { - let lastParent: Node | undefined; - let lastSymbol: Symbol | undefined; - let cutoffIndex = 0; - let index: number | undefined; - let specializedIndex = -1; - let spliceIndex: number; - Debug.assert(!result.length); - for (const signature of signatures) { - const symbol = signature.declaration - && getSymbolOfNode(signature.declaration); - const parent = signature.declaration - && signature.declaration.parent; - if (!lastSymbol || symbol === lastSymbol) { - if (lastParent && parent === lastParent) { - index = index! + 1; - } else { - lastParent = parent; - index = cutoffIndex; - } - } else { - // current declaration belongs to a different symbol - // set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex - index = cutoffIndex = result.length; - lastParent = parent; - } - lastSymbol = symbol; - - // specialized signatures always need to be placed before non-specialized signatures regardless - // of the cutoff position; see GH#1133 - if (signatureHasLiteralTypes(signature)) { - specializedIndex++; - spliceIndex = specializedIndex; - // The cutoff index always needs to be greater than or equal to the specialized signature index - // in order to prevent non-specialized signatures from being added before a specialized - // signature. - cutoffIndex++; - } else { - spliceIndex = index; - } - - result.splice( - spliceIndex, - 0, - callChainFlags - ? getOptionalCallSignature(signature, callChainFlags) - : signature - ); - } - } - - function isSpreadArgument(arg: Expression - | undefined): arg is Expression - { - return !!arg - && (arg.kind === SyntaxKind.SpreadElement - || arg.kind === SyntaxKind.SyntheticExpression - && ( arg).isSpread); - } - - function getSpreadArgumentIndex(args: readonly Expression[]): number { - return findIndex(args, isSpreadArgument); - } - - function acceptsVoid(t: Type): boolean { - return !!(t.flags & TypeFlags.Void); - } - - function hasCorrectArity( - node: CallLikeExpression, - args: readonly Expression[], - signature: Signature, - signatureHelpTrailingComma = false - ) { - let argCount: number; - let callIsIncomplete = - false; // In incomplete call we want to be lenient when we have too few arguments - let effectiveParameterCount = getParameterCount(signature); - let effectiveMinimumArguments = getMinArgumentCount(signature); - - if (node.kind === SyntaxKind.TaggedTemplateExpression) { - argCount = args.length; - if (node.template.kind === SyntaxKind.TemplateExpression) { - // If a tagged template expression lacks a tail literal, the call is incomplete. - // Specifically, a template only can end in a TemplateTail or a Missing literal. - const lastSpan = - last(node.template - .templateSpans); // we should always have at least one span. - callIsIncomplete = nodeIsMissing(lastSpan.literal) - || !!lastSpan.literal.isUnterminated; - } else { - // If the template didn't end in a backtick, or its beginning occurred right prior to EOF, - // then this might actually turn out to be a TemplateHead in the future; - // so we consider the call to be incomplete. - const templateLiteral = node.template; - Debug - .assert(templateLiteral.kind - === SyntaxKind.NoSubstitutionTemplateLiteral); - callIsIncomplete = !!templateLiteral.isUnterminated; - } - } else if (node.kind === SyntaxKind.Decorator) { - argCount = getDecoratorArgumentCount(node, signature); - } else if (isJsxOpeningLikeElement(node)) { - callIsIncomplete = node.attributes.end === node.end; - if (callIsIncomplete) { - return true; - } - argCount = effectiveMinimumArguments === 0 ? args.length : 1; - effectiveParameterCount = args.length === 0 - ? effectiveParameterCount - : 1; // class may have argumentless ctor functions - still resolve ctor and compare vs props member type - effectiveMinimumArguments = Math.min( - effectiveMinimumArguments, - 1 - ); // sfc may specify context argument - handled by framework and not typechecked - } else { - if (!node.arguments) { - // This only happens when we have something of the form: 'new C' - Debug.assert(node.kind === SyntaxKind.NewExpression); - return getMinArgumentCount(signature) === 0; - } - - argCount = signatureHelpTrailingComma - ? args.length + 1 - : args.length; - - // If we are missing the close parenthesis, the call is incomplete. - callIsIncomplete = node.arguments.end === node.end; - - // If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range. - const spreadArgIndex = getSpreadArgumentIndex(args); - if (spreadArgIndex >= 0) { - return spreadArgIndex >= getMinArgumentCount(signature) - && (hasEffectiveRestParameter(signature) - || spreadArgIndex < getParameterCount(signature)); - } - } - - // Too many arguments implies incorrect arity. - if (!hasEffectiveRestParameter(signature) - && argCount > effectiveParameterCount) - { - return false; - } - - // If the call is incomplete, we should skip the lower bound check. - // JSX signatures can have extra parameters provided by the library which we don't check - if (callIsIncomplete || argCount >= effectiveMinimumArguments) { - return true; - } - for (let i = argCount; i < effectiveMinimumArguments; i++) { - const type = getTypeAtPosition(signature, i); - if (filterType(type, acceptsVoid).flags & TypeFlags.Never) { - return false; - } - } - return true; - } - - function hasCorrectTypeArgumentArity( - signature: Signature, - typeArguments: NodeArray | undefined - ) { - // If the user supplied type arguments, but the number of type arguments does not match - // the declared number of type parameters, the call has an incorrect arity. - const numTypeParameters = length(signature.typeParameters); - const minTypeArgumentCount = - getMinTypeArgumentCount(signature.typeParameters); - return !some(typeArguments) - || (typeArguments.length >= minTypeArgumentCount - && typeArguments.length <= numTypeParameters); - } - - // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. - function getSingleCallSignature(type: Type): Signature | undefined { - return getSingleSignature( - type, - SignatureKind.Call, /*allowMembers*/ - false - ); - } - - function getSingleCallOrConstructSignature(type: Type): Signature - | undefined - { - return getSingleSignature( - type, - SignatureKind.Call, /*allowMembers*/ - false - ) - || getSingleSignature( - type, - SignatureKind.Construct, /*allowMembers*/ - false - ); - } - - function getSingleSignature( - type: Type, - kind: SignatureKind, - allowMembers: boolean - ): Signature | undefined { - if (type.flags & TypeFlags.Object) { - const resolved = - resolveStructuredTypeMembers( type); - if (allowMembers || resolved.properties.length === 0 - && !resolved.stringIndexInfo && !resolved.numberIndexInfo) - { - if (kind === SignatureKind.Call - && resolved.callSignatures.length === 1 - && resolved.constructSignatures.length === 0) - { - return resolved.callSignatures[0]; - } - if (kind === SignatureKind.Construct - && resolved.constructSignatures.length === 1 - && resolved.callSignatures.length === 0) - { - return resolved.constructSignatures[0]; - } - } - } - return undefined; - } - - // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) - function instantiateSignatureInContextOf( - signature: Signature, - contextualSignature: Signature, - inferenceContext?: InferenceContext, - compareTypes?: TypeComparer - ): Signature { - const context = createInferenceContext( - signature.typeParameters!, - signature, - InferenceFlags.None, - compareTypes - ); - // We clone the inferenceContext to avoid fixing. For example, when the source signature is (x: T) => T[] and - // the contextual signature is (...args: A) => B, we want to infer the element type of A's constraint (say 'any') - // for T but leave it possible to later infer '[any]' back to A. - const restType = getEffectiveRestType(contextualSignature); - const mapper = inferenceContext - && (restType && restType.flags & TypeFlags.TypeParameter - ? inferenceContext.nonFixingMapper - : inferenceContext.mapper); - const sourceSignature = mapper - ? instantiateSignature(contextualSignature, mapper) - : contextualSignature; - applyToParameterTypes( - sourceSignature, - signature, - (source, target) => { - // Type parameters from outer context referenced by source type are fixed by instantiation of the source type - inferTypes(context.inferences, source, target); - } - ); - if (!inferenceContext) { - applyToReturnTypes( - contextualSignature, - signature, - (source, target) => { - inferTypes( - context.inferences, - source, - target, - InferencePriority.ReturnType - ); - } - ); - } - return getSignatureInstantiation( - signature, - getInferredTypes(context), - isInJSFile(contextualSignature.declaration) - ); - } - - function inferJsxTypeArguments( - node: JsxOpeningLikeElement, - signature: Signature, - checkMode: CheckMode, - context: InferenceContext - ): Type[] { - const paramType = getEffectiveFirstArgumentForJsxSignature( - signature, - node - ); - const checkAttrType = checkExpressionWithContextualType( - node.attributes, - paramType, - context, - checkMode - ); - inferTypes(context.inferences, checkAttrType, paramType); - return getInferredTypes(context); - } - - function inferTypeArguments( - node: CallLikeExpression, - signature: Signature, - args: readonly Expression[], - checkMode: CheckMode, - context: InferenceContext - ): Type[] { - if (isJsxOpeningLikeElement(node)) { - return inferJsxTypeArguments( - node, - signature, - checkMode, - context - ); - } - - // If a contextual type is available, infer from that type to the return type of the call expression. For - // example, given a 'function wrap(cb: (x: T) => U): (x: T) => U' and a call expression - // 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the - // return type of 'wrap'. - if (node.kind !== SyntaxKind.Decorator) { - const contextualType = getContextualType(node); - if (contextualType) { - // We clone the inference context to avoid disturbing a resolution in progress for an - // outer call expression. Effectively we just want a snapshot of whatever has been - // inferred for any outer call expression so far. - const outerContext = getInferenceContext(node); - const outerMapper = - getMapperFromContext(cloneInferenceContext( - outerContext, - InferenceFlags.NoDefault - )); - const instantiatedType = instantiateType( - contextualType, - outerMapper - ); - // If the contextual type is a generic function type with a single call signature, we - // instantiate the type with its own type parameters and type arguments. This ensures that - // the type parameters are not erased to type any during type inference such that they can - // be inferred as actual types from the contextual type. For example: - // declare function arrayMap(f: (x: T) => U): (a: T[]) => U[]; - // const boxElements: (a: A[]) => { value: A }[] = arrayMap(value => ({ value })); - // Above, the type of the 'value' parameter is inferred to be 'A'. - const contextualSignature = - getSingleCallSignature(instantiatedType); - const inferenceSourceType = - contextualSignature - && contextualSignature.typeParameters - ? getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments( - contextualSignature, - contextualSignature.typeParameters - )) - : instantiatedType; - const inferenceTargetType = - getReturnTypeOfSignature(signature); - // Inferences made from return types have lower priority than all other inferences. - inferTypes( - context.inferences, - inferenceSourceType, - inferenceTargetType, - InferencePriority.ReturnType - ); - // Create a type mapper for instantiating generic contextual types using the inferences made - // from the return type. We need a separate inference pass here because (a) instantiation of - // the source type uses the outer context's return mapper (which excludes inferences made from - // outer arguments), and (b) we don't want any further inferences going into this context. - const returnContext = createInferenceContext( - signature.typeParameters!, - signature, - context.flags - ); - const returnSourceType = instantiateType( - contextualType, - outerContext && outerContext.returnMapper - ); - inferTypes( - returnContext.inferences, - returnSourceType, - inferenceTargetType - ); - context - .returnMapper = some( - returnContext.inferences, - hasInferenceCandidates - ) - ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) - : undefined; - } - } - - const thisType = getThisTypeOfSignature(signature); - if (thisType) { - const thisArgumentNode = getThisArgumentOfCall(node); - const thisArgumentType = thisArgumentNode - ? checkExpression(thisArgumentNode) - : voidType; - inferTypes(context.inferences, thisArgumentType, thisType); - } - - const restType = getNonArrayRestType(signature); - const argCount = restType - ? Math.min(getParameterCount(signature) - 1, args.length) - : args.length; - for (let i = 0; i < argCount; i++) { - const arg = args[i]; - if (arg.kind !== SyntaxKind.OmittedExpression) { - const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType( - arg, - paramType, - context, - checkMode - ); - inferTypes(context.inferences, argType, paramType); - } - } - - if (restType) { - const spreadType = getSpreadArgumentType( - args, - argCount, - args.length, - restType, - context - ); - inferTypes(context.inferences, spreadType, restType); - } - - return getInferredTypes(context); - } - - function getArrayifiedType(type: Type) { - return type.flags & TypeFlags.Union - ? mapType(type, getArrayifiedType) - : type.flags & (TypeFlags.Any | TypeFlags.Instantiable) - || isMutableArrayOrTuple(type) - ? type - : isTupleType(type) - ? createTupleType( - getTypeArguments(type), - type.target.minLength, - type.target.hasRestElement, /*readonly*/ - false, - type.target.associatedNames - ) - : createArrayType(getIndexedAccessType( - type, - numberType - )); - } - - function getSpreadArgumentType( - args: readonly Expression[], - index: number, - argCount: number, - restType: Type, - context: InferenceContext | undefined - ) { - if (index >= argCount - 1) { - const arg = args[argCount - 1]; - if (isSpreadArgument(arg)) { - // We are inferring from a spread expression in the last argument position, i.e. both the parameter - // and the argument are ...x forms. - return arg.kind === SyntaxKind.SyntheticExpression - ? createArrayType(( arg).type) - : getArrayifiedType(checkExpressionWithContextualType( - ( arg).expression, - restType, - context, - CheckMode.Normal - )); - } - } - const types = []; - let spreadIndex = -1; - for (let i = index; i < argCount; i++) { - const contextualType = getIndexedAccessType( - restType, - getLiteralType(i - index) - ); - const argType = checkExpressionWithContextualType( - args[i], - contextualType, - context, - CheckMode.Normal - ); - if (spreadIndex < 0 && isSpreadArgument(args[i])) { - spreadIndex = i - index; - } - const hasPrimitiveContextualType = maybeTypeOfKind( - contextualType, - TypeFlags.Primitive | TypeFlags.Index - ); - types - .push(hasPrimitiveContextualType - ? getRegularTypeOfLiteralType(argType) - : getWidenedLiteralType(argType)); - } - return spreadIndex < 0 - ? createTupleType(types) - : createTupleType( - append( - types.slice(0, spreadIndex), - getUnionType(types.slice(spreadIndex)) - ), - spreadIndex, /*hasRestElement*/ - true - ); - } - - function checkTypeArguments( - signature: Signature, - typeArgumentNodes: readonly TypeNode[], - reportErrors: boolean, - headMessage?: DiagnosticMessage - ): Type[] | undefined { - const isJavascript = isInJSFile(signature.declaration); - const typeParameters = signature.typeParameters!; - const typeArgumentTypes = fillMissingTypeArguments( - map(typeArgumentNodes, getTypeFromTypeNode), - typeParameters, - getMinTypeArgumentCount(typeParameters), - isJavascript - ); - let mapper: TypeMapper | undefined; - for (let i = 0; i < typeArgumentNodes.length; i++) { - Debug.assert( - typeParameters[i] !== undefined, - 'Should not call checkTypeArguments with too many type arguments' - ); - const constraint = - getConstraintOfTypeParameter(typeParameters[i]); - if (constraint) { - const errorInfo = reportErrors && headMessage - ? (() => chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .Type_0_does_not_satisfy_the_constraint_1 - )) - : undefined; - const typeArgumentHeadMessage = headMessage - || Diagnostics - .Type_0_does_not_satisfy_the_constraint_1; - if (!mapper) { - mapper = createTypeMapper( - typeParameters, - typeArgumentTypes - ); - } - const typeArgument = typeArgumentTypes[i]; - if (!checkTypeAssignableTo( - typeArgument, - getTypeWithThisArgument( - instantiateType(constraint, mapper), - typeArgument - ), - reportErrors ? typeArgumentNodes[i] : undefined, - typeArgumentHeadMessage, - errorInfo - )) { - return undefined; - } - } - } - return typeArgumentTypes; - } - - function getJsxReferenceKind(node: - JsxOpeningLikeElement): JsxReferenceKind - { - if (isJsxIntrinsicIdentifier(node.tagName)) { - return JsxReferenceKind.Mixed; - } - const tagType = getApparentType(checkExpression(node.tagName)); - if (length(getSignaturesOfType( - tagType, - SignatureKind.Construct - ))) { - return JsxReferenceKind.Component; - } - if (length(getSignaturesOfType(tagType, SignatureKind.Call))) { - return JsxReferenceKind.Function; - } - return JsxReferenceKind.Mixed; - } - - /** - * Check if the given signature can possibly be a signature called by the JSX opening-like element. - * @param node a JSX opening-like element we are trying to figure its call signature - * @param signature a candidate signature we are trying whether it is a call signature - * @param relation a relationship to check parameter and argument type - */ - function checkApplicableSignatureForJsxOpeningLikeElement( - node: JsxOpeningLikeElement, - signature: Signature, - relation: Map, - checkMode: CheckMode, - reportErrors: boolean, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined, - errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } - ) { - // Stateless function components can have maximum of three arguments: "props", "context", and "updater". - // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, - // can be specified by users through attributes property. - const paramType = getEffectiveFirstArgumentForJsxSignature( - signature, - node - ); - const attributesType = checkExpressionWithContextualType( - node.attributes, - paramType, /*inferenceContext*/ - undefined, - checkMode - ); - return checkTypeRelatedToAndOptionallyElaborate( - attributesType, - paramType, - relation, - reportErrors ? node.tagName : undefined, - node.attributes, - /*headMessage*/ undefined, - containingMessageChain, - errorOutputContainer - ); - } - - function getSignatureApplicabilityError( - node: CallLikeExpression, - args: readonly Expression[], - signature: Signature, - relation: Map, - checkMode: CheckMode, - reportErrors: boolean, - containingMessageChain: (() => DiagnosticMessageChain | undefined) - | undefined - ): readonly Diagnostic[] | undefined { - const errorOutputContainer: { errors?: Diagnostic[]; - skipLogging?: boolean; } = { errors: undefined, - skipLogging: true }; - if (isJsxOpeningLikeElement(node)) { - if (!checkApplicableSignatureForJsxOpeningLikeElement( - node, - signature, - relation, - checkMode, - reportErrors, - containingMessageChain, - errorOutputContainer - )) { - Debug.assert( - !reportErrors || !!errorOutputContainer.errors, - 'jsx should have errors when reporting errors' - ); - return errorOutputContainer.errors || emptyArray; - } - return undefined; - } - const thisType = getThisTypeOfSignature(signature); - if (thisType && thisType !== voidType - && node.kind !== SyntaxKind.NewExpression) - { - // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType - // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. - // If the expression is a new expression, then the check is skipped. - const thisArgumentNode = getThisArgumentOfCall(node); - let thisArgumentType: Type; - if (thisArgumentNode) { - thisArgumentType = checkExpression(thisArgumentNode); - if (isOptionalChainRoot(thisArgumentNode.parent)) { - thisArgumentType = getNonNullableType(thisArgumentType); - } else if (isOptionalChain(thisArgumentNode.parent)) { - thisArgumentType = removeOptionalTypeMarker(thisArgumentType); - } - } else { - thisArgumentType = voidType; - } - - const errorNode = reportErrors - ? (thisArgumentNode || node) - : undefined; - const headMessage = Diagnostics - .The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1; - if (!checkTypeRelatedTo( - thisArgumentType, - thisType, - relation, - errorNode, - headMessage, - containingMessageChain, - errorOutputContainer - )) { - Debug.assert( - !reportErrors || !!errorOutputContainer.errors, - 'this parameter should have errors when reporting errors' - ); - return errorOutputContainer.errors || emptyArray; - } - } - const headMessage = Diagnostics - .Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; - const restType = getNonArrayRestType(signature); - const argCount = restType - ? Math.min(getParameterCount(signature) - 1, args.length) - : args.length; - for (let i = 0; i < argCount; i++) { - const arg = args[i]; - if (arg.kind !== SyntaxKind.OmittedExpression) { - const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType( - arg, - paramType, /*inferenceContext*/ - undefined, - checkMode - ); - // If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive), - // we obtain the regular type of any object literal arguments because we may not have inferred complete - // parameter types yet and therefore excess property checks may yield false positives (see #17041). - const checkArgType = - checkMode & CheckMode.SkipContextSensitive - ? getRegularTypeOfObjectLiteral(argType) - : argType; - if (!checkTypeRelatedToAndOptionallyElaborate( - checkArgType, - paramType, - relation, - reportErrors ? arg : undefined, - arg, - headMessage, - containingMessageChain, - errorOutputContainer - )) { - Debug.assert( - !reportErrors || !!errorOutputContainer.errors, - 'parameter should have errors when reporting errors' - ); - maybeAddMissingAwaitInfo(arg, checkArgType, paramType); - return errorOutputContainer.errors || emptyArray; - } - } - } - if (restType) { - const spreadType = getSpreadArgumentType( - args, - argCount, - args.length, - restType, /*context*/ - undefined - ); - const errorNode = reportErrors - ? argCount < args.length ? args[argCount] : node - : undefined; - if (!checkTypeRelatedTo( - spreadType, - restType, - relation, - errorNode, - headMessage, /*containingMessageChain*/ - undefined, - errorOutputContainer - )) { - Debug.assert( - !reportErrors || !!errorOutputContainer.errors, - 'rest parameter should have errors when reporting errors' - ); - maybeAddMissingAwaitInfo(errorNode, spreadType, restType); - return errorOutputContainer.errors || emptyArray; - } - } - return undefined; - - function maybeAddMissingAwaitInfo( - errorNode: Node | undefined, - source: Type, - target: Type - ) { - if (errorNode && reportErrors && errorOutputContainer.errors - && errorOutputContainer.errors.length) - { - // Bail if target is Promise-like---something else is wrong - if (getAwaitedTypeOfPromise(target)) { - return; - } - const awaitedTypeOfSource = - getAwaitedTypeOfPromise(source); - if (awaitedTypeOfSource - && isTypeRelatedTo( - awaitedTypeOfSource, - target, - relation - )) - { - addRelatedInfo( - errorOutputContainer.errors[0], - createDiagnosticForNode( - errorNode, - Diagnostics.Did_you_forget_to_use_await - ) - ); - } - } - } - } - - /** - * Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise. - */ - function getThisArgumentOfCall(node: - CallLikeExpression): LeftHandSideExpression | undefined - { - if (node.kind === SyntaxKind.CallExpression) { - const callee = skipOuterExpressions(node.expression); - if (isAccessExpression(callee)) { - return callee.expression; - } - } - } - - function createSyntheticExpression( - parent: Node, - type: Type, - isSpread?: boolean - ) { - const result = createNode( - SyntaxKind.SyntheticExpression, - parent.pos, - parent.end - ); - result.parent = parent; - result.type = type; - result.isSpread = isSpread || false; - return result; - } - - /** - * Returns the effective arguments for an expression that works like a function invocation. - */ - function getEffectiveCallArguments(node: - CallLikeExpression): readonly Expression[] - { - if (node.kind === SyntaxKind.TaggedTemplateExpression) { - const template = node.template; - const args: Expression[] = - [createSyntheticExpression( - template, - getGlobalTemplateStringsArrayType() - )]; - if (template.kind === SyntaxKind.TemplateExpression) { - forEach(template.templateSpans, span => { - args.push(span.expression); - }); - } - return args; - } - if (node.kind === SyntaxKind.Decorator) { - return getEffectiveDecoratorArguments(node); - } - if (isJsxOpeningLikeElement(node)) { - return node.attributes.properties.length > 0 - || (isJsxOpeningElement(node) - && node.parent.children.length > 0) - ? [node.attributes] - : emptyArray; - } - const args = node.arguments || emptyArray; - const length = args.length; - if (length && isSpreadArgument(args[length - 1]) - && getSpreadArgumentIndex(args) === length - 1) - { - // We have a spread argument in the last position and no other spread arguments. If the type - // of the argument is a tuple type, spread the tuple elements into the argument list. We can - // call checkExpressionCached because spread expressions never have a contextual type. - const spreadArgument = args[length - 1]; - const type = flowLoopCount - ? checkExpression(spreadArgument.expression) - : checkExpressionCached(spreadArgument.expression); - if (isTupleType(type)) { - const typeArguments = - getTypeArguments( type); - const restIndex = type.target.hasRestElement - ? typeArguments.length - 1 - : -1; - const syntheticArgs = map( - typeArguments, - (t, i) => createSyntheticExpression( - spreadArgument, - t, /*isSpread*/ - i === restIndex - ) - ); - return concatenate( - args.slice(0, length - 1), - syntheticArgs - ); - } - } - return args; - } - - /** - * Returns the synthetic argument list for a decorator invocation. - */ - function getEffectiveDecoratorArguments(node: - Decorator): readonly Expression[] - { - const parent = node.parent; - const expr = node.expression; - switch (parent.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - // For a class decorator, the `target` is the type of the class (e.g. the - // "static" or "constructor" side of the class). - return [ - createSyntheticExpression( - expr, - getTypeOfSymbol(getSymbolOfNode(parent)) - ) - ]; - case SyntaxKind.Parameter: - // A parameter declaration decorator will have three arguments (see - // `ParameterDecorator` in core.d.ts). - const func = parent.parent; - return [ - createSyntheticExpression( - expr, - parent.parent.kind === SyntaxKind.Constructor - ? getTypeOfSymbol(getSymbolOfNode(func)) - : errorType - ), - createSyntheticExpression(expr, anyType), - createSyntheticExpression(expr, numberType) - ]; - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // A method or accessor declaration decorator will have two or three arguments (see - // `PropertyDecorator` and `MethodDecorator` in core.d.ts). If we are emitting decorators - // for ES3, we will only pass two arguments. - const hasPropDesc = - parent.kind !== SyntaxKind.PropertyDeclaration - && languageVersion !== ScriptTarget.ES3; - return [ - createSyntheticExpression( - expr, - getParentTypeOfClassElement( parent) - ), - createSyntheticExpression( - expr, - getClassElementPropertyKeyType( parent) - ), - createSyntheticExpression( - expr, - hasPropDesc - ? createTypedPropertyDescriptorType(getTypeOfNode(parent)) - : anyType - ) - ]; - } - return Debug.fail(); - } - - /** - * Returns the argument count for a decorator node that works like a function invocation. - */ - function getDecoratorArgumentCount( - node: Decorator, - signature: Signature - ) { - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - return 1; - case SyntaxKind.PropertyDeclaration: - return 2; - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // For ES3 or decorators with only two parameters we supply only two arguments - return languageVersion === ScriptTarget.ES3 - || signature.parameters.length <= 2 - ? 2 - : 3; - case SyntaxKind.Parameter: - return 3; - default: - return Debug.fail(); - } - } - function getDiagnosticSpanForCallNode( - node: CallExpression, - doNotIncludeArguments?: boolean - ) { - let start: number; - let length: number; - const sourceFile = getSourceFileOfNode(node); - - if (isPropertyAccessExpression(node.expression)) { - const nameSpan = getErrorSpanForNode( - sourceFile, - node.expression.name - ); - start = nameSpan.start; - length = doNotIncludeArguments - ? nameSpan.length - : node.end - start; - } else { - const expressionSpan = getErrorSpanForNode( - sourceFile, - node.expression - ); - start = expressionSpan.start; - length = doNotIncludeArguments - ? expressionSpan.length - : node.end - start; - } - return { start, length, sourceFile }; - } - function getDiagnosticForCallNode( - node: CallLikeExpression, - message: DiagnosticMessage, - arg0?: string | number, - arg1?: string | number, - arg2?: string | number, - arg3?: string | number - ): DiagnosticWithLocation { - if (isCallExpression(node)) { - const { sourceFile, start, length } = - getDiagnosticSpanForCallNode(node); - return createFileDiagnostic( - sourceFile, - start, - length, - message, - arg0, - arg1, - arg2, - arg3 - ); - } else { - return createDiagnosticForNode( - node, - message, - arg0, - arg1, - arg2, - arg3 - ); - } - } - - function getArgumentArityError( - node: CallLikeExpression, - signatures: readonly Signature[], - args: readonly Expression[] - ) { - let min = Number.POSITIVE_INFINITY; - let max = Number.NEGATIVE_INFINITY; - let belowArgCount = Number.NEGATIVE_INFINITY; - let aboveArgCount = Number.POSITIVE_INFINITY; - - let argCount = args.length; - let closestSignature: Signature | undefined; - for (const sig of signatures) { - const minCount = getMinArgumentCount(sig); - const maxCount = getParameterCount(sig); - if (minCount < argCount - && minCount > belowArgCount) - belowArgCount = minCount; - if (argCount < maxCount - && maxCount < aboveArgCount) - aboveArgCount = maxCount; - if (minCount < min) { - min = minCount; - closestSignature = sig; - } - max = Math.max(max, maxCount); - } - - const hasRestParameter = some( - signatures, - hasEffectiveRestParameter - ); - const paramRange = hasRestParameter - ? min - : min < max - ? min + '-' + max - : min; - const hasSpreadArgument = getSpreadArgumentIndex(args) > -1; - if (argCount <= max && hasSpreadArgument) { - argCount--; - } - - let spanArray: NodeArray; - let related: DiagnosticWithLocation | undefined; - - const error = hasRestParameter || hasSpreadArgument - ? hasRestParameter && hasSpreadArgument - ? Diagnostics - .Expected_at_least_0_arguments_but_got_1_or_more - : hasRestParameter - ? Diagnostics.Expected_at_least_0_arguments_but_got_1 - : Diagnostics.Expected_0_arguments_but_got_1_or_more - : Diagnostics.Expected_0_arguments_but_got_1; - - if (closestSignature - && getMinArgumentCount(closestSignature) > argCount - && closestSignature.declaration) - { - const paramDecl = closestSignature.declaration.parameters - [closestSignature.thisParameter ? argCount + 1 : argCount]; - if (paramDecl) { - related = createDiagnosticForNode( - paramDecl, - isBindingPattern(paramDecl.name) - ? Diagnostics - .An_argument_matching_this_binding_pattern_was_not_provided - : Diagnostics.An_argument_for_0_was_not_provided, - !paramDecl.name - ? argCount - : !isBindingPattern(paramDecl.name) - ? idText(getFirstIdentifier(paramDecl.name)) - : undefined - ); - } - } - if (min < argCount && argCount < max) { - return getDiagnosticForCallNode( - node, - Diagnostics - .No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, - argCount, - belowArgCount, - aboveArgCount - ); - } - - if (!hasSpreadArgument && argCount < min) { - const diagnostic = getDiagnosticForCallNode( - node, - error, - paramRange, - argCount - ); - return related - ? addRelatedInfo(diagnostic, related) - : diagnostic; - } - - if (hasRestParameter || hasSpreadArgument) { - spanArray = createNodeArray(args); - if (hasSpreadArgument && argCount) { - const nextArg = - elementAt(args, getSpreadArgumentIndex(args) + 1) - || undefined; - spanArray = createNodeArray(args - .slice(max > argCount && nextArg - ? args.indexOf(nextArg) - : Math.min(max, args.length - 1))); - } - } else { - spanArray = createNodeArray(args.slice(max)); - } - - spanArray.pos = first(spanArray).pos; - spanArray.end = last(spanArray).end; - if (spanArray.end === spanArray.pos) { - spanArray.end++; - } - const diagnostic = createDiagnosticForNodeArray( - getSourceFileOfNode(node), - spanArray, - error, - paramRange, - argCount - ); - return related ? addRelatedInfo(diagnostic, related) : diagnostic; - } - - function getTypeArgumentArityError( - node: Node, - signatures: readonly Signature[], - typeArguments: NodeArray - ) { - const argCount = typeArguments.length; - // No overloads exist - if (signatures.length === 1) { - const sig = signatures[0]; - const min = getMinTypeArgumentCount(sig.typeParameters); - const max = length(sig.typeParameters); - return createDiagnosticForNodeArray( - getSourceFileOfNode(node), - typeArguments, - Diagnostics.Expected_0_type_arguments_but_got_1, - min < max ? min + '-' + max : min, - argCount - ); - } - // Overloads exist - let belowArgCount = -Infinity; - let aboveArgCount = Infinity; - for (const sig of signatures) { - const min = getMinTypeArgumentCount(sig.typeParameters); - const max = length(sig.typeParameters); - if (min > argCount) { - aboveArgCount = Math.min(aboveArgCount, min); - } else if (max < argCount) { - belowArgCount = Math.max(belowArgCount, max); - } - } - if (belowArgCount !== -Infinity && aboveArgCount !== Infinity) { - return createDiagnosticForNodeArray( - getSourceFileOfNode(node), - typeArguments, - Diagnostics - .No_overload_expects_0_type_arguments_but_overloads_do_exist_that_expect_either_1_or_2_type_arguments, - argCount, - belowArgCount, - aboveArgCount - ); - } - return createDiagnosticForNodeArray( - getSourceFileOfNode(node), - typeArguments, - Diagnostics.Expected_0_type_arguments_but_got_1, - belowArgCount === -Infinity ? aboveArgCount : belowArgCount, - argCount - ); - } - - function resolveCall( - node: CallLikeExpression, - signatures: readonly Signature[], - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode, - callChainFlags: SignatureFlags, - fallbackError?: DiagnosticMessage - ): Signature { - const isTaggedTemplate = - node.kind === SyntaxKind.TaggedTemplateExpression; - const isDecorator = node.kind === SyntaxKind.Decorator; - const isJsxOpeningOrSelfClosingElement = - isJsxOpeningLikeElement(node); - const reportErrors = !candidatesOutArray; - - let typeArguments: NodeArray | undefined; - - if (!isDecorator) { - typeArguments = ( node).typeArguments; - - // We already perform checking on the type arguments on the class declaration itself. - if (isTaggedTemplate || isJsxOpeningOrSelfClosingElement - || ( node).expression.kind - !== SyntaxKind.SuperKeyword) - { - forEach(typeArguments, checkSourceElement); - } - } - - const candidates = candidatesOutArray || []; - // reorderCandidates fills up the candidates array directly - reorderCandidates(signatures, candidates, callChainFlags); - if (!candidates.length) { - if (reportErrors) { - diagnostics - .add(getDiagnosticForCallNode( - node, - Diagnostics - .Call_target_does_not_contain_any_signatures - )); - } - return resolveErrorCall(node); - } - - const args = getEffectiveCallArguments(node); - - // The excludeArgument array contains true for each context sensitive argument (an argument - // is context sensitive it is susceptible to a one-time permanent contextual typing). - // - // The idea is that we will perform type argument inference & assignability checking once - // without using the susceptible parameters that are functions, and once more for those - // parameters, contextually typing each as we go along. - // - // For a tagged template, then the first argument be 'undefined' if necessary because it - // represents a TemplateStringsArray. - // - // For a decorator, no arguments are susceptible to contextual typing due to the fact - // decorators are applied to a declaration by the emitter, and not to an expression. - const isSingleNonGenericCandidate = candidates.length === 1 - && !candidates[0].typeParameters; - let argCheckMode = - !isDecorator && !isSingleNonGenericCandidate - && some(args, isContextSensitive) - ? CheckMode.SkipContextSensitive - : CheckMode.Normal; - - // The following variables are captured and modified by calls to chooseOverload. - // If overload resolution or type argument inference fails, we want to report the - // best error possible. The best error is one which says that an argument was not - // assignable to a parameter. This implies that everything else about the overload - // was fine. So if there is any overload that is only incorrect because of an - // argument, we will report an error on that one. - // - // function foo(s: string): void; - // function foo(n: number): void; // Report argument error on this overload - // function foo(): void; - // foo(true); - // - // If none of the overloads even made it that far, there are two possibilities. - // There was a problem with type arguments for some overload, in which case - // report an error on that. Or none of the overloads even had correct arity, - // in which case give an arity error. - // - // function foo(x: T): void; // Report type argument error - // function foo(): void; - // foo(0); - // - let candidatesForArgumentError: Signature[] | undefined; - let candidateForArgumentArityError: Signature | undefined; - let candidateForTypeArgumentError: Signature | undefined; - let result: Signature | undefined; - - // If we are in signature help, a trailing comma indicates that we intend to provide another argument, - // so we will only accept overloads with arity at least 1 higher than the current number of provided arguments. - const signatureHelpTrailingComma = - !!(checkMode & CheckMode.IsForSignatureHelp) - && node.kind === SyntaxKind.CallExpression - && node.arguments.hasTrailingComma; - - // Section 4.12.1: - // if the candidate list contains one or more signatures for which the type of each argument - // expression is a subtype of each corresponding parameter type, the return type of the first - // of those signatures becomes the return type of the function call. - // Otherwise, the return type of the first signature in the candidate list becomes the return - // type of the function call. - // - // Whether the call is an error is determined by assignability of the arguments. The subtype pass - // is just important for choosing the best signature. So in the case where there is only one - // signature, the subtype pass is useless. So skipping it is an optimization. - if (candidates.length > 1) { - result = chooseOverload( - candidates, - subtypeRelation, - signatureHelpTrailingComma - ); - } - if (!result) { - result = chooseOverload( - candidates, - assignableRelation, - signatureHelpTrailingComma - ); - } - if (result) { - return result; - } - - // No signatures were applicable. Now report errors based on the last applicable signature with - // no arguments excluded from assignability checks. - // If candidate is undefined, it means that no candidates had a suitable arity. In that case, - // skip the checkApplicableSignature check. - if (reportErrors) { - if (candidatesForArgumentError) { - if (candidatesForArgumentError.length === 1 - || candidatesForArgumentError.length > 3) - { - const last = candidatesForArgumentError - [candidatesForArgumentError.length - 1]; - let chain: DiagnosticMessageChain | undefined; - if (candidatesForArgumentError.length > 3) { - chain = chainDiagnosticMessages( - chain, - Diagnostics - .The_last_overload_gave_the_following_error - ); - chain = chainDiagnosticMessages( - chain, - Diagnostics.No_overload_matches_this_call - ); - } - const diags = getSignatureApplicabilityError( - node, - args, - last, - assignableRelation, - CheckMode.Normal, /*reportErrors*/ - true, - () => chain - ); - if (diags) { - for (const d of diags) { - if (last.declaration - && candidatesForArgumentError.length > 3) - { - addRelatedInfo( - d, - createDiagnosticForNode( - last.declaration, - Diagnostics - .The_last_overload_is_declared_here - ) - ); - } - diagnostics.add(d); - } - } else { - Debug.fail('No error for last overload signature'); - } - } else { - const allDiagnostics: - (readonly DiagnosticRelatedInformation[])[] = []; - let max = 0; - let min = Number.MAX_VALUE; - let minIndex = 0; - let i = 0; - for (const c of candidatesForArgumentError) { - const chain = () => chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .Overload_0_of_1_2_gave_the_following_error, - i + 1, - candidates.length, - signatureToString(c) - ); - const diags = getSignatureApplicabilityError( - node, - args, - c, - assignableRelation, - CheckMode.Normal, /*reportErrors*/ - true, - chain - ); - if (diags) { - if (diags.length <= min) { - min = diags.length; - minIndex = i; - } - max = Math.max(max, diags.length); - allDiagnostics.push(diags); - } else { - Debug - .fail('No error for 3 or fewer overload signatures'); - } - i++; - } - - const diags = max > 1 - ? allDiagnostics[minIndex] - : flatten(allDiagnostics); - Debug.assert( - diags.length > 0, - 'No errors reported for 3 or fewer overload signatures' - ); - const chain = chainDiagnosticMessages( - map( - diags, - d => typeof d.messageText === 'string' - ? (d as DiagnosticMessageChain) - : d.messageText - ), - Diagnostics.No_overload_matches_this_call - ); - const related = flatMap( - diags, - d => (d as Diagnostic).relatedInformation - ) as DiagnosticRelatedInformation[]; - if (every( - diags, - d => d.start === diags[0].start - && d.length === diags[0].length - && d.file === diags[0].file - )) { - const { file, start, length } = diags[0]; - diagnostics - .add({ file, start, length, code: chain.code, - category: chain.category, - messageText: chain, - relatedInformation: related }); - } else { - diagnostics - .add(createDiagnosticForNodeFromMessageChain( - node, - chain, - related - )); - } - } - } else if (candidateForArgumentArityError) { - diagnostics - .add(getArgumentArityError( - node, - [candidateForArgumentArityError], - args - )); - } else if (candidateForTypeArgumentError) { - checkTypeArguments( - candidateForTypeArgumentError, - (node as CallExpression | TaggedTemplateExpression - | JsxOpeningLikeElement) - .typeArguments!, /*reportErrors*/ - true, - fallbackError - ); - } else { - const signaturesWithCorrectTypeArgumentArity = filter( - signatures, - s => hasCorrectTypeArgumentArity(s, typeArguments) - ); - if (signaturesWithCorrectTypeArgumentArity.length === 0) { - diagnostics - .add(getTypeArgumentArityError( - node, - signatures, - typeArguments! - )); - } else if (!isDecorator) { - diagnostics - .add(getArgumentArityError( - node, - signaturesWithCorrectTypeArgumentArity, - args - )); - } else if (fallbackError) { - diagnostics - .add(getDiagnosticForCallNode( - node, - fallbackError - )); - } - } - } - - return produceDiagnostics || !args - ? resolveErrorCall(node) - : getCandidateForOverloadFailure( - node, - candidates, - args, - !!candidatesOutArray - ); - - function chooseOverload( - candidates: Signature[], - relation: Map, - signatureHelpTrailingComma = false - ) { - candidatesForArgumentError = undefined; - candidateForArgumentArityError = undefined; - candidateForTypeArgumentError = undefined; - - if (isSingleNonGenericCandidate) { - const candidate = candidates[0]; - if (some(typeArguments) - || !hasCorrectArity( - node, - args, - candidate, - signatureHelpTrailingComma - )) - { - return undefined; - } - if (getSignatureApplicabilityError( - node, - args, - candidate, - relation, - CheckMode.Normal, /*reportErrors*/ - false, /*containingMessageChain*/ - undefined - )) { - candidatesForArgumentError = [candidate]; - return undefined; - } - return candidate; - } - - for (let candidateIndex = 0; - candidateIndex < candidates.length; candidateIndex++) - { - const candidate = candidates[candidateIndex]; - if (!hasCorrectTypeArgumentArity(candidate, typeArguments) - || !hasCorrectArity( - node, - args, - candidate, - signatureHelpTrailingComma - )) - { - continue; - } - - let checkCandidate: Signature; - let inferenceContext: InferenceContext | undefined; - - if (candidate.typeParameters) { - let typeArgumentTypes: Type[] | undefined; - if (some(typeArguments)) { - typeArgumentTypes = checkTypeArguments( - candidate, - typeArguments, /*reportErrors*/ - false - ); - if (!typeArgumentTypes) { - candidateForTypeArgumentError = candidate; - continue; - } - } else { - inferenceContext = createInferenceContext( - candidate.typeParameters, - candidate, /*flags*/ - isInJSFile(node) - ? InferenceFlags.AnyDefault - : InferenceFlags.None - ); - typeArgumentTypes = inferTypeArguments( - node, - candidate, - args, - argCheckMode | CheckMode.SkipGenericFunctions, - inferenceContext - ); - argCheckMode |= inferenceContext.flags - & InferenceFlags.SkippedGenericFunction - ? CheckMode.SkipGenericFunctions - : CheckMode.Normal; - } - checkCandidate = getSignatureInstantiation( - candidate, - typeArgumentTypes, - isInJSFile(candidate.declaration), - inferenceContext - && inferenceContext.inferredTypeParameters - ); - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if (getNonArrayRestType(candidate) - && !hasCorrectArity( - node, - args, - checkCandidate, - signatureHelpTrailingComma - )) - { - candidateForArgumentArityError = checkCandidate; - continue; - } - } else { - checkCandidate = candidate; - } - if (getSignatureApplicabilityError( - node, - args, - checkCandidate, - relation, - argCheckMode, /*reportErrors*/ - false, /*containingMessageChain*/ - undefined - )) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - (candidatesForArgumentError - || (candidatesForArgumentError = [])) - .push(checkCandidate); - continue; - } - if (argCheckMode) { - // If one or more context sensitive arguments were excluded, we start including - // them now (and keeping do so for any subsequent candidates) and perform a second - // round of type inference and applicability checking for this particular candidate. - argCheckMode = CheckMode.Normal; - if (inferenceContext) { - const typeArgumentTypes = inferTypeArguments( - node, - candidate, - args, - argCheckMode, - inferenceContext - ); - checkCandidate = getSignatureInstantiation( - candidate, - typeArgumentTypes, - isInJSFile(candidate.declaration), - inferenceContext - && inferenceContext.inferredTypeParameters - ); - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if (getNonArrayRestType(candidate) - && !hasCorrectArity( - node, - args, - checkCandidate, - signatureHelpTrailingComma - )) - { - candidateForArgumentArityError = checkCandidate; - continue; - } - } - if (getSignatureApplicabilityError( - node, - args, - checkCandidate, - relation, - argCheckMode, /*reportErrors*/ - false, /*containingMessageChain*/ - undefined - )) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - (candidatesForArgumentError - || (candidatesForArgumentError = [])) - .push(checkCandidate); - continue; - } - } - candidates[candidateIndex] = checkCandidate; - return checkCandidate; - } - - return undefined; - } - } - - // No signature was applicable. We have already reported the errors for the invalid signature. - // If this is a type resolution session, e.g. Language Service, try to get better information than anySignature. - function getCandidateForOverloadFailure( - node: CallLikeExpression, - candidates: Signature[], - args: readonly Expression[], - hasCandidatesOutArray: boolean - ): Signature { - Debug - .assert(candidates.length - > 0); // Else should not have called this. - // Normally we will combine overloads. Skip this if they have type parameters since that's hard to combine. - // Don't do this if there is a `candidatesOutArray`, - // because then we want the chosen best candidate to be one of the overloads, not a combination. - return hasCandidatesOutArray || candidates.length === 1 - || candidates.some(c => !!c.typeParameters) - ? pickLongestCandidateSignature(node, candidates, args) - : createUnionOfSignaturesForOverloadFailure(candidates); - } - - function createUnionOfSignaturesForOverloadFailure(candidates: - readonly Signature[]): Signature - { - const thisParameters = mapDefined( - candidates, - c => c.thisParameter - ); - let thisParameter: Symbol | undefined; - if (thisParameters.length) { - thisParameter = createCombinedSymbolFromTypes( - thisParameters, - thisParameters.map(getTypeOfParameter) - ); - } - const { min: minArgumentCount, max: maxNonRestParam } = minAndMax( - candidates, - getNumNonRestParameters - ); - const parameters: Symbol[] = []; - for (let i = 0; i < maxNonRestParam; i++) { - const symbols = mapDefined( - candidates, - s => signatureHasRestParameter(s) - ? i < s.parameters.length - 1 - ? s.parameters[i] - : last(s.parameters) - : i < s.parameters.length ? s.parameters[i] : undefined - ); - Debug.assert(symbols.length !== 0); - parameters - .push(createCombinedSymbolFromTypes( - symbols, - mapDefined( - candidates, - candidate => tryGetTypeAtPosition(candidate, i) - ) - )); - } - const restParameterSymbols = mapDefined( - candidates, - c => signatureHasRestParameter(c) - ? last(c.parameters) - : undefined - ); - let flags = SignatureFlags.None; - if (restParameterSymbols.length !== 0) { - const type = - createArrayType(getUnionType( - mapDefined(candidates, tryGetRestTypeOfSignature), - UnionReduction.Subtype - )); - parameters - .push(createCombinedSymbolForOverloadFailure( - restParameterSymbols, - type - )); - flags |= SignatureFlags.HasRestParameter; - } - if (candidates.some(signatureHasLiteralTypes)) { - flags |= SignatureFlags.HasLiteralTypes; - } - return createSignature( - candidates[0].declaration, - /*typeParameters*/ undefined, // Before calling this we tested for `!candidates.some(c => !!c.typeParameters)`. - thisParameter, - parameters, - /*resolvedReturnType*/ getIntersectionType(candidates - .map(getReturnTypeOfSignature)), - /*typePredicate*/ undefined, - minArgumentCount, - flags - ); - } - - function getNumNonRestParameters(signature: Signature): number { - const numParams = signature.parameters.length; - return signatureHasRestParameter(signature) - ? numParams - 1 - : numParams; - } - - function createCombinedSymbolFromTypes( - sources: readonly Symbol[], - types: Type[] - ): Symbol { - return createCombinedSymbolForOverloadFailure( - sources, - getUnionType(types, UnionReduction.Subtype) - ); - } - - function createCombinedSymbolForOverloadFailure( - sources: readonly Symbol[], - type: Type - ): Symbol { - // This function is currently only used for erroneous overloads, so it's good enough to just use the first source. - return createSymbolWithType(first(sources), type); - } - - function pickLongestCandidateSignature( - node: CallLikeExpression, - candidates: Signature[], - args: readonly Expression[] - ): Signature { - // Pick the longest signature. This way we can get a contextual type for cases like: - // declare function f(a: { xa: number; xb: number; }, b: number); - // f({ | - // Also, use explicitly-supplied type arguments if they are provided, so we can get a contextual signature in cases like: - // declare function f(k: keyof T); - // f(" - const bestIndex = getLongestCandidateIndex( - candidates, - apparentArgumentCount === undefined - ? args.length - : apparentArgumentCount - ); - const candidate = candidates[bestIndex]; - const { typeParameters } = candidate; - if (!typeParameters) { - return candidate; - } - - const typeArgumentNodes: readonly TypeNode[] | undefined = - callLikeExpressionMayHaveTypeArguments(node) - ? node.typeArguments - : undefined; - const instantiated = typeArgumentNodes - ? createSignatureInstantiation( - candidate, - getTypeArgumentsFromNodes( - typeArgumentNodes, - typeParameters, - isInJSFile(node) - ) - ) - : inferSignatureInstantiationForOverloadFailure( - node, - typeParameters, - candidate, - args - ); - candidates[bestIndex] = instantiated; - return instantiated; - } - - function getTypeArgumentsFromNodes( - typeArgumentNodes: readonly TypeNode[], - typeParameters: readonly TypeParameter[], - isJs: boolean - ): readonly Type[] { - const typeArguments = typeArgumentNodes.map(getTypeOfNode); - while (typeArguments.length > typeParameters.length) { - typeArguments.pop(); - } - while (typeArguments.length < typeParameters.length) { - typeArguments - .push(getConstraintOfTypeParameter(typeParameters - [typeArguments.length]) - || getDefaultTypeArgumentType(isJs)); - } - return typeArguments; - } - - function inferSignatureInstantiationForOverloadFailure( - node: CallLikeExpression, - typeParameters: readonly TypeParameter[], - candidate: Signature, - args: readonly Expression[] - ): Signature { - const inferenceContext = createInferenceContext( - typeParameters, - candidate, /*flags*/ - isInJSFile(node) - ? InferenceFlags.AnyDefault - : InferenceFlags.None - ); - const typeArgumentTypes = inferTypeArguments( - node, - candidate, - args, - CheckMode.SkipContextSensitive - | CheckMode.SkipGenericFunctions, - inferenceContext - ); - return createSignatureInstantiation(candidate, typeArgumentTypes); - } - - function getLongestCandidateIndex( - candidates: Signature[], - argsCount: number - ): number { - let maxParamsIndex = -1; - let maxParams = -1; - - for (let i = 0; i < candidates.length; i++) { - const candidate = candidates[i]; - const paramCount = getParameterCount(candidate); - if (hasEffectiveRestParameter(candidate) - || paramCount >= argsCount) - { - return i; - } - if (paramCount > maxParams) { - maxParams = paramCount; - maxParamsIndex = i; - } - } - - return maxParamsIndex; - } - - function resolveCallExpression( - node: CallExpression, - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode - ): Signature { - if (node.expression.kind === SyntaxKind.SuperKeyword) { - const superType = checkSuperExpression(node.expression); - if (isTypeAny(superType)) { - for (const arg of node.arguments) { - checkExpression(arg); // Still visit arguments so they get marked for visibility, etc - } - return anySignature; - } - if (superType !== errorType) { - // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated - // with the type arguments specified in the extends clause. - const baseTypeNode = - getEffectiveBaseTypeNode(getContainingClass(node)!); - if (baseTypeNode) { - const baseConstructors = - getInstantiatedConstructorsForTypeArguments( - superType, - baseTypeNode.typeArguments, - baseTypeNode - ); - return resolveCall( - node, - baseConstructors, - candidatesOutArray, - checkMode, - SignatureFlags.None - ); - } - } - return resolveUntypedCall(node); - } - - let callChainFlags: SignatureFlags; - let funcType = checkExpression(node.expression); - if (isCallChain(node)) { - const nonOptionalType = getOptionalExpressionType( - funcType, - node.expression - ); - callChainFlags = nonOptionalType === funcType - ? SignatureFlags.None - : isOutermostOptionalChain(node) - ? SignatureFlags.IsOuterCallChain - : SignatureFlags.IsInnerCallChain; - funcType = nonOptionalType; - } else { - callChainFlags = SignatureFlags.None; - } - - funcType = checkNonNullTypeWithReporter( - funcType, - node.expression, - reportCannotInvokePossiblyNullOrUndefinedError - ); - - if (funcType === silentNeverType) { - return silentNeverSignature; - } - - const apparentType = getApparentType(funcType); - if (apparentType === errorType) { - // Another error has already been reported - return resolveErrorCall(node); - } - - // Technically, this signatures list may be incomplete. We are taking the apparent type, - // but we are not including call signatures that may have been added to the Object or - // Function interface, since they have none by default. This is a bit of a leap of faith - // that the user will not add any. - const callSignatures = getSignaturesOfType( - apparentType, - SignatureKind.Call - ); - const numConstructSignatures = getSignaturesOfType( - apparentType, - SignatureKind.Construct - ).length; - - // TS 1.0 Spec: 4.12 - // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual - // types are provided for the argument expressions, and the result is always of type Any. - if (isUntypedFunctionCall( - funcType, - apparentType, - callSignatures.length, - numConstructSignatures - )) { - // The unknownType indicates that an error already occurred (and was reported). No - // need to report another error in this case. - if (funcType !== errorType && node.typeArguments) { - error( - node, - Diagnostics - .Untyped_function_calls_may_not_accept_type_arguments - ); - } - return resolveUntypedCall(node); - } - // If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call. - // TypeScript employs overload resolution in typed function calls in order to support functions - // with multiple call signatures. - if (!callSignatures.length) { - if (numConstructSignatures) { - error( - node, - Diagnostics - .Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, - typeToString(funcType) - ); - } else { - let relatedInformation: DiagnosticRelatedInformation - | undefined; - if (node.arguments.length === 1) { - const text = getSourceFileOfNode(node).text; - if (isLineBreak(text - .charCodeAt(skipTrivia( - text, - node.expression.end, /* stopAfterLineBreak */ - true - ) - 1))) - { - relatedInformation = createDiagnosticForNode( - node.expression, - Diagnostics - .It_is_highly_likely_that_you_are_missing_a_semicolon - ); - } - } - invocationError( - node.expression, - apparentType, - SignatureKind.Call, - relatedInformation - ); - } - return resolveErrorCall(node); - } - // When a call to a generic function is an argument to an outer call to a generic function for which - // inference is in process, we have a choice to make. If the inner call relies on inferences made from - // its contextual type to its return type, deferring the inner call processing allows the best possible - // contextual type to accumulate. But if the outer call relies on inferences made from the return type of - // the inner call, the inner call should be processed early. There's no sure way to know which choice is - // right (only a full unification algorithm can determine that), so we resort to the following heuristic: - // If no type arguments are specified in the inner call and at least one call signature is generic and - // returns a function type, we choose to defer processing. This narrowly permits function composition - // operators to flow inferences through return types, but otherwise processes calls right away. We - // use the resolvingSignature singleton to indicate that we deferred processing. This result will be - // propagated out and eventually turned into nonInferrableType (a type that is assignable to anything and - // from which we never make inferences). - if (checkMode & CheckMode.SkipGenericFunctions - && !node.typeArguments - && callSignatures.some(isGenericFunctionReturningFunction)) - { - skippedGenericFunction(node, checkMode); - return resolvingSignature; - } - // If the function is explicitly marked with `@class`, then it must be constructed. - if (callSignatures - .some(sig => isInJSFile(sig.declaration) - && !!getJSDocClassTag(sig.declaration!))) - { - error( - node, - Diagnostics - .Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, - typeToString(funcType) - ); - return resolveErrorCall(node); - } - - return resolveCall( - node, - callSignatures, - candidatesOutArray, - checkMode, - callChainFlags - ); - } - - function isGenericFunctionReturningFunction(signature: Signature) { - return !!(signature.typeParameters - && isFunctionType(getReturnTypeOfSignature(signature))); - } - - /** - * TS 1.0 spec: 4.12 - * If FuncExpr is of type Any, or of an object type that has no call or construct signatures - * but is a subtype of the Function interface, the call is an untyped function call. - */ - function isUntypedFunctionCall( - funcType: Type, - apparentFuncType: Type, - numCallSignatures: number, - numConstructSignatures: number - ): boolean { - // We exclude union types because we may have a union of function types that happen to have no common signatures. - return isTypeAny(funcType) || isTypeAny(apparentFuncType) - && !!(funcType.flags & TypeFlags.TypeParameter) - || !numCallSignatures && !numConstructSignatures - && !(apparentFuncType.flags - & (TypeFlags.Union | TypeFlags.Never)) - && isTypeAssignableTo(funcType, globalFunctionType); - } - - function resolveNewExpression( - node: NewExpression, - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode - ): Signature { - if (node.arguments && languageVersion < ScriptTarget.ES5) { - const spreadIndex = getSpreadArgumentIndex(node.arguments); - if (spreadIndex >= 0) { - error( - node.arguments[spreadIndex], - Diagnostics - .Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher - ); - } - } - - let expressionType = checkNonNullExpression(node.expression); - if (expressionType === silentNeverType) { - return silentNeverSignature; - } - - // If expressionType's apparent type(section 3.8.1) is an object type with one or - // more construct signatures, the expression is processed in the same manner as a - // function call, but using the construct signatures as the initial set of candidate - // signatures for overload resolution. The result type of the function call becomes - // the result type of the operation. - expressionType = getApparentType(expressionType); - if (expressionType === errorType) { - // Another error has already been reported - return resolveErrorCall(node); - } - - // TS 1.0 spec: 4.11 - // If expressionType is of type Any, Args can be any argument - // list and the result of the operation is of type Any. - if (isTypeAny(expressionType)) { - if (node.typeArguments) { - error( - node, - Diagnostics - .Untyped_function_calls_may_not_accept_type_arguments - ); - } - return resolveUntypedCall(node); - } - - // Technically, this signatures list may be incomplete. We are taking the apparent type, - // but we are not including construct signatures that may have been added to the Object or - // Function interface, since they have none by default. This is a bit of a leap of faith - // that the user will not add any. - const constructSignatures = getSignaturesOfType( - expressionType, - SignatureKind.Construct - ); - if (constructSignatures.length) { - if (!isConstructorAccessible(node, constructSignatures[0])) { - return resolveErrorCall(node); - } - // If the expression is a class of abstract type, then it cannot be instantiated. - // Note, only class declarations can be declared abstract. - // In the case of a merged class-module or class-interface declaration, - // only the class declaration node will have the Abstract flag set. - const valueDecl = expressionType.symbol - && getClassLikeDeclarationOfSymbol(expressionType.symbol); - if (valueDecl - && hasModifier(valueDecl, ModifierFlags.Abstract)) - { - error( - node, - Diagnostics - .Cannot_create_an_instance_of_an_abstract_class - ); - return resolveErrorCall(node); - } - - return resolveCall( - node, - constructSignatures, - candidatesOutArray, - checkMode, - SignatureFlags.None - ); - } - - // If expressionType's apparent type is an object type with no construct signatures but - // one or more call signatures, the expression is processed as a function call. A compile-time - // error occurs if the result of the function call is not Void. The type of the result of the - // operation is Any. It is an error to have a Void this type. - const callSignatures = getSignaturesOfType( - expressionType, - SignatureKind.Call - ); - if (callSignatures.length) { - const signature = resolveCall( - node, - callSignatures, - candidatesOutArray, - checkMode, - SignatureFlags.None - ); - if (!noImplicitAny) { - if (signature.declaration - && !isJSConstructor(signature.declaration) - && getReturnTypeOfSignature(signature) !== voidType) - { - error( - node, - Diagnostics - .Only_a_void_function_can_be_called_with_the_new_keyword - ); - } - if (getThisTypeOfSignature(signature) === voidType) { - error( - node, - Diagnostics - .A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void - ); - } - } - return signature; - } - - invocationError( - node.expression, - expressionType, - SignatureKind.Construct - ); - return resolveErrorCall(node); - } - - function typeHasProtectedAccessibleBase( - target: Symbol, - type: InterfaceType - ): boolean { - const baseTypes = getBaseTypes(type); - if (!length(baseTypes)) { - return false; - } - const firstBase = baseTypes[0]; - if (firstBase.flags & TypeFlags.Intersection) { - const types = (firstBase as IntersectionType).types; - const mixinFlags = findMixins(types); - let i = 0; - for (const intersectionMember - of (firstBase as IntersectionType).types) - { - // We want to ignore mixin ctors - if (!mixinFlags[i]) { - if (getObjectFlags(intersectionMember) - & (ObjectFlags.Class | ObjectFlags.Interface)) - { - if (intersectionMember.symbol === target) { - return true; - } - if (typeHasProtectedAccessibleBase( - target, - intersectionMember as InterfaceType - )) { - return true; - } - } - } - i++; - } - return false; - } - if (firstBase.symbol === target) { - return true; - } - return typeHasProtectedAccessibleBase( - target, - firstBase as InterfaceType - ); - } - - function isConstructorAccessible( - node: NewExpression, - signature: Signature - ) { - if (!signature || !signature.declaration) { - return true; - } - - const declaration = signature.declaration; - const modifiers = getSelectedModifierFlags( - declaration, - ModifierFlags.NonPublicAccessibilityModifier - ); - - // (1) Public constructors and (2) constructor functions are always accessible. - if (!modifiers || declaration.kind !== SyntaxKind.Constructor) { - return true; - } - - const declaringClassDeclaration = - getClassLikeDeclarationOfSymbol(declaration.parent.symbol)!; - const declaringClass = - getDeclaredTypeOfSymbol(declaration.parent - .symbol); - - // A private or protected constructor can only be instantiated within its own class (or a subclass, for protected) - if (!isNodeWithinClass(node, declaringClassDeclaration)) { - const containingClass = getContainingClass(node); - if (containingClass && modifiers & ModifierFlags.Protected) { - const containingType = getTypeOfNode(containingClass); - if (typeHasProtectedAccessibleBase( - declaration.parent.symbol, - containingType as InterfaceType - )) { - return true; - } - } - if (modifiers & ModifierFlags.Private) { - error( - node, - Diagnostics - .Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, - typeToString(declaringClass) - ); - } - if (modifiers & ModifierFlags.Protected) { - error( - node, - Diagnostics - .Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, - typeToString(declaringClass) - ); - } - return false; - } - - return true; - } - - function invocationErrorDetails( - apparentType: Type, - kind: SignatureKind - ): { messageChain: DiagnosticMessageChain; - relatedMessage: DiagnosticMessage | undefined; } - { - let errorInfo: DiagnosticMessageChain | undefined; - const isCall = kind === SignatureKind.Call; - const awaitedType = getAwaitedType(apparentType); - const maybeMissingAwait = awaitedType - && getSignaturesOfType(awaitedType, kind).length > 0; - if (apparentType.flags & TypeFlags.Union) { - const types = (apparentType as UnionType).types; - let hasSignatures = false; - for (const constituent of types) { - const signatures = getSignaturesOfType(constituent, kind); - if (signatures.length !== 0) { - hasSignatures = true; - if (errorInfo) { - // Bail early if we already have an error, no chance of "No constituent of type is callable" - break; - } - } else { - // Error on the first non callable constituent only - if (!errorInfo) { - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall - ? Diagnostics.Type_0_has_no_call_signatures - : Diagnostics - .Type_0_has_no_construct_signatures, - typeToString(constituent) - ); - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall - ? Diagnostics - .Not_all_constituents_of_type_0_are_callable - : Diagnostics - .Not_all_constituents_of_type_0_are_constructable, - typeToString(apparentType) - ); - } - if (hasSignatures) { - // Bail early if we already found a siganture, no chance of "No constituent of type is callable" - break; - } - } - } - if (!hasSignatures) { - errorInfo = chainDiagnosticMessages( - /* detials */ undefined, - isCall - ? Diagnostics.No_constituent_of_type_0_is_callable - : Diagnostics - .No_constituent_of_type_0_is_constructable, - typeToString(apparentType) - ); - } - if (!errorInfo) { - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall - ? Diagnostics - .Each_member_of_the_union_type_0_has_signatures_but_none_of_those_signatures_are_compatible_with_each_other - : Diagnostics - .Each_member_of_the_union_type_0_has_construct_signatures_but_none_of_those_signatures_are_compatible_with_each_other, - typeToString(apparentType) - ); - } - } else { - errorInfo = chainDiagnosticMessages( - errorInfo, - isCall - ? Diagnostics.Type_0_has_no_call_signatures - : Diagnostics.Type_0_has_no_construct_signatures, - typeToString(apparentType) - ); - } - return { - messageChain: chainDiagnosticMessages( - errorInfo, - isCall - ? Diagnostics.This_expression_is_not_callable - : Diagnostics.This_expression_is_not_constructable - ), - relatedMessage: maybeMissingAwait - ? Diagnostics.Did_you_forget_to_use_await - : undefined - }; - } - function invocationError( - errorTarget: Node, - apparentType: Type, - kind: SignatureKind, - relatedInformation?: DiagnosticRelatedInformation - ) { - const { messageChain, relatedMessage: relatedInfo } = - invocationErrorDetails(apparentType, kind); - const diagnostic = createDiagnosticForNodeFromMessageChain( - errorTarget, - messageChain - ); - if (relatedInfo) { - addRelatedInfo( - diagnostic, - createDiagnosticForNode(errorTarget, relatedInfo) - ); - } - if (isCallExpression(errorTarget.parent)) { - const { start, length } = getDiagnosticSpanForCallNode( - errorTarget.parent, /* doNotIncludeArguments */ - true - ); - diagnostic.start = start; - diagnostic.length = length; - } - diagnostics.add(diagnostic); - invocationErrorRecovery( - apparentType, - kind, - relatedInformation - ? addRelatedInfo(diagnostic, relatedInformation) - : diagnostic - ); - } - - function invocationErrorRecovery( - apparentType: Type, - kind: SignatureKind, - diagnostic: Diagnostic - ) { - if (!apparentType.symbol) { - return; - } - const importNode = getSymbolLinks(apparentType.symbol) - .originatingImport; - // Create a diagnostic on the originating import if possible onto which we can attach a quickfix - // An import call expression cannot be rewritten into another form to correct the error - the only solution is to use `.default` at the use-site - if (importNode && !isImportCall(importNode)) { - const sigs = getSignaturesOfType( - getTypeOfSymbol(getSymbolLinks(apparentType.symbol) - .target!), - kind - ); - if (!sigs || !sigs.length) return; - - addRelatedInfo( - diagnostic, - createDiagnosticForNode( - importNode, - Diagnostics - .Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead - ) - ); - } - } - - function resolveTaggedTemplateExpression( - node: TaggedTemplateExpression, - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode - ): Signature { - const tagType = checkExpression(node.tag); - const apparentType = getApparentType(tagType); - - if (apparentType === errorType) { - // Another error has already been reported - return resolveErrorCall(node); - } - - const callSignatures = getSignaturesOfType( - apparentType, - SignatureKind.Call - ); - const numConstructSignatures = getSignaturesOfType( - apparentType, - SignatureKind.Construct - ).length; - - if (isUntypedFunctionCall( - tagType, - apparentType, - callSignatures.length, - numConstructSignatures - )) { - return resolveUntypedCall(node); - } - - if (!callSignatures.length) { - invocationError(node.tag, apparentType, SignatureKind.Call); - return resolveErrorCall(node); - } - - return resolveCall( - node, - callSignatures, - candidatesOutArray, - checkMode, - SignatureFlags.None - ); - } - - /** - * Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression. - */ - function getDiagnosticHeadMessageForDecoratorResolution(node: - Decorator) - { - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - return Diagnostics - .Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression; - - case SyntaxKind.Parameter: - return Diagnostics - .Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression; - - case SyntaxKind.PropertyDeclaration: - return Diagnostics - .Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return Diagnostics - .Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression; - - default: - return Debug.fail(); - } - } - - /** - * Resolves a decorator as if it were a call expression. - */ - function resolveDecorator( - node: Decorator, - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode - ): Signature { - const funcType = checkExpression(node.expression); - const apparentType = getApparentType(funcType); - if (apparentType === errorType) { - return resolveErrorCall(node); - } - - const callSignatures = getSignaturesOfType( - apparentType, - SignatureKind.Call - ); - const numConstructSignatures = getSignaturesOfType( - apparentType, - SignatureKind.Construct - ).length; - if (isUntypedFunctionCall( - funcType, - apparentType, - callSignatures.length, - numConstructSignatures - )) { - return resolveUntypedCall(node); - } - - if (isPotentiallyUncalledDecorator(node, callSignatures)) { - const nodeStr = getTextOfNode( - node.expression, /*includeTrivia*/ - false - ); - error( - node, - Diagnostics - ._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0, - nodeStr - ); - return resolveErrorCall(node); - } - - const headMessage = - getDiagnosticHeadMessageForDecoratorResolution(node); - if (!callSignatures.length) { - const errorDetails = invocationErrorDetails( - apparentType, - SignatureKind.Call - ); - const messageChain = chainDiagnosticMessages( - errorDetails.messageChain, - headMessage - ); - const diag = createDiagnosticForNodeFromMessageChain( - node.expression, - messageChain - ); - if (errorDetails.relatedMessage) { - addRelatedInfo( - diag, - createDiagnosticForNode( - node.expression, - errorDetails.relatedMessage - ) - ); - } - diagnostics.add(diag); - invocationErrorRecovery( - apparentType, - SignatureKind.Call, - diag - ); - return resolveErrorCall(node); - } - - return resolveCall( - node, - callSignatures, - candidatesOutArray, - checkMode, - SignatureFlags.None, - headMessage - ); - } - - function createSignatureForJSXIntrinsic( - node: JsxOpeningLikeElement, - result: Type - ): Signature { - const namespace = getJsxNamespaceAt(node); - const exports = namespace && getExportsOfSymbol(namespace); - // We fake up a SFC signature for each intrinsic, however a more specific per-element signature drawn from the JSX declaration - // file would probably be preferable. - const typeSymbol = exports - && getSymbol(exports, JsxNames.Element, SymbolFlags.Type); - const returnNode = typeSymbol - && nodeBuilder.symbolToEntityName( - typeSymbol, - SymbolFlags.Type, - node - ); - const declaration = createFunctionTypeNode( - /*typeParameters*/ undefined, - [createParameter( - /*decorators*/ undefined, /*modifiers*/ - undefined, /*dotdotdot*/ - undefined, - 'props', /*questionMark*/ - undefined, - nodeBuilder.typeToTypeNode(result, node) - )], - returnNode - ? createTypeReferenceNode( - returnNode, /*typeArguments*/ - undefined - ) - : createKeywordTypeNode(SyntaxKind.AnyKeyword) - ); - const parameterSymbol = createSymbol( - SymbolFlags.FunctionScopedVariable, - 'props' as __String - ); - parameterSymbol.type = result; - return createSignature( - declaration, - /*typeParameters*/ undefined, - /*thisParameter*/ undefined, - [parameterSymbol], - typeSymbol ? getDeclaredTypeOfSymbol(typeSymbol) : errorType, - /*returnTypePredicate*/ undefined, - 1, - SignatureFlags.None - ); - } - - function resolveJsxOpeningLikeElement( - node: JsxOpeningLikeElement, - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode - ): Signature { - if (isJsxIntrinsicIdentifier(node.tagName)) { - const result = - getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); - const fakeSignature = createSignatureForJSXIntrinsic( - node, - result - ); - checkTypeAssignableToAndOptionallyElaborate( - checkExpressionWithContextualType( - node.attributes, - getEffectiveFirstArgumentForJsxSignature( - fakeSignature, - node - ), /*mapper*/ - undefined, - CheckMode.Normal - ), - result, - node.tagName, - node.attributes - ); - return fakeSignature; - } - const exprTypes = checkExpression(node.tagName); - const apparentType = getApparentType(exprTypes); - if (apparentType === errorType) { - return resolveErrorCall(node); - } - - const signatures = getUninstantiatedJsxSignaturesOfType( - exprTypes, - node - ); - if (isUntypedFunctionCall( - exprTypes, - apparentType, - signatures.length, /*constructSignatures*/ - 0 - )) { - return resolveUntypedCall(node); - } - - if (signatures.length === 0) { - // We found no signatures at all, which is an error - error( - node.tagName, - Diagnostics - .JSX_element_type_0_does_not_have_any_construct_or_call_signatures, - getTextOfNode(node.tagName) - ); - return resolveErrorCall(node); - } - - return resolveCall( - node, - signatures, - candidatesOutArray, - checkMode, - SignatureFlags.None - ); - } - - /** - * Sometimes, we have a decorator that could accept zero arguments, - * but is receiving too many arguments as part of the decorator invocation. - * In those cases, a user may have meant to *call* the expression before using it as a decorator. - */ - function isPotentiallyUncalledDecorator( - decorator: Decorator, - signatures: readonly Signature[] - ) { - return signatures.length - && every( - signatures, - signature => signature.minArgumentCount === 0 - && !signatureHasRestParameter(signature) - && signature.parameters.length - < getDecoratorArgumentCount(decorator, signature) - ); - } - - function resolveSignature( - node: CallLikeExpression, - candidatesOutArray: Signature[] | undefined, - checkMode: CheckMode - ): Signature { - switch (node.kind) { - case SyntaxKind.CallExpression: - return resolveCallExpression( - node, - candidatesOutArray, - checkMode - ); - case SyntaxKind.NewExpression: - return resolveNewExpression( - node, - candidatesOutArray, - checkMode - ); - case SyntaxKind.TaggedTemplateExpression: - return resolveTaggedTemplateExpression( - node, - candidatesOutArray, - checkMode - ); - case SyntaxKind.Decorator: - return resolveDecorator( - node, - candidatesOutArray, - checkMode - ); - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSelfClosingElement: - return resolveJsxOpeningLikeElement( - node, - candidatesOutArray, - checkMode - ); - } - throw Debug.assertNever( - node, - 'Branch in \'resolveSignature\' should be unreachable.' - ); - } - - /** - * Resolve a signature of a given call-like expression. - * @param node a call-like expression to try resolve a signature for - * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service; - * the function will fill it up with appropriate candidate signatures - * @return a signature of the call-like expression or undefined if one can't be found - */ - function getResolvedSignature( - node: CallLikeExpression, - candidatesOutArray?: Signature[] | undefined, - checkMode?: CheckMode - ): Signature { - const links = getNodeLinks(node); - // If getResolvedSignature has already been called, we will have cached the resolvedSignature. - // However, it is possible that either candidatesOutArray was not passed in the first time, - // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work - // to correctly fill the candidatesOutArray. - const cached = links.resolvedSignature; - if (cached && cached !== resolvingSignature - && !candidatesOutArray) - { - return cached; - } - links.resolvedSignature = resolvingSignature; - const result = resolveSignature( - node, - candidatesOutArray, - checkMode || CheckMode.Normal - ); - // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call - // resolution should be deferred. - if (result !== resolvingSignature) { - // If signature resolution originated in control flow type analysis (for example to compute the - // assigned type in a flow assignment) we don't cache the result as it may be based on temporary - // types from the control flow analysis. - links.resolvedSignature = flowLoopStart === flowLoopCount - ? result - : cached; - } - return result; - } - - /** - * Indicates whether a declaration can be treated as a constructor in a JavaScript - * file. - */ - function isJSConstructor(node: Node - | undefined): node is FunctionDeclaration | FunctionExpression - { - if (!node || !isInJSFile(node)) { - return false; - } - const func = - isFunctionDeclaration(node) || isFunctionExpression(node) - ? node - : isVariableDeclaration(node) && node.initializer - && isFunctionExpression(node.initializer) - ? node.initializer - : undefined; - if (func) { - // If the node has a @class tag, treat it like a constructor. - if (getJSDocClassTag(node)) return true; - - // If the symbol of the node has members, treat it like a constructor. - const symbol = getSymbolOfNode(func); - return !!symbol && hasEntries(symbol.members); - } - return false; - } - - function mergeJSSymbols(target: Symbol, source: Symbol | undefined) { - if (source) { - const links = getSymbolLinks(source); - if (!links.inferredClassSymbol - || !links.inferredClassSymbol - .has('' + getSymbolId(target))) - { - const inferred = isTransientSymbol(target) - ? target - : cloneSymbol(target) as TransientSymbol; - inferred.exports = inferred.exports || createSymbolTable(); - inferred.members = inferred.members || createSymbolTable(); - inferred.flags |= source.flags & SymbolFlags.Class; - if (hasEntries(source.exports)) { - mergeSymbolTable(inferred.exports, source.exports); - } - if (hasEntries(source.members)) { - mergeSymbolTable(inferred.members, source.members); - } - (links.inferredClassSymbol - || (links - .inferredClassSymbol = createMap())) - .set('' + getSymbolId(inferred), inferred); - return inferred; - } - return links.inferredClassSymbol.get('' + getSymbolId(target)); - } - } - - function getAssignedClassSymbol(decl: Declaration): Symbol - | undefined - { - const assignmentSymbol = decl && decl.parent - && (isFunctionDeclaration(decl) && getSymbolOfNode(decl) - || isBinaryExpression(decl.parent) - && getSymbolOfNode(decl.parent.left) - || isVariableDeclaration(decl.parent) - && getSymbolOfNode(decl.parent)); - const prototype = assignmentSymbol && assignmentSymbol.exports - && assignmentSymbol.exports.get('prototype' as __String); - const init = prototype && prototype.valueDeclaration - && getAssignedJSPrototype(prototype.valueDeclaration); - return init ? getSymbolOfNode(init) : undefined; - } - - function getAssignedJSPrototype(node: Node) { - if (!node.parent) { - return false; - } - let parent: Node = node.parent; - while (parent - && parent.kind === SyntaxKind.PropertyAccessExpression) - { - parent = parent.parent; - } - if (parent && isBinaryExpression(parent) - && isPrototypeAccess(parent.left) - && parent.operatorToken.kind === SyntaxKind.EqualsToken) - { - const right = getInitializerOfBinaryExpression(parent); - return isObjectLiteralExpression(right) && right; - } - } - - /** - * Syntactically and semantically checks a call or new expression. - * @param node The call/new expression to be checked. - * @returns On success, the expression's signature's return type. On failure, anyType. - */ - function checkCallExpression( - node: CallExpression | NewExpression, - checkMode?: CheckMode - ): Type { - if (!checkGrammarTypeArguments( - node, - node.typeArguments - )) - checkGrammarArguments(node.arguments); - - const signature = getResolvedSignature( - node, /*candidatesOutArray*/ - undefined, - checkMode - ); - if (signature === resolvingSignature) { - // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that - // returns a function type. We defer checking and return nonInferrableType. - return nonInferrableType; - } - - if (node.expression.kind === SyntaxKind.SuperKeyword) { - return voidType; - } - - if (node.kind === SyntaxKind.NewExpression) { - const declaration = signature.declaration; - - if (declaration - && declaration.kind !== SyntaxKind.Constructor - && declaration.kind !== SyntaxKind.ConstructSignature - && declaration.kind !== SyntaxKind.ConstructorType - && !isJSDocConstructSignature(declaration) - && !isJSConstructor(declaration)) - { - // When resolved signature is a call signature (and not a construct signature) the result type is any - if (noImplicitAny) { - error( - node, - Diagnostics - .new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type - ); - } - return anyType; - } - } - - // In JavaScript files, calls to any identifier 'require' are treated as external module imports - if (isInJSFile(node) && isCommonJsRequire(node)) { - return resolveExternalModuleTypeByLiteral(node.arguments! - [0] as StringLiteral); - } - - const returnType = getReturnTypeOfSignature(signature); - // Treat any call to the global 'Symbol' function that is part of a const variable or readonly property - // as a fresh unique symbol literal type. - if (returnType.flags & TypeFlags.ESSymbolLike - && isSymbolOrSymbolForCall(node)) - { - return getESSymbolLikeTypeForNode(walkUpParenthesizedExpressions(node - .parent)); - } - if (node.kind === SyntaxKind.CallExpression - && node.parent.kind === SyntaxKind.ExpressionStatement - && returnType.flags & TypeFlags.Void - && getTypePredicateOfSignature(signature)) - { - if (!isDottedName(node.expression)) { - error( - node.expression, - Diagnostics - .Assertions_require_the_call_target_to_be_an_identifier_or_qualified_name - ); - } else if (!getEffectsSignature(node)) { - const diagnostic = error( - node.expression, - Diagnostics - .Assertions_require_every_name_in_the_call_target_to_be_declared_with_an_explicit_type_annotation - ); - getTypeOfDottedName(node.expression, diagnostic); - } - } - - if (isInJSFile(node)) { - const decl = getDeclarationOfExpando(node); - if (decl) { - const jsSymbol = getSymbolOfNode(decl); - if (jsSymbol && hasEntries(jsSymbol.exports)) { - const jsAssignmentType = createAnonymousType( - jsSymbol, - jsSymbol.exports, - emptyArray, - emptyArray, - undefined, - undefined - ); - jsAssignmentType.objectFlags |= ObjectFlags.JSLiteral; - return getIntersectionType([returnType, - jsAssignmentType]); - } - } - } - - return returnType; - } - - function isSymbolOrSymbolForCall(node: Node) { - if (!isCallExpression(node)) return false; - let left = node.expression; - if (isPropertyAccessExpression(left) - && left.name.escapedText === 'for') - { - left = left.expression; - } - if (!isIdentifier(left) || left.escapedText !== 'Symbol') { - return false; - } - - // make sure `Symbol` is the global symbol - const globalESSymbol = - getGlobalESSymbolConstructorSymbol(/*reportErrors*/ false); - if (!globalESSymbol) { - return false; - } - - return globalESSymbol - === resolveName( - left, - 'Symbol' as __String, - SymbolFlags.Value, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - ); - } - - function checkImportCallExpression(node: ImportCall): Type { - // Check grammar of dynamic import - if (!checkGrammarArguments(node - .arguments)) - checkGrammarImportCallExpression(node); - - if (node.arguments.length === 0) { - return createPromiseReturnType(node, anyType); - } - const specifier = node.arguments[0]; - const specifierType = checkExpressionCached(specifier); - // Even though multiple arguments is grammatically incorrect, type-check extra arguments for completion - for (let i = 1; i < node.arguments.length; ++i) { - checkExpressionCached(node.arguments[i]); - } - - if (specifierType.flags & TypeFlags.Undefined - || specifierType.flags & TypeFlags.Null - || !isTypeAssignableTo(specifierType, stringType)) - { - error( - specifier, - Diagnostics - .Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, - typeToString(specifierType) - ); - } - - // resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal - const moduleSymbol = resolveExternalModuleName(node, specifier); - if (moduleSymbol) { - const esModuleSymbol = resolveESModuleSymbol( - moduleSymbol, - specifier, /*dontRecursivelyResolve*/ - true, /*suppressUsageError*/ - false - ); - if (esModuleSymbol) { - return createPromiseReturnType( - node, - getTypeWithSyntheticDefaultImportType( - getTypeOfSymbol(esModuleSymbol), - esModuleSymbol, - moduleSymbol - ) - ); - } - } - return createPromiseReturnType(node, anyType); - } - - function getTypeWithSyntheticDefaultImportType( - type: Type, - symbol: Symbol, - originalSymbol: Symbol - ): Type { - if (allowSyntheticDefaultImports && type && type !== errorType) { - const synthType = type as SyntheticDefaultModuleType; - if (!synthType.syntheticType) { - const file = find( - originalSymbol.declarations, - isSourceFile - ); - const hasSyntheticDefault = canHaveSyntheticDefault( - file, - originalSymbol, /*dontResolveAlias*/ - false - ); - if (hasSyntheticDefault) { - const memberTable = createSymbolTable(); - const newSymbol = createSymbol( - SymbolFlags.Alias, - InternalSymbolName.Default - ); - newSymbol.nameType = getLiteralType('default'); - newSymbol.target = resolveSymbol(symbol); - memberTable.set(InternalSymbolName.Default, newSymbol); - const anonymousSymbol = createSymbol( - SymbolFlags.TypeLiteral, - InternalSymbolName.Type - ); - const defaultContainingObject = createAnonymousType( - anonymousSymbol, - memberTable, - emptyArray, - emptyArray, /*stringIndexInfo*/ - undefined, /*numberIndexInfo*/ - undefined - ); - anonymousSymbol.type = defaultContainingObject; - synthType.syntheticType = isValidSpreadType(type) - ? getSpreadType( - type, - defaultContainingObject, - anonymousSymbol, /*objectFlags*/ - 0, /*readonly*/ - false - ) - : defaultContainingObject; - } else { - synthType.syntheticType = type; - } - } - return synthType.syntheticType; - } - return type; - } - - function isCommonJsRequire(node: Node): boolean { - if (!isRequireCall( - node, /*checkArgumentIsStringLiteralLike*/ - true - )) { - return false; - } - - // Make sure require is not a local function - if (!isIdentifier(node.expression)) return Debug.fail(); - const resolvedRequire = resolveName( - node.expression, - node.expression.escapedText, - SymbolFlags.Value, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - true - )!; // TODO: GH#18217 - if (resolvedRequire === requireSymbol) { - return true; - } - // project includes symbol named 'require' - make sure that it is ambient and local non-alias - if (resolvedRequire.flags & SymbolFlags.Alias) { - return false; - } - - const targetDeclarationKind = - resolvedRequire.flags & SymbolFlags.Function - ? SyntaxKind.FunctionDeclaration - : resolvedRequire.flags & SymbolFlags.Variable - ? SyntaxKind.VariableDeclaration - : SyntaxKind.Unknown; - if (targetDeclarationKind !== SyntaxKind.Unknown) { - const decl = getDeclarationOfKind( - resolvedRequire, - targetDeclarationKind - )!; - // function/variable declaration should be ambient - return !!decl && !!(decl.flags & NodeFlags.Ambient); - } - return false; - } - - function checkTaggedTemplateExpression(node: - TaggedTemplateExpression): Type - { - if (!checkGrammarTaggedTemplateChain(node)) { - checkGrammarTypeArguments( - node, - node.typeArguments - ); - } - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.MakeTemplateObject - ); - } - return getReturnTypeOfSignature(getResolvedSignature(node)); - } - - function checkAssertion(node: AssertionExpression) { - return checkAssertionWorker(node, node.type, node.expression); - } - - function isValidConstAssertionArgument(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - return true; - case SyntaxKind.ParenthesizedExpression: - return isValidConstAssertionArgument(( node) - .expression); - case SyntaxKind.PrefixUnaryExpression: - const op = ( node).operator; - const arg = ( node).operand; - return op === SyntaxKind.MinusToken - && (arg.kind === SyntaxKind.NumericLiteral - || arg.kind === SyntaxKind.BigIntLiteral) - || op === SyntaxKind.PlusToken - && arg.kind === SyntaxKind.NumericLiteral; - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - const expr = - ( node).expression; - if (isIdentifier(expr)) { - let symbol = getSymbolAtLocation(expr); - if (symbol && symbol.flags & SymbolFlags.Alias) { - symbol = resolveAlias(symbol); - } - return !!(symbol && (symbol.flags & SymbolFlags.Enum) - && getEnumKind(symbol) === EnumKind.Literal); - } - } - return false; - } - - function checkAssertionWorker( - errNode: Node, - type: TypeNode, - expression: UnaryExpression | Expression, - checkMode?: CheckMode - ) { - let exprType = checkExpression(expression, checkMode); - if (isConstTypeReference(type)) { - if (!isValidConstAssertionArgument(expression)) { - error( - expression, - Diagnostics - .A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals - ); - } - return getRegularTypeOfLiteralType(exprType); - } - checkSourceElement(type); - exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); - const targetType = getTypeFromTypeNode(type); - if (produceDiagnostics && targetType !== errorType) { - const widenedType = getWidenedType(exprType); - if (!isTypeComparableTo(targetType, widenedType)) { - checkTypeComparableTo( - exprType, - targetType, - errNode, - Diagnostics - .Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first - ); - } - } - return targetType; - } - - function checkNonNullAssertion(node: NonNullExpression) { - return getNonNullableType(checkExpression(node.expression)); - } - - function checkMetaProperty(node: MetaProperty): Type { - checkGrammarMetaProperty(node); - - if (node.keywordToken === SyntaxKind.NewKeyword) { - return checkNewTargetMetaProperty(node); - } - - if (node.keywordToken === SyntaxKind.ImportKeyword) { - return checkImportMetaProperty(node); - } - - return Debug.assertNever(node.keywordToken); - } - - function checkNewTargetMetaProperty(node: MetaProperty) { - const container = getNewTargetContainer(node); - if (!container) { - error( - node, - Diagnostics - .Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, - 'new.target' - ); - return errorType; - } else if (container.kind === SyntaxKind.Constructor) { - const symbol = - getSymbolOfNode(container.parent as ClassLikeDeclaration); - return getTypeOfSymbol(symbol); - } else { - const symbol = getSymbolOfNode(container)!; - return getTypeOfSymbol(symbol); - } - } - - function checkImportMetaProperty(node: MetaProperty) { - if (moduleKind !== ModuleKind.ESNext - && moduleKind !== ModuleKind.System) - { - error( - node, - Diagnostics - .The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_esnext_or_system - ); - } - const file = getSourceFileOfNode(node); - Debug.assert( - !!(file.flags & NodeFlags.PossiblyContainsImportMeta), - 'Containing file is missing import meta node flag.' - ); - Debug.assert( - !!file.externalModuleIndicator, - 'Containing file should be a module.' - ); - return node.name.escapedText === 'meta' - ? getGlobalImportMetaType() - : errorType; - } - - function getTypeOfParameter(symbol: Symbol) { - const type = getTypeOfSymbol(symbol); - if (strictNullChecks) { - const declaration = symbol.valueDeclaration; - if (declaration && hasInitializer(declaration)) { - return getOptionalType(type); - } - } - return type; - } - - function getParameterNameAtPosition( - signature: Signature, - pos: number - ) { - const paramCount = signature.parameters.length - - (signatureHasRestParameter(signature) ? 1 : 0); - if (pos < paramCount) { - return signature.parameters[pos].escapedName; - } - const restParameter = signature.parameters[paramCount] - || unknownSymbol; - const restType = getTypeOfSymbol(restParameter); - if (isTupleType(restType)) { - const associatedNames = - ( ( restType).target) - .associatedNames; - const index = pos - paramCount; - return associatedNames && associatedNames[index] - || restParameter.escapedName + '_' + index as __String; - } - return restParameter.escapedName; - } - - function getTypeAtPosition(signature: Signature, pos: number): Type { - return tryGetTypeAtPosition(signature, pos) || anyType; - } - - function tryGetTypeAtPosition(signature: Signature, pos: number): Type - | undefined - { - const paramCount = signature.parameters.length - - (signatureHasRestParameter(signature) ? 1 : 0); - if (pos < paramCount) { - return getTypeOfParameter(signature.parameters[pos]); - } - if (signatureHasRestParameter(signature)) { - // We want to return the value undefined for an out of bounds parameter position, - // so we need to check bounds here before calling getIndexedAccessType (which - // otherwise would return the type 'undefined'). - const restType = - getTypeOfSymbol(signature.parameters[paramCount]); - const index = pos - paramCount; - if (!isTupleType(restType) || restType.target.hasRestElement - || index < getTypeArguments(restType).length) - { - return getIndexedAccessType( - restType, - getLiteralType(index) - ); - } - } - return undefined; - } - - function getRestTypeAtPosition(source: Signature, pos: number): Type { - const paramCount = getParameterCount(source); - const restType = getEffectiveRestType(source); - const nonRestCount = paramCount - (restType ? 1 : 0); - if (restType && pos === nonRestCount) { - return restType; - } - const types = []; - const names = []; - for (let i = pos; i < nonRestCount; i++) { - types.push(getTypeAtPosition(source, i)); - names.push(getParameterNameAtPosition(source, i)); - } - if (restType) { - types.push(getIndexedAccessType(restType, numberType)); - names.push(getParameterNameAtPosition(source, nonRestCount)); - } - const minArgumentCount = getMinArgumentCount(source); - const minLength = minArgumentCount < pos - ? 0 - : minArgumentCount - pos; - return createTupleType( - types, - minLength, - !!restType, /*readonly*/ - false, - names - ); - } - - function getParameterCount(signature: Signature) { - const length = signature.parameters.length; - if (signatureHasRestParameter(signature)) { - const restType = - getTypeOfSymbol(signature.parameters[length - 1]); - if (isTupleType(restType)) { - return length + getTypeArguments(restType).length - 1; - } - } - return length; - } - - function getMinArgumentCount(signature: Signature) { - if (signatureHasRestParameter(signature)) { - const restType = - getTypeOfSymbol(signature.parameters - [signature.parameters.length - 1]); - if (isTupleType(restType)) { - const minLength = restType.target.minLength; - if (minLength > 0) { - return signature.parameters.length - 1 + minLength; - } - } - } - return signature.minArgumentCount; - } - - function hasEffectiveRestParameter(signature: Signature) { - if (signatureHasRestParameter(signature)) { - const restType = - getTypeOfSymbol(signature.parameters - [signature.parameters.length - 1]); - return !isTupleType(restType) - || restType.target.hasRestElement; - } - return false; - } - - function getEffectiveRestType(signature: Signature) { - if (signatureHasRestParameter(signature)) { - const restType = - getTypeOfSymbol(signature.parameters - [signature.parameters.length - 1]); - return isTupleType(restType) - ? getRestArrayTypeOfTupleType(restType) - : restType; - } - return undefined; - } - - function getNonArrayRestType(signature: Signature) { - const restType = getEffectiveRestType(signature); - return restType && !isArrayType(restType) && !isTypeAny(restType) - ? restType - : undefined; - } - - function getTypeOfFirstParameterOfSignature(signature: Signature) { - return getTypeOfFirstParameterOfSignatureWithFallback( - signature, - neverType - ); - } - - function getTypeOfFirstParameterOfSignatureWithFallback( - signature: Signature, - fallbackType: Type - ) { - return signature.parameters.length > 0 - ? getTypeAtPosition(signature, 0) - : fallbackType; - } - - function inferFromAnnotatedParameters( - signature: Signature, - context: Signature, - inferenceContext: InferenceContext - ) { - const len = signature.parameters.length - - (signatureHasRestParameter(signature) ? 1 : 0); - for (let i = 0; i < len; i++) { - const declaration = signature.parameters - [i].valueDeclaration; - if (declaration.type) { - const typeNode = - getEffectiveTypeAnnotationNode(declaration); - if (typeNode) { - inferTypes( - inferenceContext.inferences, - getTypeFromTypeNode(typeNode), - getTypeAtPosition(context, i) - ); - } - } - } - const restType = getEffectiveRestType(context); - if (restType && restType.flags & TypeFlags.TypeParameter) { - // The contextual signature has a generic rest parameter. We first instantiate the contextual - // signature (without fixing type parameters) and assign types to contextually typed parameters. - const instantiatedContext = instantiateSignature( - context, - inferenceContext.nonFixingMapper - ); - assignContextualParameterTypes(signature, instantiatedContext); - // We then infer from a tuple type representing the parameters that correspond to the contextual - // rest parameter. - const restPos = getParameterCount(context) - 1; - inferTypes( - inferenceContext.inferences, - getRestTypeAtPosition(signature, restPos), - restType - ); - } - } - - function assignContextualParameterTypes( - signature: Signature, - context: Signature - ) { - signature.typeParameters = context.typeParameters; - if (context.thisParameter) { - const parameter = signature.thisParameter; - if (!parameter || parameter.valueDeclaration - && !( parameter.valueDeclaration) - .type) - { - if (!parameter) { - signature.thisParameter = createSymbolWithType( - context.thisParameter, /*type*/ - undefined - ); - } - assignTypeToParameterAndFixTypeParameters( - signature.thisParameter!, - getTypeOfSymbol(context.thisParameter) - ); - } - } - const len = signature.parameters.length - - (signatureHasRestParameter(signature) ? 1 : 0); - for (let i = 0; i < len; i++) { - const parameter = signature.parameters[i]; - if (!getEffectiveTypeAnnotationNode( parameter - .valueDeclaration)) - { - const contextualParameterType = getTypeAtPosition( - context, - i - ); - assignTypeToParameterAndFixTypeParameters( - parameter, - contextualParameterType - ); - } - } - if (signatureHasRestParameter(signature)) { - // parameter might be a transient symbol generated by use of `arguments` in the function body. - const parameter = last(signature.parameters); - if (isTransientSymbol(parameter) - || !getEffectiveTypeAnnotationNode( parameter - .valueDeclaration)) - { - const contextualParameterType = getRestTypeAtPosition( - context, - len - ); - assignTypeToParameterAndFixTypeParameters( - parameter, - contextualParameterType - ); - } - } - } - - // When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push - // the destructured type into the contained binding elements. - function assignBindingElementTypes(pattern: BindingPattern) { - for (const element of pattern.elements) { - if (!isOmittedExpression(element)) { - if (element.name.kind === SyntaxKind.Identifier) { - getSymbolLinks(getSymbolOfNode(element)) - .type = getTypeForBindingElement(element); - } else { - assignBindingElementTypes(element.name); - } - } - } - } - - function assignTypeToParameterAndFixTypeParameters( - parameter: Symbol, - contextualType: Type - ) { - const links = getSymbolLinks(parameter); - if (!links.type) { - links.type = contextualType; - const decl = parameter - .valueDeclaration as ParameterDeclaration; - if (decl.name.kind !== SyntaxKind.Identifier) { - // if inference didn't come up with anything but unknown, fall back to the binding pattern if present. - if (links.type === unknownType) { - links.type = getTypeFromBindingPattern(decl.name); - } - assignBindingElementTypes(decl.name); - } - } - } - - function createPromiseType(promisedType: Type): Type { - // creates a `Promise` type where `T` is the promisedType argument - const globalPromiseType = - getGlobalPromiseType(/*reportErrors*/ true); - if (globalPromiseType !== emptyGenericType) { - // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type - promisedType = getAwaitedType(promisedType) || unknownType; - return createTypeReference(globalPromiseType, [promisedType]); - } - - return unknownType; - } - - function createPromiseLikeType(promisedType: Type): Type { - // creates a `PromiseLike` type where `T` is the promisedType argument - const globalPromiseLikeType = - getGlobalPromiseLikeType(/*reportErrors*/ true); - if (globalPromiseLikeType !== emptyGenericType) { - // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type - promisedType = getAwaitedType(promisedType) || unknownType; - return createTypeReference( - globalPromiseLikeType, - [promisedType] - ); - } - - return unknownType; - } - - function createPromiseReturnType( - func: FunctionLikeDeclaration | ImportCall, - promisedType: Type - ) { - const promiseType = createPromiseType(promisedType); - if (promiseType === unknownType) { - error(func, isImportCall(func) - ? Diagnostics - .A_dynamic_import_call_returns_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option - : Diagnostics - .An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option); - return errorType; - } else if (!getGlobalPromiseConstructorSymbol(/*reportErrors*/ true)) { - error(func, isImportCall(func) - ? Diagnostics - .A_dynamic_import_call_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option - : Diagnostics - .An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option); - } - - return promiseType; - } - - function getReturnTypeFromBody( - func: FunctionLikeDeclaration, - checkMode?: CheckMode - ): Type { - if (!func.body) { - return errorType; - } - - const functionFlags = getFunctionFlags(func); - const isAsync = (functionFlags & FunctionFlags.Async) !== 0; - const isGenerator = - (functionFlags & FunctionFlags.Generator) !== 0; - - let returnType: Type | undefined; - let yieldType: Type | undefined; - let nextType: Type | undefined; - let fallbackReturnType: Type = voidType; - if (func.body.kind - !== SyntaxKind.Block) - { // Async or normal arrow function - returnType = checkExpressionCached( - func.body, - checkMode && checkMode & ~CheckMode.SkipGenericFunctions - ); - if (isAsync) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which we will wrap in - // the native Promise type later in this function. - returnType = checkAwaitedType( - returnType, /*errorNode*/ - func, - Diagnostics - .The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - ); - } - } else if (isGenerator) { // Generator or AsyncGenerator function - const returnTypes = checkAndAggregateReturnExpressionTypes( - func, - checkMode - ); - if (!returnTypes) { - fallbackReturnType = neverType; - } else if (returnTypes.length > 0) { - returnType = getUnionType( - returnTypes, - UnionReduction.Subtype - ); - } - const { yieldTypes, nextTypes } = - checkAndAggregateYieldOperandTypes(func, checkMode); - yieldType = some(yieldTypes) - ? getUnionType(yieldTypes, UnionReduction.Subtype) - : undefined; - nextType = some(nextTypes) - ? getIntersectionType(nextTypes) - : undefined; - } else { // Async or normal function - const types = checkAndAggregateReturnExpressionTypes( - func, - checkMode - ); - if (!types) { - // For an async function, the return type will not be never, but rather a Promise for never. - return functionFlags & FunctionFlags.Async - ? createPromiseReturnType( - func, - neverType - ) // Async function - : neverType; // Normal function - } - if (types.length === 0) { - // For an async function, the return type will not be void, but rather a Promise for void. - return functionFlags & FunctionFlags.Async - ? createPromiseReturnType( - func, - voidType - ) // Async function - : voidType; // Normal function - } - - // Return a union of the return expression types. - returnType = getUnionType(types, UnionReduction.Subtype); - } - - if (returnType || yieldType || nextType) { - const contextualSignature = - getContextualSignatureForFunctionLikeDeclaration(func); - if (!contextualSignature) { - if (yieldType) { - reportErrorsFromWidening( - func, - yieldType, - WideningKind.GeneratorYield - ); - } - if (returnType) reportErrorsFromWidening(func, returnType); - if (nextType) reportErrorsFromWidening(func, nextType); - } - if (returnType && isUnitType(returnType) - || yieldType && isUnitType(yieldType) - || nextType && isUnitType(nextType)) - { - const contextualType = !contextualSignature - ? undefined - : contextualSignature - === getSignatureFromDeclaration(func) - ? isGenerator ? undefined : returnType - : instantiateContextualType( - getReturnTypeOfSignature(contextualSignature), - func - ); - if (isGenerator) { - yieldType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded( - yieldType, - contextualType, - IterationTypeKind.Yield, - isAsync - ); - returnType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded( - returnType, - contextualType, - IterationTypeKind.Return, - isAsync - ); - nextType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded( - nextType, - contextualType, - IterationTypeKind.Next, - isAsync - ); - } else { - returnType = getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded( - returnType, - contextualType, - isAsync - ); - } - } - - if (yieldType) yieldType = getWidenedType(yieldType); - if (returnType) returnType = getWidenedType(returnType); - if (nextType) nextType = getWidenedType(nextType); - } - - if (isGenerator) { - return createGeneratorReturnType( - yieldType || neverType, - returnType || fallbackReturnType, - nextType - || getContextualIterationType( - IterationTypeKind.Next, - func - ) || unknownType, - isAsync - ); - } else { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body is awaited type of the body, wrapped in a native Promise type. - return isAsync - ? createPromiseType(returnType || fallbackReturnType) - : returnType || fallbackReturnType; - } - } - - function createGeneratorReturnType( - yieldType: Type, - returnType: Type, - nextType: Type, - isAsyncGenerator: boolean - ) { - const resolver = isAsyncGenerator - ? asyncIterationTypesResolver - : syncIterationTypesResolver; - const globalGeneratorType = resolver - .getGlobalGeneratorType(/*reportErrors*/ false); - yieldType = resolver.resolveIterationType( - yieldType, /*errorNode*/ - undefined - ) || unknownType; - returnType = resolver.resolveIterationType( - returnType, /*errorNode*/ - undefined - ) || unknownType; - nextType = resolver.resolveIterationType( - nextType, /*errorNode*/ - undefined - ) || unknownType; - if (globalGeneratorType === emptyGenericType) { - // Fall back to the global IterableIterator if returnType is assignable to the expected return iteration - // type of IterableIterator, and the expected next iteration type of IterableIterator is assignable to - // nextType. - const globalType = resolver - .getGlobalIterableIteratorType(/*reportErrors*/ false); - const iterationTypes = globalType !== emptyGenericType - ? getIterationTypesOfGlobalIterableType( - globalType, - resolver - ) - : undefined; - const iterableIteratorReturnType = iterationTypes - ? iterationTypes.returnType - : anyType; - const iterableIteratorNextType = iterationTypes - ? iterationTypes.nextType - : undefinedType; - if (isTypeAssignableTo(returnType, iterableIteratorReturnType) - && isTypeAssignableTo(iterableIteratorNextType, nextType)) - { - if (globalType !== emptyGenericType) { - return createTypeFromGenericGlobalType( - globalType, - [yieldType] - ); - } - - // The global IterableIterator type doesn't exist, so report an error - resolver - .getGlobalIterableIteratorType(/*reportErrors*/ true); - return emptyObjectType; - } - - // The global Generator type doesn't exist, so report an error - resolver.getGlobalGeneratorType(/*reportErrors*/ true); - return emptyObjectType; - } - - return createTypeFromGenericGlobalType( - globalGeneratorType, - [yieldType, returnType, nextType] - ); - } - - function checkAndAggregateYieldOperandTypes( - func: FunctionLikeDeclaration, - checkMode: CheckMode | undefined - ) { - const yieldTypes: Type[] = []; - const nextTypes: Type[] = []; - const isAsync = - (getFunctionFlags(func) & FunctionFlags.Async) !== 0; - forEachYieldExpression( func.body, yieldExpression => { - const yieldExpressionType = yieldExpression.expression - ? checkExpression(yieldExpression.expression, checkMode) - : undefinedWideningType; - pushIfUnique( - yieldTypes, - getYieldedTypeOfYieldExpression( - yieldExpression, - yieldExpressionType, - anyType, - isAsync - ) - ); - let nextType: Type | undefined; - if (yieldExpression.asteriskToken) { - const iterationTypes = getIterationTypesOfIterable( - yieldExpressionType, - isAsync - ? IterationUse.AsyncYieldStar - : IterationUse.YieldStar, - yieldExpression.expression - ); - nextType = iterationTypes && iterationTypes.nextType; - } else { - nextType = getContextualType(yieldExpression); - } - if (nextType) pushIfUnique(nextTypes, nextType); - }); - return { yieldTypes, nextTypes }; - } - - function getYieldedTypeOfYieldExpression( - node: YieldExpression, - expressionType: Type, - sentType: Type, - isAsync: boolean - ): Type | undefined { - const errorNode = node.expression || node; - // A `yield*` expression effectively yields everything that its operand yields - const yieldedType = node.asteriskToken - ? checkIteratedTypeOrElementType( - isAsync - ? IterationUse.AsyncYieldStar - : IterationUse.YieldStar, - expressionType, - sentType, - errorNode - ) - : expressionType; - return !isAsync - ? yieldedType - : getAwaitedType(yieldedType, errorNode, node.asteriskToken - ? Diagnostics - .Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - : Diagnostics - .Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - } - - /** - * Collect the TypeFacts learned from a typeof switch with - * total clauses `witnesses`, and the active clause ranging - * from `start` to `end`. Parameter `hasDefault` denotes - * whether the active clause contains a default clause. - */ - function getFactsFromTypeofSwitch( - start: number, - end: number, - witnesses: string[], - hasDefault: boolean - ): TypeFacts { - let facts: TypeFacts = TypeFacts.None; - // When in the default we only collect inequality facts - // because default is 'in theory' a set of infinite - // equalities. - if (hasDefault) { - // Value is not equal to any types after the active clause. - for (let i = end; i < witnesses.length; i++) { - facts |= typeofNEFacts.get(witnesses[i]) - || TypeFacts.TypeofNEHostObject; - } - // Remove inequalities for types that appear in the - // active clause because they appear before other - // types collected so far. - for (let i = start; i < end; i++) { - facts &= ~(typeofNEFacts.get(witnesses[i]) || 0); - } - // Add inequalities for types before the active clause unconditionally. - for (let i = 0; i < start; i++) { - facts |= typeofNEFacts.get(witnesses[i]) - || TypeFacts.TypeofNEHostObject; - } - } // When in an active clause without default the set of - // equalities is finite. - else { - // Add equalities for all types in the active clause. - for (let i = start; i < end; i++) { - facts |= typeofEQFacts.get(witnesses[i]) - || TypeFacts.TypeofEQHostObject; - } - // Remove equalities for types that appear before the - // active clause. - for (let i = 0; i < start; i++) { - facts &= ~(typeofEQFacts.get(witnesses[i]) || 0); - } - } - return facts; - } - - function isExhaustiveSwitchStatement(node: SwitchStatement): boolean { - const links = getNodeLinks(node); - return links.isExhaustive !== undefined - ? links.isExhaustive - : (links - .isExhaustive = computeExhaustiveSwitchStatement(node)); - } - - function computeExhaustiveSwitchStatement(node: - SwitchStatement): boolean - { - if (node.expression.kind === SyntaxKind.TypeOfExpression) { - const operandType = - getTypeOfExpression((node.expression as TypeOfExpression) - .expression); - // This cast is safe because the switch is possibly exhaustive and does not contain a default case, so there can be no undefined. - const witnesses = - getSwitchClauseTypeOfWitnesses(node); - // notEqualFacts states that the type of the switched value is not equal to every type in the switch. - const notEqualFacts = getFactsFromTypeofSwitch( - 0, - 0, - witnesses, /*hasDefault*/ - true - ); - const type = getBaseConstraintOfType(operandType) - || operandType; - return !!(filterType( - type, - t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts - ).flags & TypeFlags.Never); - } - const type = getTypeOfExpression(node.expression); - if (!isLiteralType(type)) { - return false; - } - const switchTypes = getSwitchClauseTypes(node); - if (!switchTypes.length - || some(switchTypes, isNeitherUnitTypeNorNever)) - { - return false; - } - return eachTypeContainedIn( - mapType(type, getRegularTypeOfLiteralType), - switchTypes - ); - } - - function functionHasImplicitReturn(func: FunctionLikeDeclaration) { - return func.endFlowNode && isReachableFlowNode(func.endFlowNode); - } - - /** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means func returns `void`, `undefined` means it returns `never`. */ - function checkAndAggregateReturnExpressionTypes( - func: FunctionLikeDeclaration, - checkMode: CheckMode | undefined - ): Type[] | undefined { - const functionFlags = getFunctionFlags(func); - const aggregatedTypes: Type[] = []; - let hasReturnWithNoExpression = functionHasImplicitReturn(func); - let hasReturnOfTypeNever = false; - forEachReturnStatement( func.body, returnStatement => { - const expr = returnStatement.expression; - if (expr) { - let type = checkExpressionCached( - expr, - checkMode - && checkMode & ~CheckMode.SkipGenericFunctions - ); - if (functionFlags & FunctionFlags.Async) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which should be wrapped in - // the native Promise type by the caller. - type = checkAwaitedType( - type, - func, - Diagnostics - .The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - ); - } - if (type.flags & TypeFlags.Never) { - hasReturnOfTypeNever = true; - } - pushIfUnique(aggregatedTypes, type); - } else { - hasReturnWithNoExpression = true; - } - }); - if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression - && (hasReturnOfTypeNever || mayReturnNever(func))) - { - return undefined; - } - if (strictNullChecks && aggregatedTypes.length - && hasReturnWithNoExpression - && !(isJSConstructor(func) - && aggregatedTypes.some(t => t.symbol === func.symbol))) - { - // Javascript "callable constructors", containing eg `if (!(this instanceof A)) return new A()` should not add undefined - pushIfUnique(aggregatedTypes, undefinedType); - } - return aggregatedTypes; - } - function mayReturnNever(func: FunctionLikeDeclaration): boolean { - switch (func.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return true; - case SyntaxKind.MethodDeclaration: - return func.parent.kind - === SyntaxKind.ObjectLiteralExpression; - default: - return false; - } - } - - /** - * TypeScript Specification 1.0 (6.3) - July 2014 - * An explicitly typed function whose return type isn't the Void type, - * the Any type, or a union type containing the Void or Any type as a constituent - * must have at least one return statement somewhere in its body. - * An exception to this rule is if the function implementation consists of a single 'throw' statement. - * - * @param returnType - return type of the function, can be undefined if return type is not explicitly specified - */ - function checkAllCodePathsInNonVoidFunctionReturnOrThrow( - func: FunctionLikeDeclaration | MethodSignature, - returnType: Type | undefined - ): void { - if (!produceDiagnostics) { - return; - } - - const functionFlags = getFunctionFlags(func); - const type = returnType - && getReturnOrPromisedType(returnType, functionFlags); - - // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. - if (type - && maybeTypeOfKind(type, TypeFlags.Any | TypeFlags.Void)) - { - return; - } - - // If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check. - // also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw - if (func.kind === SyntaxKind.MethodSignature - || nodeIsMissing(func.body) - || func.body!.kind !== SyntaxKind.Block - || !functionHasImplicitReturn(func)) - { - return; - } - - const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn; - - if (type && type.flags & TypeFlags.Never) { - error( - getEffectiveReturnTypeNode(func), - Diagnostics - .A_function_returning_never_cannot_have_a_reachable_end_point - ); - } else if (type && !hasExplicitReturn) { - // minimal check: function has syntactic return type annotation and no explicit return statements in the body - // this function does not conform to the specification. - // NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present - error( - getEffectiveReturnTypeNode(func), - Diagnostics - .A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value - ); - } else if (type && strictNullChecks - && !isTypeAssignableTo(undefinedType, type)) - { - error( - getEffectiveReturnTypeNode(func) || func, - Diagnostics - .Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined - ); - } else if (compilerOptions.noImplicitReturns) { - if (!type) { - // If return type annotation is omitted check if function has any explicit return statements. - // If it does not have any - its inferred return type is void - don't do any checks. - // Otherwise get inferred return type from function body and report error only if it is not void / anytype - if (!hasExplicitReturn) { - return; - } - const inferredReturnType = - getReturnTypeOfSignature(getSignatureFromDeclaration(func)); - if (isUnwrappedReturnTypeVoidOrAny( - func, - inferredReturnType - )) { - return; - } - } - error( - getEffectiveReturnTypeNode(func) || func, - Diagnostics.Not_all_code_paths_return_a_value - ); - } - } - - function checkFunctionExpressionOrObjectLiteralMethod( - node: FunctionExpression | MethodDeclaration, - checkMode?: CheckMode - ): Type { - Debug - .assert(node.kind !== SyntaxKind.MethodDeclaration - || isObjectLiteralMethod(node)); - checkNodeDeferred(node); - - // The identityMapper object is used to indicate that function expressions are wildcards - if (checkMode && checkMode & CheckMode.SkipContextSensitive - && isContextSensitive(node)) - { - // Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage - if (!getEffectiveReturnTypeNode(node) - && hasContextSensitiveReturnExpression(node)) - { - // Return plain anyFunctionType if there is no possibility we'll make inferences from the return type - const contextualSignature = getContextualSignature(node); - if (contextualSignature - && couldContainTypeVariables(getReturnTypeOfSignature(contextualSignature))) - { - const links = getNodeLinks(node); - if (links.contextFreeType) { - return links.contextFreeType; - } - const returnType = getReturnTypeFromBody( - node, - checkMode - ); - const returnOnlySignature = createSignature( - undefined, - undefined, - undefined, - emptyArray, - returnType, /*resolvedTypePredicate*/ - undefined, - 0, - SignatureFlags.None - ); - const returnOnlyType = createAnonymousType( - node.symbol, - emptySymbols, - [returnOnlySignature], - emptyArray, - undefined, - undefined - ); - returnOnlyType.objectFlags |= ObjectFlags - .NonInferrableType; - return links.contextFreeType = returnOnlyType; - } - } - return anyFunctionType; - } - - // Grammar checking - const hasGrammarError = checkGrammarFunctionLikeDeclaration(node); - if (!hasGrammarError - && node.kind === SyntaxKind.FunctionExpression) - { - checkGrammarForGenerator(node); - } - - const type = getTypeOfSymbol(getMergedSymbol(node.symbol)); - if (isTypeAny(type)) { - return type; - } - - contextuallyCheckFunctionExpressionOrObjectLiteralMethod( - node, - checkMode - ); - - return type; - } - - function contextuallyCheckFunctionExpressionOrObjectLiteralMethod( - node: FunctionExpression | ArrowFunction | MethodDeclaration, - checkMode?: CheckMode - ) { - const links = getNodeLinks(node); - // Check if function expression is contextually typed and assign parameter types if so. - if (!(links.flags & NodeCheckFlags.ContextChecked)) { - const contextualSignature = getContextualSignature(node); - // If a type check is started at a function expression that is an argument of a function call, obtaining the - // contextual type may recursively get back to here during overload resolution of the call. If so, we will have - // already assigned contextual types. - if (!(links.flags & NodeCheckFlags.ContextChecked)) { - links.flags |= NodeCheckFlags.ContextChecked; - if (contextualSignature) { - const type = - getTypeOfSymbol(getMergedSymbol(node.symbol)); - if (isTypeAny(type)) { - return; - } - const signature = getSignaturesOfType( - type, - SignatureKind.Call - )[0]; - if (isContextSensitive(node)) { - const inferenceContext = getInferenceContext(node); - if (checkMode - && checkMode & CheckMode.Inferential) - { - inferFromAnnotatedParameters( - signature, - contextualSignature, - inferenceContext! - ); - } - const instantiatedContextualSignature = - inferenceContext - ? instantiateSignature( - contextualSignature, - inferenceContext.mapper - ) - : contextualSignature; - assignContextualParameterTypes( - signature, - instantiatedContextualSignature - ); - } - if (!getReturnTypeFromAnnotation(node) - && !signature.resolvedReturnType) - { - const returnType = getReturnTypeFromBody( - node, - checkMode - ); - if (!signature.resolvedReturnType) { - signature.resolvedReturnType = returnType; - } - } - } - checkSignatureDeclaration(node); - } - } - } - - function getReturnOrPromisedType( - type: Type | undefined, - functionFlags: FunctionFlags - ) { - const isGenerator = !!(functionFlags & FunctionFlags.Generator); - const isAsync = !!(functionFlags & FunctionFlags.Async); - return type && isGenerator - ? getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Return, - type, - isAsync - ) || errorType - : type && isAsync - ? getAwaitedType(type) || errorType - : type; - } - - function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: - ArrowFunction | FunctionExpression | MethodDeclaration) - { - Debug - .assert(node.kind !== SyntaxKind.MethodDeclaration - || isObjectLiteralMethod(node)); - - const functionFlags = getFunctionFlags(node); - const returnType = getReturnTypeFromAnnotation(node); - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); - - if (node.body) { - if (!getEffectiveReturnTypeNode(node)) { - // There are some checks that are only performed in getReturnTypeFromBody, that may produce errors - // we need. An example is the noImplicitAny errors resulting from widening the return expression - // of a function. Because checking of function expression bodies is deferred, there was never an - // appropriate time to do this during the main walk of the file (see the comment at the top of - // checkFunctionExpressionBodies). So it must be done now. - getReturnTypeOfSignature(getSignatureFromDeclaration(node)); - } - - if (node.body.kind === SyntaxKind.Block) { - checkSourceElement(node.body); - } else { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so we - // should not be checking assignability of a promise to the return type. Instead, we need to - // check assignability of the awaited type of the expression body against the promised type of - // its return type annotation. - const exprType = checkExpression(node.body); - const returnOrPromisedType = getReturnOrPromisedType( - returnType, - functionFlags - ); - if (returnOrPromisedType) { - if ((functionFlags & FunctionFlags.AsyncGenerator) - === FunctionFlags.Async) - { // Async function - const awaitedType = checkAwaitedType( - exprType, - node.body, - Diagnostics - .The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - ); - checkTypeAssignableToAndOptionallyElaborate( - awaitedType, - returnOrPromisedType, - node.body, - node.body - ); - } else { // Normal function - checkTypeAssignableToAndOptionallyElaborate( - exprType, - returnOrPromisedType, - node.body, - node.body - ); - } - } - } - } - } - - function checkArithmeticOperandType( - operand: Node, - type: Type, - diagnostic: DiagnosticMessage, - isAwaitValid = false - ): boolean { - if (!isTypeAssignableTo(type, numberOrBigIntType)) { - const awaitedType = isAwaitValid - && getAwaitedTypeOfPromise(type); - errorAndMaybeSuggestAwait( - operand, - !!awaitedType - && isTypeAssignableTo(awaitedType, numberOrBigIntType), - diagnostic - ); - return false; - } - return true; - } - - function isReadonlyAssignmentDeclaration(d: Declaration) { - if (!isCallExpression(d)) { - return false; - } - if (!isBindableObjectDefinePropertyCall(d)) { - return false; - } - const objectLitType = checkExpressionCached(d.arguments[2]); - const valueType = getTypeOfPropertyOfType( - objectLitType, - 'value' as __String - ); - if (valueType) { - const writableProp = getPropertyOfType( - objectLitType, - 'writable' as __String - ); - const writableType = writableProp - && getTypeOfSymbol(writableProp); - if (!writableType || writableType === falseType - || writableType === regularFalseType) - { - return true; - } - // We include this definition whereupon we walk back and check the type at the declaration because - // The usual definition of `Object.defineProperty` will _not_ cause literal types to be preserved in the - // argument types, should the type be contextualized by the call itself. - if (writableProp && writableProp.valueDeclaration - && isPropertyAssignment(writableProp.valueDeclaration)) - { - const initializer = writableProp.valueDeclaration - .initializer; - const rawOriginalType = checkExpression(initializer); - if (rawOriginalType === falseType - || rawOriginalType === regularFalseType) - { - return true; - } - } - return false; - } - const setProp = getPropertyOfType( - objectLitType, - 'set' as __String - ); - return !setProp; - } - - function isReadonlySymbol(symbol: Symbol): boolean { - // The following symbols are considered read-only: - // Properties with a 'readonly' modifier - // Variables declared with 'const' - // Get accessors without matching set accessors - // Enum members - // Object.defineProperty assignments with writable false or no setter - // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) - return !!(getCheckFlags(symbol) & CheckFlags.Readonly - || symbol.flags & SymbolFlags.Property - && getDeclarationModifierFlagsFromSymbol(symbol) - & ModifierFlags.Readonly - || symbol.flags & SymbolFlags.Variable - && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const - || symbol.flags & SymbolFlags.Accessor - && !(symbol.flags & SymbolFlags.SetAccessor) - || symbol.flags & SymbolFlags.EnumMember - || some(symbol.declarations, isReadonlyAssignmentDeclaration)); - } - - function isAssignmentToReadonlyEntity( - expr: Expression, - symbol: Symbol, - assignmentKind: AssignmentKind - ) { - if (assignmentKind === AssignmentKind.None) { - // no assigment means it doesn't matter whether the entity is readonly - return false; - } - if (isReadonlySymbol(symbol)) { - // Allow assignments to readonly properties within constructors of the same class declaration. - if (symbol.flags & SymbolFlags.Property - && isAccessExpression(expr) - && expr.expression.kind === SyntaxKind.ThisKeyword) - { - // Look for if this is the constructor for the class that `symbol` is a property of. - const ctor = getContainingFunction(expr); - if (!(ctor && ctor.kind === SyntaxKind.Constructor)) { - return true; - } - if (symbol.valueDeclaration) { - const isAssignmentDeclaration = - isBinaryExpression(symbol.valueDeclaration); - const isLocalPropertyDeclaration = - ctor.parent === symbol.valueDeclaration.parent; - const isLocalParameterProperty = - ctor === symbol.valueDeclaration.parent; - const isLocalThisPropertyAssignment = - isAssignmentDeclaration - && symbol.parent?.valueDeclaration - === ctor.parent; - const isLocalThisPropertyAssignmentConstructorFunction = - isAssignmentDeclaration - && symbol.parent?.valueDeclaration === ctor; - const isWriteableSymbol = isLocalPropertyDeclaration - || isLocalParameterProperty - || isLocalThisPropertyAssignment - || isLocalThisPropertyAssignmentConstructorFunction; - return !isWriteableSymbol; - } - } - return true; - } - if (isAccessExpression(expr)) { - // references through namespace import should be readonly - const node = skipParentheses(expr.expression); - if (node.kind === SyntaxKind.Identifier) { - const symbol = getNodeLinks(node).resolvedSymbol!; - if (symbol.flags & SymbolFlags.Alias) { - const declaration = - getDeclarationOfAliasSymbol(symbol); - return !!declaration - && declaration.kind === SyntaxKind.NamespaceImport; - } - } - } - return false; - } - - function checkReferenceExpression( - expr: Expression, - invalidReferenceMessage: DiagnosticMessage, - invalidOptionalChainMessage: DiagnosticMessage - ): boolean { - // References are combinations of identifiers, parentheses, and property accesses. - const node = skipOuterExpressions( - expr, - OuterExpressionKinds.Assertions - | OuterExpressionKinds.Parentheses - ); - if (node.kind !== SyntaxKind.Identifier - && !isAccessExpression(node)) - { - error(expr, invalidReferenceMessage); - return false; - } - if (node.flags & NodeFlags.OptionalChain) { - error(expr, invalidOptionalChainMessage); - return false; - } - return true; - } - - function checkDeleteExpression(node: DeleteExpression): Type { - checkExpression(node.expression); - const expr = skipParentheses(node.expression); - if (!isAccessExpression(expr)) { - error( - expr, - Diagnostics - .The_operand_of_a_delete_operator_must_be_a_property_reference - ); - return booleanType; - } - if (expr.kind === SyntaxKind.PropertyAccessExpression - && isPrivateIdentifier((expr as PropertyAccessExpression) - .name)) - { - error( - expr, - Diagnostics - .The_operand_of_a_delete_operator_cannot_be_a_private_identifier - ); - } - const links = getNodeLinks(expr); - const symbol = - getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol); - if (symbol && isReadonlySymbol(symbol)) { - error( - expr, - Diagnostics - .The_operand_of_a_delete_operator_cannot_be_a_read_only_property - ); - } - return booleanType; - } - - function checkTypeOfExpression(node: TypeOfExpression): Type { - checkExpression(node.expression); - return typeofType; - } - - function checkVoidExpression(node: VoidExpression): Type { - checkExpression(node.expression); - return undefinedWideningType; - } - - function isTopLevelAwait(node: AwaitExpression) { - const container = getThisContainer( - node, /*includeArrowFunctions*/ - true - ); - return isSourceFile(container); - } - - function checkAwaitExpression(node: AwaitExpression): Type { - // Grammar checking - if (produceDiagnostics) { - if (!(node.flags & NodeFlags.AwaitContext)) { - if (isTopLevelAwait(node)) { - const sourceFile = getSourceFileOfNode(node); - if ((moduleKind !== ModuleKind.ESNext - && moduleKind !== ModuleKind.System) - || languageVersion < ScriptTarget.ES2017 - || !isEffectiveExternalModule( - sourceFile, - compilerOptions - )) - { - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition( - sourceFile, - node.pos - ); - const diagnostic = createFileDiagnostic( - sourceFile, - span.start, - span.length, - Diagnostics - .await_outside_of_an_async_function_is_only_allowed_at_the_top_level_of_a_module_when_module_is_esnext_or_system_and_target_is_es2017_or_higher - ); - diagnostics.add(diagnostic); - } - } - } else { - // use of 'await' in non-async function - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition( - sourceFile, - node.pos - ); - const diagnostic = createFileDiagnostic( - sourceFile, - span.start, - span.length, - Diagnostics - .await_expression_is_only_allowed_within_an_async_function - ); - const func = getContainingFunction(node); - if (func && func.kind !== SyntaxKind.Constructor - && (getFunctionFlags(func) - & FunctionFlags.Async) === 0) - { - const relatedInfo = createDiagnosticForNode( - func, - Diagnostics - .Did_you_mean_to_mark_this_function_as_async - ); - addRelatedInfo(diagnostic, relatedInfo); - } - diagnostics.add(diagnostic); - } - } - } - - if (isInParameterInitializerBeforeContainingFunction(node)) { - error( - node, - Diagnostics - .await_expressions_cannot_be_used_in_a_parameter_initializer - ); - } - } - - const operandType = checkExpression(node.expression); - const awaitedType = checkAwaitedType( - operandType, - node, - Diagnostics - .Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - ); - if (awaitedType === operandType && awaitedType !== errorType - && !(operandType.flags & TypeFlags.AnyOrUnknown)) - { - addErrorOrSuggestion( - /*isError*/ false, - createDiagnosticForNode( - node, - Diagnostics - .await_has_no_effect_on_the_type_of_this_expression - ) - ); - } - return awaitedType; - } - - function checkPrefixUnaryExpression(node: - PrefixUnaryExpression): Type - { - const operandType = checkExpression(node.operand); - if (operandType === silentNeverType) { - return silentNeverType; - } - switch (node.operand.kind) { - case SyntaxKind.NumericLiteral: - switch (node.operator) { - case SyntaxKind.MinusToken: - return getFreshTypeOfLiteralType(getLiteralType(-(node - .operand as NumericLiteral).text)); - case SyntaxKind.PlusToken: - return getFreshTypeOfLiteralType(getLiteralType(+(node - .operand as NumericLiteral).text)); - } - break; - case SyntaxKind.BigIntLiteral: - if (node.operator === SyntaxKind.MinusToken) { - return getFreshTypeOfLiteralType(getLiteralType({ - negative: true, - base10Value: - parsePseudoBigInt((node - .operand as BigIntLiteral).text) - })); - } - } - switch (node.operator) { - case SyntaxKind.PlusToken: - case SyntaxKind.MinusToken: - case SyntaxKind.TildeToken: - checkNonNullType(operandType, node.operand); - if (maybeTypeOfKind(operandType, TypeFlags.ESSymbolLike)) { - error( - node.operand, - Diagnostics - .The_0_operator_cannot_be_applied_to_type_symbol, - tokenToString(node.operator) - ); - } - if (node.operator === SyntaxKind.PlusToken) { - if (maybeTypeOfKind( - operandType, - TypeFlags.BigIntLike - )) { - error( - node.operand, - Diagnostics - .Operator_0_cannot_be_applied_to_type_1, - tokenToString(node.operator), - typeToString(getBaseTypeOfLiteralType(operandType)) - ); - } - return numberType; - } - return getUnaryResultType(operandType); - case SyntaxKind.ExclamationToken: - checkTruthinessExpression(node.operand); - const facts = - getTypeFacts(operandType) - & (TypeFacts.Truthy | TypeFacts.Falsy); - return facts === TypeFacts.Truthy - ? falseType - : facts === TypeFacts.Falsy - ? trueType - : booleanType; - case SyntaxKind.PlusPlusToken: - case SyntaxKind.MinusMinusToken: - const ok = checkArithmeticOperandType( - node.operand, - checkNonNullType(operandType, node.operand), - Diagnostics - .An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type - ); - if (ok) { - // run check only if former checks succeeded to avoid reporting cascading errors - checkReferenceExpression( - node.operand, - Diagnostics - .The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, - Diagnostics - .The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access - ); - } - return getUnaryResultType(operandType); - } - return errorType; - } - - function checkPostfixUnaryExpression(node: - PostfixUnaryExpression): Type - { - const operandType = checkExpression(node.operand); - if (operandType === silentNeverType) { - return silentNeverType; - } - const ok = checkArithmeticOperandType( - node.operand, - checkNonNullType(operandType, node.operand), - Diagnostics - .An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type - ); - if (ok) { - // run check only if former checks succeeded to avoid reporting cascading errors - checkReferenceExpression( - node.operand, - Diagnostics - .The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, - Diagnostics - .The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access - ); - } - return getUnaryResultType(operandType); - } - - function getUnaryResultType(operandType: Type): Type { - if (maybeTypeOfKind(operandType, TypeFlags.BigIntLike)) { - return isTypeAssignableToKind( - operandType, - TypeFlags.AnyOrUnknown - ) || maybeTypeOfKind(operandType, TypeFlags.NumberLike) - ? numberOrBigIntType - : bigintType; - } - // If it's not a bigint type, implicit coercion will result in a number - return numberType; - } - - // Return true if type might be of the given kind. A union or intersection type might be of a given - // kind if at least one constituent type is of the given kind. - function maybeTypeOfKind(type: Type, kind: TypeFlags): boolean { - if (type.flags & kind & ~TypeFlags.GenericMappedType - || kind & TypeFlags.GenericMappedType - && isGenericMappedType(type)) - { - return true; - } - if (type.flags & TypeFlags.UnionOrIntersection) { - const types = ( type).types; - for (const t of types) { - if (maybeTypeOfKind(t, kind)) { - return true; - } - } - } - return false; - } - - function isTypeAssignableToKind( - source: Type, - kind: TypeFlags, - strict?: boolean - ): boolean { - if (source.flags & kind) { - return true; - } - if (strict - && source.flags - & (TypeFlags.AnyOrUnknown | TypeFlags.Void - | TypeFlags.Undefined | TypeFlags.Null)) - { - return false; - } - return !!(kind & TypeFlags.NumberLike) - && isTypeAssignableTo(source, numberType) - || !!(kind & TypeFlags.BigIntLike) - && isTypeAssignableTo(source, bigintType) - || !!(kind & TypeFlags.StringLike) - && isTypeAssignableTo(source, stringType) - || !!(kind & TypeFlags.BooleanLike) - && isTypeAssignableTo(source, booleanType) - || !!(kind & TypeFlags.Void) - && isTypeAssignableTo(source, voidType) - || !!(kind & TypeFlags.Never) - && isTypeAssignableTo(source, neverType) - || !!(kind & TypeFlags.Null) - && isTypeAssignableTo(source, nullType) - || !!(kind & TypeFlags.Undefined) - && isTypeAssignableTo(source, undefinedType) - || !!(kind & TypeFlags.ESSymbol) - && isTypeAssignableTo(source, esSymbolType) - || !!(kind & TypeFlags.NonPrimitive) - && isTypeAssignableTo(source, nonPrimitiveType); - } - - function allTypesAssignableToKind( - source: Type, - kind: TypeFlags, - strict?: boolean - ): boolean { - return source.flags & TypeFlags.Union - ? every( - (source as UnionType).types, - subType => allTypesAssignableToKind(subType, kind, strict) - ) - : isTypeAssignableToKind(source, kind, strict); - } - - function isConstEnumObjectType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Anonymous) - && !!type.symbol && isConstEnumSymbol(type.symbol); - } - - function isConstEnumSymbol(symbol: Symbol): boolean { - return (symbol.flags & SymbolFlags.ConstEnum) !== 0; - } - - function checkInstanceOfExpression( - left: Expression, - right: Expression, - leftType: Type, - rightType: Type - ): Type { - if (leftType === silentNeverType - || rightType === silentNeverType) - { - return silentNeverType; - } - // TypeScript 1.0 spec (April 2014): 4.15.4 - // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, - // and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature. - // The result is always of the Boolean primitive type. - // NOTE: do not raise error if leftType is unknown as related error was already reported - if (!isTypeAny(leftType) - && allTypesAssignableToKind(leftType, TypeFlags.Primitive)) - { - error( - left, - Diagnostics - .The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter - ); - } - // NOTE: do not raise error if right is unknown as related error was already reported - if (!(isTypeAny(rightType) - || typeHasCallOrConstructSignatures(rightType) - || isTypeSubtypeOf(rightType, globalFunctionType))) - { - error( - right, - Diagnostics - .The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type - ); - } - return booleanType; - } - - function checkInExpression( - left: Expression, - right: Expression, - leftType: Type, - rightType: Type - ): Type { - if (leftType === silentNeverType - || rightType === silentNeverType) - { - return silentNeverType; - } - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - // TypeScript 1.0 spec (April 2014): 4.15.5 - // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, - // and the right operand to be of type Any, an object type, or a type parameter type. - // The result is always of the Boolean primitive type. - if (!(isTypeComparableTo(leftType, stringType) - || isTypeAssignableToKind( - leftType, - TypeFlags.NumberLike | TypeFlags.ESSymbolLike - ))) - { - error( - left, - Diagnostics - .The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol - ); - } - if (!allTypesAssignableToKind( - rightType, - TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive - )) { - error( - right, - Diagnostics - .The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter - ); - } - return booleanType; - } - - function checkObjectLiteralAssignment( - node: ObjectLiteralExpression, - sourceType: Type, - rightIsThis?: boolean - ): Type { - const properties = node.properties; - if (strictNullChecks && properties.length === 0) { - return checkNonNullType(sourceType, node); - } - for (let i = 0; i < properties.length; i++) { - checkObjectLiteralDestructuringPropertyAssignment( - node, - sourceType, - i, - properties, - rightIsThis - ); - } - return sourceType; - } - - /** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */ - function checkObjectLiteralDestructuringPropertyAssignment( - node: ObjectLiteralExpression, - objectLiteralType: Type, - propertyIndex: number, - allProperties?: NodeArray, - rightIsThis = false - ) { - const properties = node.properties; - const property = properties[propertyIndex]; - if (property.kind === SyntaxKind.PropertyAssignment - || property.kind === SyntaxKind.ShorthandPropertyAssignment) - { - const name = property.name; - const exprType = getLiteralTypeFromPropertyName(name); - if (isTypeUsableAsPropertyName(exprType)) { - const text = getPropertyNameFromType(exprType); - const prop = getPropertyOfType(objectLiteralType, text); - if (prop) { - markPropertyAsReferenced(prop, property, rightIsThis); - checkPropertyAccessibility( - property, /*isSuper*/ - false, - objectLiteralType, - prop - ); - } - } - const elementType = getIndexedAccessType( - objectLiteralType, - exprType, - name - ); - const type = getFlowTypeOfDestructuring(property, elementType); - return checkDestructuringAssignment( - property.kind === SyntaxKind.ShorthandPropertyAssignment - ? property - : property.initializer, - type - ); - } else if (property.kind === SyntaxKind.SpreadAssignment) { - if (propertyIndex < properties.length - 1) { - error( - property, - Diagnostics - .A_rest_element_must_be_last_in_a_destructuring_pattern - ); - } else { - if (languageVersion < ScriptTarget.ESNext) { - checkExternalEmitHelpers( - property, - ExternalEmitHelpers.Rest - ); - } - const nonRestNames: PropertyName[] = []; - if (allProperties) { - for (const otherProperty of allProperties) { - if (!isSpreadAssignment(otherProperty)) { - nonRestNames.push(otherProperty.name); - } - } - } - const type = getRestType( - objectLiteralType, - nonRestNames, - objectLiteralType.symbol - ); - checkGrammarForDisallowedTrailingComma( - allProperties, - Diagnostics - .A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma - ); - return checkDestructuringAssignment( - property.expression, - type - ); - } - } else { - error(property, Diagnostics.Property_assignment_expected); - } - } - - function checkArrayLiteralAssignment( - node: ArrayLiteralExpression, - sourceType: Type, - checkMode?: CheckMode - ): Type { - const elements = node.elements; - if (languageVersion < ScriptTarget.ES2015 - && compilerOptions.downlevelIteration) - { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Read); - } - // This elementType will be used if the specific property corresponding to this index is not - // present (aka the tuple element property). This call also checks that the parentType is in - // fact an iterable or array (depending on target language). - const elementType = - checkIteratedTypeOrElementType( - IterationUse.Destructuring, - sourceType, - undefinedType, - node - ) || errorType; - for (let i = 0; i < elements.length; i++) { - checkArrayLiteralDestructuringElementAssignment( - node, - sourceType, - i, - elementType, - checkMode - ); - } - return sourceType; - } - - function checkArrayLiteralDestructuringElementAssignment( - node: ArrayLiteralExpression, - sourceType: Type, - elementIndex: number, - elementType: Type, - checkMode?: CheckMode - ) { - const elements = node.elements; - const element = elements[elementIndex]; - if (element.kind !== SyntaxKind.OmittedExpression) { - if (element.kind !== SyntaxKind.SpreadElement) { - const indexType = getLiteralType(elementIndex); - if (isArrayLikeType(sourceType)) { - // We create a synthetic expression so that getIndexedAccessType doesn't get confused - // when the element is a SyntaxKind.ElementAccessExpression. - const accessFlags = hasDefaultValue(element) - ? AccessFlags.NoTupleBoundsCheck - : 0; - const elementType = - getIndexedAccessTypeOrUndefined( - sourceType, - indexType, - createSyntheticExpression(element, indexType), - accessFlags - ) || errorType; - const assignedType = hasDefaultValue(element) - ? getTypeWithFacts( - elementType, - TypeFacts.NEUndefined - ) - : elementType; - const type = getFlowTypeOfDestructuring( - element, - assignedType - ); - return checkDestructuringAssignment( - element, - type, - checkMode - ); - } - return checkDestructuringAssignment( - element, - elementType, - checkMode - ); - } - if (elementIndex < elements.length - 1) { - error( - element, - Diagnostics - .A_rest_element_must_be_last_in_a_destructuring_pattern - ); - } else { - const restExpression = ( element) - .expression; - if (restExpression.kind === SyntaxKind.BinaryExpression - && ( restExpression).operatorToken - .kind === SyntaxKind.EqualsToken) - { - error( - ( restExpression).operatorToken, - Diagnostics - .A_rest_element_cannot_have_an_initializer - ); - } else { - checkGrammarForDisallowedTrailingComma( - node.elements, - Diagnostics - .A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma - ); - const type = everyType(sourceType, isTupleType) - ? mapType( - sourceType, - t => sliceTupleType( - t, - elementIndex - ) - ) - : createArrayType(elementType); - return checkDestructuringAssignment( - restExpression, - type, - checkMode - ); - } - } - } - return undefined; - } - - function checkDestructuringAssignment( - exprOrAssignment: Expression | ShorthandPropertyAssignment, - sourceType: Type, - checkMode?: CheckMode, - rightIsThis?: boolean - ): Type { - let target: Expression; - if (exprOrAssignment.kind - === SyntaxKind.ShorthandPropertyAssignment) - { - const prop = exprOrAssignment; - if (prop.objectAssignmentInitializer) { - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - if (strictNullChecks - && !(getFalsyFlags(checkExpression(prop - .objectAssignmentInitializer)) - & TypeFlags.Undefined)) - { - sourceType = getTypeWithFacts( - sourceType, - TypeFacts.NEUndefined - ); - } - checkBinaryLikeExpression( - prop.name, - prop.equalsToken!, - prop.objectAssignmentInitializer, - checkMode - ); - } - target = ( exprOrAssignment).name; - } else { - target = exprOrAssignment; - } - - if (target.kind === SyntaxKind.BinaryExpression - && ( target).operatorToken.kind - === SyntaxKind.EqualsToken) - { - checkBinaryExpression( target, checkMode); - target = ( target).left; - } - if (target.kind === SyntaxKind.ObjectLiteralExpression) { - return checkObjectLiteralAssignment( - target, - sourceType, - rightIsThis - ); - } - if (target.kind === SyntaxKind.ArrayLiteralExpression) { - return checkArrayLiteralAssignment( - target, - sourceType, - checkMode - ); - } - return checkReferenceAssignment(target, sourceType, checkMode); - } - - function checkReferenceAssignment( - target: Expression, - sourceType: Type, - checkMode?: CheckMode - ): Type { - const targetType = checkExpression(target, checkMode); - const error = target.parent.kind === SyntaxKind.SpreadAssignment - ? Diagnostics - .The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access - : Diagnostics - .The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access; - const optionalError = - target.parent.kind === SyntaxKind.SpreadAssignment - ? Diagnostics - .The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access - : Diagnostics - .The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access; - if (checkReferenceExpression(target, error, optionalError)) { - checkTypeAssignableToAndOptionallyElaborate( - sourceType, - targetType, - target, - target - ); - } - if (isPrivateIdentifierPropertyAccessExpression(target)) { - checkExternalEmitHelpers( - target.parent, - ExternalEmitHelpers.ClassPrivateFieldSet - ); - } - return sourceType; - } - - /** - * This is a *shallow* check: An expression is side-effect-free if the - * evaluation of the expression *itself* cannot produce side effects. - * For example, x++ / 3 is side-effect free because the / operator - * does not have side effects. - * The intent is to "smell test" an expression for correctness in positions where - * its value is discarded (e.g. the left side of the comma operator). - */ - function isSideEffectFree(node: Node): boolean { - node = skipParentheses(node); - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.TemplateExpression: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ClassExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.NonNullExpression: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxElement: - return true; - - case SyntaxKind.ConditionalExpression: - return isSideEffectFree((node as ConditionalExpression) - .whenTrue) - && isSideEffectFree((node as ConditionalExpression) - .whenFalse); - - case SyntaxKind.BinaryExpression: - if (isAssignmentOperator((node as BinaryExpression) - .operatorToken.kind)) - { - return false; - } - return isSideEffectFree((node as BinaryExpression).left) - && isSideEffectFree((node as BinaryExpression).right); - - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - // Unary operators ~, !, +, and - have no side effects. - // The rest do. - switch ((node as PrefixUnaryExpression).operator) { - case SyntaxKind.ExclamationToken: - case SyntaxKind.PlusToken: - case SyntaxKind.MinusToken: - case SyntaxKind.TildeToken: - return true; - } - return false; - - // Some forms listed here for clarity - - case SyntaxKind.VoidExpression: // Explicit opt-out - case SyntaxKind - .TypeAssertionExpression: // Not SEF, but can produce useful type warnings - case SyntaxKind - .AsExpression: // Not SEF, but can produce useful type warnings - default: - return false; - } - } - - function isTypeEqualityComparableTo(source: Type, target: Type) { - return (target.flags & TypeFlags.Nullable) !== 0 - || isTypeComparableTo(source, target); - } - - function checkBinaryExpression( - node: BinaryExpression, - checkMode?: CheckMode - ) { - if (isInJSFile(node) && getAssignedExpandoInitializer(node)) { - return checkExpression(node.right, checkMode); - } - checkGrammarNullishCoalesceWithLogicalExpression(node); - return checkBinaryLikeExpression( - node.left, - node.operatorToken, - node.right, - checkMode, - node - ); - } - - function checkGrammarNullishCoalesceWithLogicalExpression(node: - BinaryExpression) - { - const { left, operatorToken, right } = node; - if (operatorToken.kind === SyntaxKind.QuestionQuestionToken) { - if (isBinaryExpression(left) - && (left.operatorToken.kind === SyntaxKind.BarBarToken - || left.operatorToken.kind - === SyntaxKind.AmpersandAmpersandToken)) - { - grammarErrorOnNode( - left, - Diagnostics - ._0_and_1_operations_cannot_be_mixed_without_parentheses, - tokenToString(left.operatorToken.kind), - tokenToString(operatorToken.kind) - ); - } - if (isBinaryExpression(right) - && (right.operatorToken.kind === SyntaxKind.BarBarToken - || right.operatorToken.kind - === SyntaxKind.AmpersandAmpersandToken)) - { - grammarErrorOnNode( - right, - Diagnostics - ._0_and_1_operations_cannot_be_mixed_without_parentheses, - tokenToString(right.operatorToken.kind), - tokenToString(operatorToken.kind) - ); - } - } - } - - function checkBinaryLikeExpression( - left: Expression, - operatorToken: Node, - right: Expression, - checkMode?: CheckMode, - errorNode?: Node - ): Type { - const operator = operatorToken.kind; - if (operator === SyntaxKind.EqualsToken - && (left.kind === SyntaxKind.ObjectLiteralExpression - || left.kind === SyntaxKind.ArrayLiteralExpression)) - { - return checkDestructuringAssignment( - left, - checkExpression(right, checkMode), - checkMode, - right.kind === SyntaxKind.ThisKeyword - ); - } - let leftType: Type; - if (operator === SyntaxKind.AmpersandAmpersandToken - || operator === SyntaxKind.BarBarToken - || operator === SyntaxKind.QuestionQuestionToken) - { - leftType = checkTruthinessExpression(left, checkMode); - } else { - leftType = checkExpression(left, checkMode); - } - - let rightType = checkExpression(right, checkMode); - switch (operator) { - case SyntaxKind.AsteriskToken: - case SyntaxKind.AsteriskAsteriskToken: - case SyntaxKind.AsteriskEqualsToken: - case SyntaxKind.AsteriskAsteriskEqualsToken: - case SyntaxKind.SlashToken: - case SyntaxKind.SlashEqualsToken: - case SyntaxKind.PercentToken: - case SyntaxKind.PercentEqualsToken: - case SyntaxKind.MinusToken: - case SyntaxKind.MinusEqualsToken: - case SyntaxKind.LessThanLessThanToken: - case SyntaxKind.LessThanLessThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanToken: - case SyntaxKind.GreaterThanGreaterThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - case SyntaxKind.BarToken: - case SyntaxKind.BarEqualsToken: - case SyntaxKind.CaretToken: - case SyntaxKind.CaretEqualsToken: - case SyntaxKind.AmpersandToken: - case SyntaxKind.AmpersandEqualsToken: - if (leftType === silentNeverType - || rightType === silentNeverType) - { - return silentNeverType; - } - - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - - let suggestedOperator: SyntaxKind | undefined; - // if a user tries to apply a bitwise operator to 2 boolean operands - // try and return them a helpful suggestion - if ((leftType.flags & TypeFlags.BooleanLike) - && (rightType.flags & TypeFlags.BooleanLike) - && (suggestedOperator = getSuggestedBooleanOperator(operatorToken - .kind)) !== undefined) - { - error( - errorNode || operatorToken, - Diagnostics - .The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, - tokenToString(operatorToken.kind), - tokenToString(suggestedOperator) - ); - return numberType; - } else { - // otherwise just check each operand separately and report errors as normal - const leftOk = checkArithmeticOperandType( - left, - leftType, - Diagnostics - .The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ - true - ); - const rightOk = checkArithmeticOperandType( - right, - rightType, - Diagnostics - .The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ - true - ); - let resultType: Type; - // If both are any or unknown, allow operation; assume it will resolve to number - if ((isTypeAssignableToKind( - leftType, - TypeFlags.AnyOrUnknown - ) - && isTypeAssignableToKind( - rightType, - TypeFlags.AnyOrUnknown - )) - // Or, if neither could be bigint, implicit coercion results in a number result - || !(maybeTypeOfKind( - leftType, - TypeFlags.BigIntLike - ) - || maybeTypeOfKind( - rightType, - TypeFlags.BigIntLike - ))) - { - resultType = numberType; - } // At least one is assignable to bigint, so check that both are - else if (bothAreBigIntLike(leftType, rightType)) { - switch (operator) { - case SyntaxKind - .GreaterThanGreaterThanGreaterThanToken: - case SyntaxKind - .GreaterThanGreaterThanGreaterThanEqualsToken: - reportOperatorError(); - } - resultType = bigintType; - } // Exactly one of leftType/rightType is assignable to bigint - else { - reportOperatorError(bothAreBigIntLike); - resultType = errorType; - } - if (leftOk && rightOk) { - checkAssignmentOperator(resultType); - } - return resultType; - } - case SyntaxKind.PlusToken: - case SyntaxKind.PlusEqualsToken: - if (leftType === silentNeverType - || rightType === silentNeverType) - { - return silentNeverType; - } - - if (!isTypeAssignableToKind(leftType, TypeFlags.StringLike) - && !isTypeAssignableToKind( - rightType, - TypeFlags.StringLike - )) - { - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - } - - let resultType: Type | undefined; - if (isTypeAssignableToKind( - leftType, - TypeFlags.NumberLike, /*strict*/ - true - ) - && isTypeAssignableToKind( - rightType, - TypeFlags.NumberLike, /*strict*/ - true - )) - { - // Operands of an enum type are treated as having the primitive type Number. - // If both operands are of the Number primitive type, the result is of the Number primitive type. - resultType = numberType; - } else if (isTypeAssignableToKind( - leftType, - TypeFlags.BigIntLike, /*strict*/ - true - ) - && isTypeAssignableToKind( - rightType, - TypeFlags.BigIntLike, /*strict*/ - true - )) - { - // If both operands are of the BigInt primitive type, the result is of the BigInt primitive type. - resultType = bigintType; - } else if (isTypeAssignableToKind( - leftType, - TypeFlags.StringLike, /*strict*/ - true - ) - || isTypeAssignableToKind( - rightType, - TypeFlags.StringLike, /*strict*/ - true - )) - { - // If one or both operands are of the String primitive type, the result is of the String primitive type. - resultType = stringType; - } else if (isTypeAny(leftType) || isTypeAny(rightType)) { - // Otherwise, the result is of type Any. - // NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we. - resultType = leftType === errorType - || rightType === errorType - ? errorType - : anyType; - } - - // Symbols are not allowed at all in arithmetic expressions - if (resultType - && !checkForDisallowedESSymbolOperand(operator)) - { - return resultType; - } - - if (!resultType) { - // Types that have a reasonably good chance of being a valid operand type. - // If both types have an awaited type of one of these, we'll assume the user - // might be missing an await without doing an exhaustive check that inserting - // await(s) will actually be a completely valid binary expression. - const closeEnoughKind = - TypeFlags.NumberLike | TypeFlags.BigIntLike - | TypeFlags.StringLike - | TypeFlags.AnyOrUnknown; - reportOperatorError(( - left, - right - ) => isTypeAssignableToKind(left, closeEnoughKind) - && isTypeAssignableToKind(right, closeEnoughKind)); - return anyType; - } - - if (operator === SyntaxKind.PlusEqualsToken) { - checkAssignmentOperator(resultType); - } - return resultType; - case SyntaxKind.LessThanToken: - case SyntaxKind.GreaterThanToken: - case SyntaxKind.LessThanEqualsToken: - case SyntaxKind.GreaterThanEqualsToken: - if (checkForDisallowedESSymbolOperand(operator)) { - leftType = getBaseTypeOfLiteralType(checkNonNullType( - leftType, - left - )); - rightType = getBaseTypeOfLiteralType(checkNonNullType( - rightType, - right - )); - reportOperatorErrorUnless(( - left, - right - ) => isTypeComparableTo(left, right) - || isTypeComparableTo(right, left) || ( - isTypeAssignableTo(left, numberOrBigIntType) - && isTypeAssignableTo( - right, - numberOrBigIntType - ) - )); - } - return booleanType; - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - reportOperatorErrorUnless(( - left, - right - ) => isTypeEqualityComparableTo(left, right) - || isTypeEqualityComparableTo(right, left)); - return booleanType; - - case SyntaxKind.InstanceOfKeyword: - return checkInstanceOfExpression( - left, - right, - leftType, - rightType - ); - case SyntaxKind.InKeyword: - return checkInExpression(left, right, leftType, rightType); - case SyntaxKind.AmpersandAmpersandToken: - return getTypeFacts(leftType) & TypeFacts.Truthy - ? getUnionType([extractDefinitelyFalsyTypes(strictNullChecks - ? leftType - : getBaseTypeOfLiteralType(rightType)), rightType]) - : leftType; - case SyntaxKind.BarBarToken: - return getTypeFacts(leftType) & TypeFacts.Falsy - ? getUnionType( - [removeDefinitelyFalsyTypes(leftType), rightType], - UnionReduction.Subtype - ) - : leftType; - case SyntaxKind.QuestionQuestionToken: - return getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull - ? getUnionType( - [getNonNullableType(leftType), rightType], - UnionReduction.Subtype - ) - : leftType; - case SyntaxKind.EqualsToken: - const declKind = isBinaryExpression(left.parent) - ? getAssignmentDeclarationKind(left.parent) - : AssignmentDeclarationKind.None; - checkAssignmentDeclaration(declKind, rightType); - if (isAssignmentDeclaration(declKind)) { - if (!(rightType.flags & TypeFlags.Object) - || declKind - !== AssignmentDeclarationKind.ModuleExports - && declKind !== AssignmentDeclarationKind.Prototype - && !isEmptyObjectType(rightType) - && !isFunctionObjectType(rightType as ObjectType) - && !(getObjectFlags(rightType) - & ObjectFlags.Class)) - { - // don't check assignability of module.exports=, C.prototype=, or expando types because they will necessarily be incomplete - checkAssignmentOperator(rightType); - } - return leftType; - } else { - checkAssignmentOperator(rightType); - return getRegularTypeOfObjectLiteral(rightType); - } - case SyntaxKind.CommaToken: - if (!compilerOptions.allowUnreachableCode - && isSideEffectFree(left) && !isEvalNode(right)) - { - error( - left, - Diagnostics - .Left_side_of_comma_operator_is_unused_and_has_no_side_effects - ); - } - return rightType; - - default: - return Debug.fail(); - } - - function bothAreBigIntLike(left: Type, right: Type): boolean { - return isTypeAssignableToKind(left, TypeFlags.BigIntLike) - && isTypeAssignableToKind(right, TypeFlags.BigIntLike); - } - - function checkAssignmentDeclaration( - kind: AssignmentDeclarationKind, - rightType: Type - ) { - if (kind === AssignmentDeclarationKind.ModuleExports) { - for (const prop of getPropertiesOfObjectType(rightType)) { - const propType = getTypeOfSymbol(prop); - if (propType.symbol - && propType.symbol.flags & SymbolFlags.Class) - { - const name = prop.escapedName; - const symbol = resolveName( - prop.valueDeclaration, - name, - SymbolFlags.Type, - undefined, - name, /*isUse*/ - false - ); - if (symbol - && symbol.declarations.some(isJSDocTypedefTag)) - { - grammarErrorOnNode( - symbol.declarations[0], - Diagnostics.Duplicate_identifier_0, - unescapeLeadingUnderscores(name) - ); - return grammarErrorOnNode( - prop.valueDeclaration, - Diagnostics.Duplicate_identifier_0, - unescapeLeadingUnderscores(name) - ); - } - } - } - } - } - - function isEvalNode(node: Expression) { - return node.kind === SyntaxKind.Identifier - && (node as Identifier).escapedText === 'eval'; - } - - // Return true if there was no error, false if there was an error. - function checkForDisallowedESSymbolOperand(operator: - SyntaxKind): boolean - { - const offendingSymbolOperand = - maybeTypeOfKind(leftType, TypeFlags.ESSymbolLike) - ? left - : maybeTypeOfKind(rightType, TypeFlags.ESSymbolLike) - ? right - : undefined; - - if (offendingSymbolOperand) { - error( - offendingSymbolOperand, - Diagnostics - .The_0_operator_cannot_be_applied_to_type_symbol, - tokenToString(operator) - ); - return false; - } - - return true; - } - - function getSuggestedBooleanOperator(operator: - SyntaxKind): SyntaxKind | undefined - { - switch (operator) { - case SyntaxKind.BarToken: - case SyntaxKind.BarEqualsToken: - return SyntaxKind.BarBarToken; - case SyntaxKind.CaretToken: - case SyntaxKind.CaretEqualsToken: - return SyntaxKind.ExclamationEqualsEqualsToken; - case SyntaxKind.AmpersandToken: - case SyntaxKind.AmpersandEqualsToken: - return SyntaxKind.AmpersandAmpersandToken; - default: - return undefined; - } - } - - function checkAssignmentOperator(valueType: Type): void { - if (produceDiagnostics && isAssignmentOperator(operator)) { - // TypeScript 1.0 spec (April 2014): 4.17 - // An assignment of the form - // VarExpr = ValueExpr - // requires VarExpr to be classified as a reference - // A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1) - // and the type of the non-compound operation to be assignable to the type of VarExpr. - - if (checkReferenceExpression( - left, - Diagnostics - .The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access, - Diagnostics - .The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access - ) - && (!isIdentifier(left) - || unescapeLeadingUnderscores(left.escapedText) - !== 'exports')) - { - // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported - checkTypeAssignableToAndOptionallyElaborate( - valueType, - leftType, - left, - right - ); - } - } - } - - function isAssignmentDeclaration(kind: AssignmentDeclarationKind) { - switch (kind) { - case AssignmentDeclarationKind.ModuleExports: - return true; - case AssignmentDeclarationKind.ExportsProperty: - case AssignmentDeclarationKind.Property: - case AssignmentDeclarationKind.Prototype: - case AssignmentDeclarationKind.PrototypeProperty: - case AssignmentDeclarationKind.ThisProperty: - const symbol = getSymbolOfNode(left); - const init = getAssignedExpandoInitializer(right); - return init && isObjectLiteralExpression(init) - && symbol && hasEntries(symbol.exports); - default: - return false; - } - } - - /** - * Returns true if an error is reported - */ - function reportOperatorErrorUnless(typesAreCompatible: ( - left: Type, - right: Type - ) => boolean): boolean { - if (!typesAreCompatible(leftType, rightType)) { - reportOperatorError(typesAreCompatible); - return true; - } - return false; - } - - function reportOperatorError(isRelated?: (left: Type, right: Type) - => boolean) { - let wouldWorkWithAwait = false; - const errNode = errorNode || operatorToken; - if (isRelated) { - const awaitedLeftType = getAwaitedType(leftType); - const awaitedRightType = getAwaitedType(rightType); - wouldWorkWithAwait = !(awaitedLeftType === leftType - && awaitedRightType === rightType) - && !!(awaitedLeftType && awaitedRightType) - && isRelated(awaitedLeftType, awaitedRightType); - } - - let effectiveLeft = leftType; - let effectiveRight = rightType; - if (!wouldWorkWithAwait && isRelated) { - [effectiveLeft, effectiveRight] = getBaseTypesIfUnrelated( - leftType, - rightType, - isRelated - ); - } - const [leftStr, rightStr] = getTypeNamesForErrorDisplay( - effectiveLeft, - effectiveRight - ); - if (!tryGiveBetterPrimaryError( - errNode, - wouldWorkWithAwait, - leftStr, - rightStr - )) { - errorAndMaybeSuggestAwait( - errNode, - wouldWorkWithAwait, - Diagnostics - .Operator_0_cannot_be_applied_to_types_1_and_2, - tokenToString(operatorToken.kind), - leftStr, - rightStr - ); - } - } - - function tryGiveBetterPrimaryError( - errNode: Node, - maybeMissingAwait: boolean, - leftStr: string, - rightStr: string - ) { - let typeName: string | undefined; - switch (operatorToken.kind) { - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.EqualsEqualsToken: - typeName = 'false'; - break; - case SyntaxKind.ExclamationEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - typeName = 'true'; - } - - if (typeName) { - return errorAndMaybeSuggestAwait( - errNode, - maybeMissingAwait, - Diagnostics - .This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, - typeName, - leftStr, - rightStr - ); - } - - return undefined; - } - } - - function getBaseTypesIfUnrelated( - leftType: Type, - rightType: Type, - isRelated: (left: Type, right: Type) => boolean - ): [Type, Type] { - let effectiveLeft = leftType; - let effectiveRight = rightType; - const leftBase = getBaseTypeOfLiteralType(leftType); - const rightBase = getBaseTypeOfLiteralType(rightType); - if (!isRelated(leftBase, rightBase)) { - effectiveLeft = leftBase; - effectiveRight = rightBase; - } - return [effectiveLeft, effectiveRight]; - } - - function checkYieldExpression(node: YieldExpression): Type { - // Grammar checking - if (produceDiagnostics) { - if (!(node.flags & NodeFlags.YieldContext)) { - grammarErrorOnFirstToken( - node, - Diagnostics - .A_yield_expression_is_only_allowed_in_a_generator_body - ); - } - - if (isInParameterInitializerBeforeContainingFunction(node)) { - error( - node, - Diagnostics - .yield_expressions_cannot_be_used_in_a_parameter_initializer - ); - } - } - - const func = getContainingFunction(node); - if (!func) return anyType; - const functionFlags = getFunctionFlags(func); - - if (!(functionFlags & FunctionFlags.Generator)) { - // If the user's code is syntactically correct, the func should always have a star. After all, we are in a yield context. - return anyType; - } - - const isAsync = (functionFlags & FunctionFlags.Async) !== 0; - if (node.asteriskToken) { - // Async generator functions prior to ESNext require the __await, __asyncDelegator, - // and __asyncValues helpers - if (isAsync && languageVersion < ScriptTarget.ESNext) { - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.AsyncDelegatorIncludes - ); - } - - // Generator functions prior to ES2015 require the __values helper - if (!isAsync && languageVersion < ScriptTarget.ES2015 - && compilerOptions.downlevelIteration) - { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Values); - } - } - - // There is no point in doing an assignability check if the function - // has no explicit return type because the return type is directly computed - // from the yield expressions. - const returnType = getReturnTypeFromAnnotation(func); - const iterationTypes = returnType - && getIterationTypesOfGeneratorFunctionReturnType( - returnType, - isAsync - ); - const signatureYieldType = iterationTypes - && iterationTypes.yieldType || anyType; - const signatureNextType = iterationTypes && iterationTypes.nextType - || anyType; - const resolvedSignatureNextType = isAsync - ? getAwaitedType(signatureNextType) || anyType - : signatureNextType; - const yieldExpressionType = node.expression - ? checkExpression(node.expression) - : undefinedWideningType; - const yieldedType = getYieldedTypeOfYieldExpression( - node, - yieldExpressionType, - resolvedSignatureNextType, - isAsync - ); - if (returnType && yieldedType) { - checkTypeAssignableToAndOptionallyElaborate( - yieldedType, - signatureYieldType, - node.expression || node, - node.expression - ); - } - - if (node.asteriskToken) { - const use = isAsync - ? IterationUse.AsyncYieldStar - : IterationUse.YieldStar; - return getIterationTypeOfIterable( - use, - IterationTypeKind.Return, - yieldExpressionType, - node.expression - ) - || anyType; - } else if (returnType) { - return getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Next, - returnType, - isAsync - ) - || anyType; - } - - return getContextualIterationType(IterationTypeKind.Next, func) - || anyType; - } - - function checkConditionalExpression( - node: ConditionalExpression, - checkMode?: CheckMode - ): Type { - checkTruthinessExpression(node.condition); - const type1 = checkExpression(node.whenTrue, checkMode); - const type2 = checkExpression(node.whenFalse, checkMode); - return getUnionType([type1, type2], UnionReduction.Subtype); - } - - function checkTemplateExpression(node: TemplateExpression): Type { - // We just want to check each expressions, but we are unconcerned with - // the type of each expression, as any value may be coerced into a string. - // It is worth asking whether this is what we really want though. - // A place where we actually *are* concerned with the expressions' types are - // in tagged templates. - forEach(node.templateSpans, templateSpan => { - if (maybeTypeOfKind( - checkExpression(templateSpan.expression), - TypeFlags.ESSymbolLike - )) { - error( - templateSpan.expression, - Diagnostics - .Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String - ); - } - }); - - return stringType; - } - - function getContextNode(node: Expression): Node { - if (node.kind === SyntaxKind.JsxAttributes - && !isJsxSelfClosingElement(node.parent)) - { - return node.parent - .parent; // Needs to be the root JsxElement, so it encompasses the attributes _and_ the children (which are essentially part of the attributes) - } - return node; - } - - function checkExpressionWithContextualType( - node: Expression, - contextualType: Type, - inferenceContext: InferenceContext | undefined, - checkMode: CheckMode - ): Type { - const context = getContextNode(node); - const saveContextualType = context.contextualType; - const saveInferenceContext = context.inferenceContext; - context.contextualType = contextualType; - context.inferenceContext = inferenceContext; - const type = checkExpression( - node, - checkMode | CheckMode.Contextual - | (inferenceContext ? CheckMode.Inferential : 0) - ); - // We strip literal freshness when an appropriate contextual type is present such that contextually typed - // literals always preserve their literal types (otherwise they might widen during type inference). An alternative - // here would be to not mark contextually typed literals as fresh in the first place. - const result = - maybeTypeOfKind(type, TypeFlags.Literal) - && isLiteralOfContextualType( - type, - instantiateContextualType(contextualType, node) - ) - ? getRegularTypeOfLiteralType(type) - : type; - context.contextualType = saveContextualType; - context.inferenceContext = saveInferenceContext; - return result; - } - - function checkExpressionCached( - node: Expression | QualifiedName, - checkMode?: CheckMode - ): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - if (checkMode && checkMode !== CheckMode.Normal) { - return checkExpression(node, checkMode); - } - // When computing a type that we're going to cache, we need to ignore any ongoing control flow - // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart - // to the top of the stack ensures all transient types are computed from a known point. - const saveFlowLoopStart = flowLoopStart; - const saveFlowTypeCache = flowTypeCache; - flowLoopStart = flowLoopCount; - flowTypeCache = undefined; - links.resolvedType = checkExpression(node, checkMode); - flowTypeCache = saveFlowTypeCache; - flowLoopStart = saveFlowLoopStart; - } - return links.resolvedType; - } - - function isTypeAssertion(node: Expression) { - node = skipParentheses(node); - return node.kind === SyntaxKind.TypeAssertionExpression - || node.kind === SyntaxKind.AsExpression; - } - - function checkDeclarationInitializer(declaration: - HasExpressionInitializer) - { - const initializer = getEffectiveInitializer(declaration)!; - const type = getQuickTypeOfExpression(initializer) - || checkExpressionCached(initializer); - const padded = - isParameter(declaration) - && declaration.name.kind === SyntaxKind.ArrayBindingPattern - && isTupleType(type) && !type.target.hasRestElement - && getTypeReferenceArity(type) - < declaration.name.elements.length - ? padTupleType(type, declaration.name) - : type; - const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const - || isDeclarationReadonly(declaration) - || isTypeAssertion(initializer) - || isLiteralOfContextualType( - padded, - getContextualType(initializer) - ) - ? padded - : getWidenedLiteralType(padded); - if (isInJSFile(declaration)) { - if (widened.flags & TypeFlags.Nullable) { - reportImplicitAny(declaration, anyType); - return anyType; - } else if (isEmptyArrayLiteralType(widened)) { - reportImplicitAny(declaration, anyArrayType); - return anyArrayType; - } - } - return widened; - } - - function padTupleType( - type: TupleTypeReference, - pattern: ArrayBindingPattern - ) { - const patternElements = pattern.elements; - const arity = getTypeReferenceArity(type); - const elementTypes = arity ? getTypeArguments(type).slice() : []; - for (let i = arity; i < patternElements.length; i++) { - const e = patternElements[i]; - if (i < patternElements.length - 1 - || !(e.kind === SyntaxKind.BindingElement - && e.dotDotDotToken)) - { - elementTypes - .push(!isOmittedExpression(e) && hasDefaultValue(e) - ? getTypeFromBindingElement( - e, /*includePatternInType*/ - false, /*reportErrors*/ - false - ) - : anyType); - if (!isOmittedExpression(e) && !hasDefaultValue(e)) { - reportImplicitAny(e, anyType); - } - } - } - return createTupleType( - elementTypes, - type.target.minLength, /*hasRestElement*/ - false, - type.target.readonly - ); - } - - function isLiteralOfContextualType( - candidateType: Type, - contextualType: Type | undefined - ): boolean { - if (contextualType) { - if (contextualType.flags & TypeFlags.UnionOrIntersection) { - const types = ( contextualType).types; - return some( - types, - t => isLiteralOfContextualType(candidateType, t) - ); - } - if (contextualType.flags - & TypeFlags.InstantiableNonPrimitive) - { - // If the contextual type is a type variable constrained to a primitive type, consider - // this a literal context for literals of that primitive type. For example, given a - // type parameter 'T extends string', infer string literal types for T. - const constraint = getBaseConstraintOfType(contextualType) - || unknownType; - return maybeTypeOfKind(constraint, TypeFlags.String) - && maybeTypeOfKind( - candidateType, - TypeFlags.StringLiteral - ) - || maybeTypeOfKind(constraint, TypeFlags.Number) - && maybeTypeOfKind( - candidateType, - TypeFlags.NumberLiteral - ) - || maybeTypeOfKind(constraint, TypeFlags.BigInt) - && maybeTypeOfKind( - candidateType, - TypeFlags.BigIntLiteral - ) - || maybeTypeOfKind(constraint, TypeFlags.ESSymbol) - && maybeTypeOfKind( - candidateType, - TypeFlags.UniqueESSymbol - ) - || isLiteralOfContextualType( - candidateType, - constraint - ); - } - // If the contextual type is a literal of a particular primitive type, we consider this a - // literal context for all literals of that primitive type. - return !!(contextualType.flags - & (TypeFlags.StringLiteral | TypeFlags.Index) - && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) - || contextualType.flags & TypeFlags.NumberLiteral - && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) - || contextualType.flags & TypeFlags.BigIntLiteral - && maybeTypeOfKind(candidateType, TypeFlags.BigIntLiteral) - || contextualType.flags & TypeFlags.BooleanLiteral - && maybeTypeOfKind(candidateType, TypeFlags.BooleanLiteral) - || contextualType.flags & TypeFlags.UniqueESSymbol - && maybeTypeOfKind( - candidateType, - TypeFlags.UniqueESSymbol - )); - } - return false; - } - - function isConstContext(node: Expression): boolean { - const parent = node.parent; - return isAssertionExpression(parent) - && isConstTypeReference(parent.type) - || (isParenthesizedExpression(parent) - || isArrayLiteralExpression(parent) - || isSpreadElement(parent)) && isConstContext(parent) - || (isPropertyAssignment(parent) - || isShorthandPropertyAssignment(parent)) - && isConstContext(parent.parent); - } - - function checkExpressionForMutableLocation( - node: Expression, - checkMode: CheckMode | undefined, - contextualType?: Type, - forceTuple?: boolean - ): Type { - const type = checkExpression(node, checkMode, forceTuple); - return isConstContext(node) - ? getRegularTypeOfLiteralType(type) - : isTypeAssertion(node) - ? type - : getWidenedLiteralLikeTypeForContextualType( - type, - instantiateContextualType( - arguments.length === 2 - ? getContextualType(node) - : contextualType, - node - ) - ); - } - - function checkPropertyAssignment( - node: PropertyAssignment, - checkMode?: CheckMode - ): Type { - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - } - - return checkExpressionForMutableLocation( - node.initializer, - checkMode - ); - } - - function checkObjectLiteralMethod( - node: MethodDeclaration, - checkMode?: CheckMode - ): Type { - // Grammar checking - checkGrammarMethod(node); - - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - } - - const uninstantiatedType = - checkFunctionExpressionOrObjectLiteralMethod(node, checkMode); - return instantiateTypeWithSingleGenericCallSignature( - node, - uninstantiatedType, - checkMode - ); - } - - function instantiateTypeWithSingleGenericCallSignature( - node: Expression | MethodDeclaration | QualifiedName, - type: Type, - checkMode?: CheckMode - ) { - if (checkMode - && checkMode - & (CheckMode.Inferential | CheckMode.SkipGenericFunctions)) - { - const callSignature = getSingleSignature( - type, - SignatureKind.Call, /*allowMembers*/ - true - ); - const constructSignature = getSingleSignature( - type, - SignatureKind.Construct, /*allowMembers*/ - true - ); - const signature = callSignature || constructSignature; - if (signature && signature.typeParameters) { - const contextualType = getApparentTypeOfContextualType( - node, - ContextFlags.NoConstraints - ); - if (contextualType) { - const contextualSignature = getSingleSignature( - getNonNullableType(contextualType), - callSignature - ? SignatureKind.Call - : SignatureKind.Construct, /*allowMembers*/ - false - ); - if (contextualSignature - && !contextualSignature.typeParameters) - { - if (checkMode & CheckMode.SkipGenericFunctions) { - skippedGenericFunction(node, checkMode); - return anyFunctionType; - } - const context = getInferenceContext(node)!; - // We have an expression that is an argument of a generic function for which we are performing - // type argument inference. The expression is of a function type with a single generic call - // signature and a contextual function type with a single non-generic call signature. Now check - // if the outer function returns a function type with a single non-generic call signature and - // if some of the outer function type parameters have no inferences so far. If so, we can - // potentially add inferred type parameters to the outer function return type. - const returnType = context.signature - && getReturnTypeOfSignature(context.signature); - const returnSignature = returnType - && getSingleCallOrConstructSignature(returnType); - if (returnSignature - && !returnSignature.typeParameters - && !every( - context.inferences, - hasInferenceCandidates - )) - { - // Instantiate the signature with its own type parameters as type arguments, possibly - // renaming the type parameters to ensure they have unique names. - const uniqueTypeParameters = - getUniqueTypeParameters( - context, - signature.typeParameters - ); - const instantiatedSignature = - getSignatureInstantiationWithoutFillingInTypeArguments( - signature, - uniqueTypeParameters - ); - // Infer from the parameters of the instantiated signature to the parameters of the - // contextual signature starting with an empty set of inference candidates. - const inferences = map( - context.inferences, - info => createInferenceInfo(info - .typeParameter) - ); - applyToParameterTypes( - instantiatedSignature, - contextualSignature, - (source, target) => { - inferTypes( - inferences, - source, - target, /*priority*/ - 0, /*contravariant*/ - true - ); - } - ); - if (some(inferences, hasInferenceCandidates)) { - // We have inference candidates, indicating that one or more type parameters are referenced - // in the parameter types of the contextual signature. Now also infer from the return type. - applyToReturnTypes( - instantiatedSignature, - contextualSignature, - (source, target) => { - inferTypes( - inferences, - source, - target - ); - } - ); - // If the type parameters for which we produced candidates do not have any inferences yet, - // we adopt the new inference candidates and add the type parameters of the expression type - // to the set of inferred type parameters for the outer function return type. - if (!hasOverlappingInferences( - context.inferences, - inferences - )) { - mergeInferences( - context.inferences, - inferences - ); - context - .inferredTypeParameters = concatenate( - context.inferredTypeParameters, - uniqueTypeParameters - ); - return getOrCreateTypeFromSignature(instantiatedSignature); - } - } - } - return getOrCreateTypeFromSignature(instantiateSignatureInContextOf( - signature, - contextualSignature, - context - )); - } - } - } - } - return type; - } - - function skippedGenericFunction(node: Node, checkMode: CheckMode) { - if (checkMode & CheckMode.Inferential) { - // We have skipped a generic function during inferential typing. Obtain the inference context and - // indicate this has occurred such that we know a second pass of inference is be needed. - const context = getInferenceContext(node)!; - context.flags |= InferenceFlags.SkippedGenericFunction; - } - } - - function hasInferenceCandidates(info: InferenceInfo) { - return !!(info.candidates || info.contraCandidates); - } - - function hasOverlappingInferences( - a: InferenceInfo[], - b: InferenceInfo[] - ) { - for (let i = 0; i < a.length; i++) { - if (hasInferenceCandidates(a[i]) - && hasInferenceCandidates(b[i])) - { - return true; - } - } - return false; - } - - function mergeInferences( - target: InferenceInfo[], - source: InferenceInfo[] - ) { - for (let i = 0; i < target.length; i++) { - if (!hasInferenceCandidates(target[i]) - && hasInferenceCandidates(source[i])) - { - target[i] = source[i]; - } - } - } - - function getUniqueTypeParameters( - context: InferenceContext, - typeParameters: readonly TypeParameter[] - ): readonly TypeParameter[] { - const result: TypeParameter[] = []; - let oldTypeParameters: TypeParameter[] | undefined; - let newTypeParameters: TypeParameter[] | undefined; - for (const tp of typeParameters) { - const name = tp.symbol.escapedName; - if (hasTypeParameterByName( - context.inferredTypeParameters, - name - ) || hasTypeParameterByName(result, name)) { - const newName = getUniqueTypeParameterName( - concatenate(context.inferredTypeParameters, result), - name - ); - const symbol = createSymbol( - SymbolFlags.TypeParameter, - newName - ); - const newTypeParameter = createTypeParameter(symbol); - newTypeParameter.target = tp; - oldTypeParameters = append(oldTypeParameters, tp); - newTypeParameters = append( - newTypeParameters, - newTypeParameter - ); - result.push(newTypeParameter); - } else { - result.push(tp); - } - } - if (newTypeParameters) { - const mapper = createTypeMapper( - oldTypeParameters!, - newTypeParameters - ); - for (const tp of newTypeParameters) { - tp.mapper = mapper; - } - } - return result; - } - - function hasTypeParameterByName( - typeParameters: readonly TypeParameter[] | undefined, - name: __String - ) { - return some(typeParameters, tp => tp.symbol.escapedName === name); - } - - function getUniqueTypeParameterName( - typeParameters: readonly TypeParameter[], - baseName: __String - ) { - let len = ( baseName).length; - while (len > 1 - && ( baseName).charCodeAt(len - 1) >= CharacterCodes._0 - && ( baseName).charCodeAt(len - 1) - <= CharacterCodes._9) - { - len--; - } - const s = ( baseName).slice(0, len); - for (let index = 1; true; index++) { - const augmentedName = <__String> (s + index); - if (!hasTypeParameterByName(typeParameters, augmentedName)) { - return augmentedName; - } - } - } - - function getReturnTypeOfSingleNonGenericCallSignature(funcType: Type) { - const signature = getSingleCallSignature(funcType); - if (signature && !signature.typeParameters) { - return getReturnTypeOfSignature(signature); - } - } - - function getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr: - CallChain) - { - const funcType = checkExpression(expr.expression); - const nonOptionalType = getOptionalExpressionType( - funcType, - expr.expression - ); - const returnType = - getReturnTypeOfSingleNonGenericCallSignature(funcType); - return returnType - && propagateOptionalTypeMarker( - returnType, - expr, - nonOptionalType !== funcType - ); - } - - /** - * Returns the type of an expression. Unlike checkExpression, this function is simply concerned - * with computing the type and may not fully check all contained sub-expressions for errors. - */ - function getTypeOfExpression(node: Expression) { - // Don't bother caching types that require no flow analysis and are quick to compute. - const quickType = getQuickTypeOfExpression(node); - if (quickType) { - return quickType; - } - // If a type has been cached for the node, return it. - if (node.flags & NodeFlags.TypeCached && flowTypeCache) { - const cachedType = flowTypeCache[getNodeId(node)]; - if (cachedType) { - return cachedType; - } - } - const startInvocationCount = flowInvocationCount; - const type = checkExpression(node); - // If control flow analysis was required to determine the type, it is worth caching. - if (flowInvocationCount !== startInvocationCount) { - const cache = flowTypeCache || (flowTypeCache = []); - cache[getNodeId(node)] = type; - node.flags |= NodeFlags.TypeCached; - } - return type; - } - - function getQuickTypeOfExpression(node: Expression) { - const expr = skipParentheses(node); - // Optimize for the common case of a call to a function with a single non-generic call - // signature where we can just fetch the return type without checking the arguments. - if (isCallExpression(expr) - && expr.expression.kind !== SyntaxKind.SuperKeyword - && !isRequireCall( - expr, /*checkArgumentIsStringLiteralLike*/ - true - ) && !isSymbolOrSymbolForCall(expr)) - { - const type = isCallChain(expr) - ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) - : getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr - .expression)); - if (type) { - return type; - } - } else if (isAssertionExpression(expr) - && !isConstTypeReference(expr.type)) - { - return getTypeFromTypeNode(( expr).type); - } else if (node.kind === SyntaxKind.NumericLiteral - || node.kind === SyntaxKind.StringLiteral - || node.kind === SyntaxKind.TrueKeyword - || node.kind === SyntaxKind.FalseKeyword) - { - return checkExpression(node); - } - return undefined; - } - - /** - * Returns the type of an expression. Unlike checkExpression, this function is simply concerned - * with computing the type and may not fully check all contained sub-expressions for errors. - * It is intended for uses where you know there is no contextual type, - * and requesting the contextual type might cause a circularity or other bad behaviour. - * It sets the contextual type of the node to any before calling getTypeOfExpression. - */ - function getContextFreeTypeOfExpression(node: Expression) { - const links = getNodeLinks(node); - if (links.contextFreeType) { - return links.contextFreeType; - } - const saveContextualType = node.contextualType; - node.contextualType = anyType; - const type = links.contextFreeType = checkExpression( - node, - CheckMode.SkipContextSensitive - ); - node.contextualType = saveContextualType; - return type; - } - - function checkExpression( - node: Expression | QualifiedName, - checkMode?: CheckMode, - forceTuple?: boolean - ): Type { - const saveCurrentNode = currentNode; - currentNode = node; - instantiationCount = 0; - const uninstantiatedType = checkExpressionWorker( - node, - checkMode, - forceTuple - ); - const type = instantiateTypeWithSingleGenericCallSignature( - node, - uninstantiatedType, - checkMode - ); - if (isConstEnumObjectType(type)) { - checkConstEnumAccess(node, type); - } - currentNode = saveCurrentNode; - return type; - } - - function checkConstEnumAccess( - node: Expression | QualifiedName, - type: Type - ) { - // enum object type for const enums are only permitted in: - // - 'left' in property access - // - 'object' in indexed access - // - target in rhs of import statement - const ok = - (node.parent.kind === SyntaxKind.PropertyAccessExpression - && ( node.parent).expression - === node) - || (node.parent.kind === SyntaxKind.ElementAccessExpression - && ( node.parent).expression - === node) - || ((node.kind === SyntaxKind.Identifier - || node.kind === SyntaxKind.QualifiedName) - && isInRightSideOfImportOrExportAssignment( node) - || (node.parent.kind === SyntaxKind.TypeQuery - && ( node.parent).exprName - === node)) - || (node.parent.kind - === SyntaxKind - .ExportSpecifier); // We allow reexporting const enums - - if (!ok) { - error( - node, - Diagnostics - .const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query - ); - } - - if (compilerOptions.isolatedModules) { - Debug.assert(!!(type.symbol.flags & SymbolFlags.ConstEnum)); - const constEnumDeclaration = type.symbol - .valueDeclaration as EnumDeclaration; - if (constEnumDeclaration.flags & NodeFlags.Ambient) { - error( - node, - Diagnostics - .Cannot_access_ambient_const_enums_when_the_isolatedModules_flag_is_provided - ); - } - } - } - - function checkParenthesizedExpression( - node: ParenthesizedExpression, - checkMode?: CheckMode - ): Type { - const tag = isInJSFile(node) ? getJSDocTypeTag(node) : undefined; - if (tag) { - return checkAssertionWorker( - tag, - tag.typeExpression.type, - node.expression, - checkMode - ); - } - return checkExpression(node.expression, checkMode); - } - - function checkExpressionWorker( - node: Expression | QualifiedName, - checkMode: CheckMode | undefined, - forceTuple?: boolean - ): Type { - const kind = node.kind; - if (cancellationToken) { - // Only bother checking on a few construct kinds. We don't want to be excessively - // hitting the cancellation token on every node we check. - switch (kind) { - case SyntaxKind.ClassExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - cancellationToken.throwIfCancellationRequested(); - } - } - switch (kind) { - case SyntaxKind.Identifier: - return checkIdentifier( node); - case SyntaxKind.ThisKeyword: - return checkThisExpression(node); - case SyntaxKind.SuperKeyword: - return checkSuperExpression(node); - case SyntaxKind.NullKeyword: - return nullWideningType; - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.StringLiteral: - return getFreshTypeOfLiteralType(getLiteralType((node as StringLiteralLike) - .text)); - case SyntaxKind.NumericLiteral: - checkGrammarNumericLiteral(node as NumericLiteral); - return getFreshTypeOfLiteralType(getLiteralType(+(node as NumericLiteral) - .text)); - case SyntaxKind.BigIntLiteral: - checkGrammarBigIntLiteral(node as BigIntLiteral); - return getFreshTypeOfLiteralType(getBigIntLiteralType(node as BigIntLiteral)); - case SyntaxKind.TrueKeyword: - return trueType; - case SyntaxKind.FalseKeyword: - return falseType; - case SyntaxKind.TemplateExpression: - return checkTemplateExpression( node); - case SyntaxKind.RegularExpressionLiteral: - return globalRegExpType; - case SyntaxKind.ArrayLiteralExpression: - return checkArrayLiteral( - node, - checkMode, - forceTuple - ); - case SyntaxKind.ObjectLiteralExpression: - return checkObjectLiteral( - node, - checkMode - ); - case SyntaxKind.PropertyAccessExpression: - return checkPropertyAccessExpression( node); - case SyntaxKind.QualifiedName: - return checkQualifiedName( node); - case SyntaxKind.ElementAccessExpression: - return checkIndexedAccess( node); - case SyntaxKind.CallExpression: - if (( node).expression.kind - === SyntaxKind.ImportKeyword) - { - return checkImportCallExpression( node); - } - // falls through - case SyntaxKind.NewExpression: - return checkCallExpression( - node, - checkMode - ); - case SyntaxKind.TaggedTemplateExpression: - return checkTaggedTemplateExpression( node); - case SyntaxKind.ParenthesizedExpression: - return checkParenthesizedExpression( - node, - checkMode - ); - case SyntaxKind.ClassExpression: - return checkClassExpression( node); - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return checkFunctionExpressionOrObjectLiteralMethod( - node, - checkMode - ); - case SyntaxKind.TypeOfExpression: - return checkTypeOfExpression( node); - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return checkAssertion( node); - case SyntaxKind.NonNullExpression: - return checkNonNullAssertion( node); - case SyntaxKind.MetaProperty: - return checkMetaProperty( node); - case SyntaxKind.DeleteExpression: - return checkDeleteExpression( node); - case SyntaxKind.VoidExpression: - return checkVoidExpression( node); - case SyntaxKind.AwaitExpression: - return checkAwaitExpression( node); - case SyntaxKind.PrefixUnaryExpression: - return checkPrefixUnaryExpression( node); - case SyntaxKind.PostfixUnaryExpression: - return checkPostfixUnaryExpression( node); - case SyntaxKind.BinaryExpression: - return checkBinaryExpression( - node, - checkMode - ); - case SyntaxKind.ConditionalExpression: - return checkConditionalExpression( - node, - checkMode - ); - case SyntaxKind.SpreadElement: - return checkSpreadExpression( - node, - checkMode - ); - case SyntaxKind.OmittedExpression: - return undefinedWideningType; - case SyntaxKind.YieldExpression: - return checkYieldExpression( node); - case SyntaxKind.SyntheticExpression: - return ( node).type; - case SyntaxKind.JsxExpression: - return checkJsxExpression( node, checkMode); - case SyntaxKind.JsxElement: - return checkJsxElement( node, checkMode); - case SyntaxKind.JsxSelfClosingElement: - return checkJsxSelfClosingElement( - node, - checkMode - ); - case SyntaxKind.JsxFragment: - return checkJsxFragment( node); - case SyntaxKind.JsxAttributes: - return checkJsxAttributes( node, checkMode); - case SyntaxKind.JsxOpeningElement: - Debug - .fail('Shouldn\'t ever directly check a JsxOpeningElement'); - } - return errorType; - } - - // DECLARATION AND STATEMENT TYPE CHECKING - - function checkTypeParameter(node: TypeParameterDeclaration) { - // Grammar Checking - if (node.expression) { - grammarErrorOnFirstToken( - node.expression, - Diagnostics.Type_expected - ); - } - - checkSourceElement(node.constraint); - checkSourceElement(node.default); - const typeParameter = - getDeclaredTypeOfTypeParameter(getSymbolOfNode(node)); - // Resolve base constraint to reveal circularity errors - getBaseConstraintOfType(typeParameter); - if (!hasNonCircularTypeParameterDefault(typeParameter)) { - error( - node.default, - Diagnostics.Type_parameter_0_has_a_circular_default, - typeToString(typeParameter) - ); - } - const constraintType = getConstraintOfTypeParameter(typeParameter); - const defaultType = getDefaultFromTypeParameter(typeParameter); - if (constraintType && defaultType) { - checkTypeAssignableTo( - defaultType, - getTypeWithThisArgument( - instantiateType( - constraintType, - makeUnaryTypeMapper(typeParameter, defaultType) - ), - defaultType - ), - node.default, - Diagnostics.Type_0_does_not_satisfy_the_constraint_1 - ); - } - if (produceDiagnostics) { - checkTypeNameIsReserved( - node.name, - Diagnostics.Type_parameter_name_cannot_be_0 - ); - } - } - - function checkParameter(node: ParameterDeclaration) { - // Grammar checking - // It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the - // Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code - // or if its FunctionBody is strict code(11.1.5). - checkGrammarDecoratorsAndModifiers(node); - - checkVariableLikeDeclaration(node); - const func = getContainingFunction(node)!; - if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { - if (!(func.kind === SyntaxKind.Constructor - && nodeIsPresent(func.body))) - { - error( - node, - Diagnostics - .A_parameter_property_is_only_allowed_in_a_constructor_implementation - ); - } - } - if (node.questionToken && isBindingPattern(node.name) - && (func as FunctionLikeDeclaration).body) - { - error( - node, - Diagnostics - .A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature - ); - } - if (node.name && isIdentifier(node.name) - && (node.name.escapedText === 'this' - || node.name.escapedText === 'new')) - { - if (func.parameters.indexOf(node) !== 0) { - error( - node, - Diagnostics.A_0_parameter_must_be_the_first_parameter, - node.name.escapedText as string - ); - } - if (func.kind === SyntaxKind.Constructor - || func.kind === SyntaxKind.ConstructSignature - || func.kind === SyntaxKind.ConstructorType) - { - error( - node, - Diagnostics.A_constructor_cannot_have_a_this_parameter - ); - } - if (func.kind === SyntaxKind.ArrowFunction) { - error( - node, - Diagnostics - .An_arrow_function_cannot_have_a_this_parameter - ); - } - } - - // Only check rest parameter type if it's not a binding pattern. Since binding patterns are - // not allowed in a rest parameter, we already have an error from checkGrammarParameterList. - if (node.dotDotDotToken && !isBindingPattern(node.name) - && !isTypeAssignableTo( - getTypeOfSymbol(node.symbol), - anyReadonlyArrayType - )) - { - error( - node, - Diagnostics.A_rest_parameter_must_be_of_an_array_type - ); - } - } - - function checkTypePredicate(node: TypePredicateNode): void { - const parent = getTypePredicateParent(node); - if (!parent) { - // The parent must not be valid. - error( - node, - Diagnostics - .A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods - ); - return; - } - - const signature = getSignatureFromDeclaration(parent); - const typePredicate = getTypePredicateOfSignature(signature); - if (!typePredicate) { - return; - } - - checkSourceElement(node.type); - - const { parameterName } = node; - if (typePredicate.kind === TypePredicateKind.This - || typePredicate.kind === TypePredicateKind.AssertsThis) - { - getTypeFromThisTypeNode(parameterName as ThisTypeNode); - } else { - if (typePredicate.parameterIndex >= 0) { - if (signatureHasRestParameter(signature) - && typePredicate.parameterIndex - === signature.parameters.length - 1) - { - error( - parameterName, - Diagnostics - .A_type_predicate_cannot_reference_a_rest_parameter - ); - } else { - if (typePredicate.type) { - const leadingError = () => chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type - ); - checkTypeAssignableTo( - typePredicate.type, - getTypeOfSymbol(signature.parameters - [typePredicate.parameterIndex]), - node.type, - /*headMessage*/ undefined, - leadingError - ); - } - } - } else if (parameterName) { - let hasReportedError = false; - for (const { name } of parent.parameters) { - if (isBindingPattern(name) - && checkIfTypePredicateVariableIsDeclaredInBindingPattern( - name, - parameterName, - typePredicate.parameterName - )) - { - hasReportedError = true; - break; - } - } - if (!hasReportedError) { - error( - node.parameterName, - Diagnostics.Cannot_find_parameter_0, - typePredicate.parameterName - ); - } - } - } - } - - function getTypePredicateParent(node: Node): SignatureDeclaration - | undefined - { - switch (node.parent.kind) { - case SyntaxKind.ArrowFunction: - case SyntaxKind.CallSignature: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionType: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - const parent = node.parent; - if (node === parent.type) { - return parent; - } - } - } - - function checkIfTypePredicateVariableIsDeclaredInBindingPattern( - pattern: BindingPattern, - predicateVariableNode: Node, - predicateVariableName: string - ) { - for (const element of pattern.elements) { - if (isOmittedExpression(element)) { - continue; - } - - const name = element.name; - if (name.kind === SyntaxKind.Identifier - && name.escapedText === predicateVariableName) - { - error( - predicateVariableNode, - Diagnostics - .A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, - predicateVariableName - ); - return true; - } else if (name.kind === SyntaxKind.ArrayBindingPattern - || name.kind === SyntaxKind.ObjectBindingPattern) - { - if (checkIfTypePredicateVariableIsDeclaredInBindingPattern( - name, - predicateVariableNode, - predicateVariableName - )) { - return true; - } - } - } - } - - function checkSignatureDeclaration(node: SignatureDeclaration) { - // Grammar checking - if (node.kind === SyntaxKind.IndexSignature) { - checkGrammarIndexSignature( node); - } // TODO (yuisu): Remove this check in else-if when SyntaxKind.Construct is moved and ambient context is handled - else if (node.kind === SyntaxKind.FunctionType - || node.kind === SyntaxKind.FunctionDeclaration - || node.kind === SyntaxKind.ConstructorType - || node.kind === SyntaxKind.CallSignature - || node.kind === SyntaxKind.Constructor - || node.kind === SyntaxKind.ConstructSignature) - { - checkGrammarFunctionLikeDeclaration( node); - } - - const functionFlags = - getFunctionFlags( node); - if (!(functionFlags & FunctionFlags.Invalid)) { - // Async generators prior to ESNext require the __await and __asyncGenerator helpers - if ((functionFlags & FunctionFlags.AsyncGenerator) - === FunctionFlags.AsyncGenerator - && languageVersion < ScriptTarget.ESNext) - { - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.AsyncGeneratorIncludes - ); - } - - // Async functions prior to ES2017 require the __awaiter helper - if ((functionFlags & FunctionFlags.AsyncGenerator) - === FunctionFlags.Async - && languageVersion < ScriptTarget.ES2017) - { - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.Awaiter - ); - } - - // Generator functions, Async functions, and Async Generator functions prior to - // ES2015 require the __generator helper - if ((functionFlags & FunctionFlags.AsyncGenerator) - !== FunctionFlags.Normal - && languageVersion < ScriptTarget.ES2015) - { - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.Generator - ); - } - } - - checkTypeParameters(node.typeParameters); - - forEach(node.parameters, checkParameter); - - // TODO(rbuckton): Should we start checking JSDoc types? - if (node.type) { - checkSourceElement(node.type); - } - - if (produceDiagnostics) { - checkCollisionWithArgumentsInGeneratedCode(node); - const returnTypeNode = getEffectiveReturnTypeNode(node); - if (noImplicitAny && !returnTypeNode) { - switch (node.kind) { - case SyntaxKind.ConstructSignature: - error( - node, - Diagnostics - .Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type - ); - break; - case SyntaxKind.CallSignature: - error( - node, - Diagnostics - .Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type - ); - break; - } - } - - if (returnTypeNode) { - const functionFlags = - getFunctionFlags( node); - if ((functionFlags - & (FunctionFlags.Invalid | FunctionFlags.Generator)) - === FunctionFlags.Generator) - { - const returnType = getTypeFromTypeNode(returnTypeNode); - if (returnType === voidType) { - error( - returnTypeNode, - Diagnostics - .A_generator_cannot_have_a_void_type_annotation - ); - } else { - // Naively, one could check that Generator is assignable to the return type annotation. - // However, that would not catch the error in the following case. - // - // interface BadGenerator extends Iterable, Iterator { } - // function* g(): BadGenerator { } // Iterable and Iterator have different types! - // - const generatorYieldType = - getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Yield, - returnType, - (functionFlags & FunctionFlags.Async) !== 0 - ) || anyType; - const generatorReturnType = - getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Return, - returnType, - (functionFlags & FunctionFlags.Async) !== 0 - ) || generatorYieldType; - const generatorNextType = - getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Next, - returnType, - (functionFlags & FunctionFlags.Async) !== 0 - ) || unknownType; - const generatorInstantiation = - createGeneratorReturnType( - generatorYieldType, - generatorReturnType, - generatorNextType, - !!(functionFlags & FunctionFlags.Async) - ); - checkTypeAssignableTo( - generatorInstantiation, - returnType, - returnTypeNode - ); - } - } else if ((functionFlags & FunctionFlags.AsyncGenerator) - === FunctionFlags.Async) - { - checkAsyncFunctionReturnType( - node, - returnTypeNode - ); - } - } - if (node.kind !== SyntaxKind.IndexSignature - && node.kind !== SyntaxKind.JSDocFunctionType) - { - registerForUnusedIdentifiersCheck(node); - } - } - } - - function checkClassForDuplicateDeclarations(node: - ClassLikeDeclaration) - { - const instanceNames = - createUnderscoreEscapedMap(); - const staticNames = - createUnderscoreEscapedMap(); - // instance and static private identifiers share the same scope - const privateIdentifiers = - createUnderscoreEscapedMap(); - for (const member of node.members) { - if (member.kind === SyntaxKind.Constructor) { - for (const param of (member as ConstructorDeclaration) - .parameters) - { - if (isParameterPropertyDeclaration(param, member) - && !isBindingPattern(param.name)) - { - addName( - instanceNames, - param.name, - param.name.escapedText, - DeclarationMeaning.GetOrSetAccessor - ); - } - } - } else { - const isStatic = hasModifier(member, ModifierFlags.Static); - const name = member.name; - if (!name) { - return; - } - const names = isPrivateIdentifier(name) - ? privateIdentifiers - : isStatic - ? staticNames - : instanceNames; - const memberName = name - && getPropertyNameForPropertyNameNode(name); - if (memberName) { - switch (member.kind) { - case SyntaxKind.GetAccessor: - addName( - names, - name, - memberName, - DeclarationMeaning.GetAccessor - ); - break; - - case SyntaxKind.SetAccessor: - addName( - names, - name, - memberName, - DeclarationMeaning.SetAccessor - ); - break; - - case SyntaxKind.PropertyDeclaration: - addName( - names, - name, - memberName, - DeclarationMeaning.GetOrSetAccessor - ); - break; - - case SyntaxKind.MethodDeclaration: - addName( - names, - name, - memberName, - DeclarationMeaning.Method - ); - break; - } - } - } - } - - function addName( - names: UnderscoreEscapedMap, - location: Node, - name: __String, - meaning: DeclarationMeaning - ) { - const prev = names.get(name); - if (prev) { - if (prev & DeclarationMeaning.Method) { - if (meaning !== DeclarationMeaning.Method) { - error( - location, - Diagnostics.Duplicate_identifier_0, - getTextOfNode(location) - ); - } - } else if (prev & meaning) { - error( - location, - Diagnostics.Duplicate_identifier_0, - getTextOfNode(location) - ); - } else { - names.set(name, prev | meaning); - } - } else { - names.set(name, meaning); - } - } - } - - /** - * Static members being set on a constructor function may conflict with built-in properties - * of Function. Esp. in ECMAScript 5 there are non-configurable and non-writable - * built-in properties. This check issues a transpile error when a class has a static - * member with the same name as a non-writable built-in property. - * - * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3 - * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5 - * @see http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-constructor - * @see http://www.ecma-international.org/ecma-262/6.0/#sec-function-instances - */ - function checkClassForStaticPropertyNameConflicts(node: - ClassLikeDeclaration) - { - for (const member of node.members) { - const memberNameNode = member.name; - const isStatic = hasModifier(member, ModifierFlags.Static); - if (isStatic && memberNameNode) { - const memberName = - getPropertyNameForPropertyNameNode(memberNameNode); - switch (memberName) { - case 'name': - case 'length': - case 'caller': - case 'arguments': - case 'prototype': - const message = Diagnostics - .Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1; - const className = - getNameOfSymbolAsWritten(getSymbolOfNode(node)); - error( - memberNameNode, - message, - memberName, - className - ); - break; - } - } - } - } - - function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode - | InterfaceDeclaration) - { - const names = createMap(); - for (const member of node.members) { - if (member.kind === SyntaxKind.PropertySignature) { - let memberName: string; - const name = member.name!; - switch (name.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - memberName = name.text; - break; - case SyntaxKind.Identifier: - memberName = idText(name); - break; - default: - continue; - } - - if (names.get(memberName)) { - error( - getNameOfDeclaration(member.symbol - .valueDeclaration), - Diagnostics.Duplicate_identifier_0, - memberName - ); - error( - member.name, - Diagnostics.Duplicate_identifier_0, - memberName - ); - } else { - names.set(memberName, true); - } - } - } - } - - function checkTypeForDuplicateIndexSignatures(node: Node) { - if (node.kind === SyntaxKind.InterfaceDeclaration) { - const nodeSymbol = - getSymbolOfNode(node as InterfaceDeclaration); - // in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration - // to prevent this run check only for the first declaration of a given kind - if (nodeSymbol.declarations.length > 0 - && nodeSymbol.declarations[0] !== node) - { - return; - } - } - - // TypeScript 1.0 spec (April 2014) - // 3.7.4: An object type can contain at most one string index signature and one numeric index signature. - // 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration - const indexSymbol = getIndexSymbol(getSymbolOfNode(node)!); - if (indexSymbol) { - let seenNumericIndexer = false; - let seenStringIndexer = false; - for (const decl of indexSymbol.declarations) { - const declaration = decl; - if (declaration.parameters.length === 1 - && declaration.parameters[0].type) - { - switch (declaration.parameters[0].type.kind) { - case SyntaxKind.StringKeyword: - if (!seenStringIndexer) { - seenStringIndexer = true; - } else { - error( - declaration, - Diagnostics - .Duplicate_string_index_signature - ); - } - break; - case SyntaxKind.NumberKeyword: - if (!seenNumericIndexer) { - seenNumericIndexer = true; - } else { - error( - declaration, - Diagnostics - .Duplicate_number_index_signature - ); - } - break; - } - } - } - } - } - - function checkPropertyDeclaration(node: PropertyDeclaration - | PropertySignature) - { - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node) - && !checkGrammarProperty(node)) - { - checkGrammarComputedPropertyName(node.name); - } - checkVariableLikeDeclaration(node); - - // Private class fields transformation relies on WeakMaps. - if (isPrivateIdentifier(node.name) - && languageVersion < ScriptTarget.ESNext) - { - for (let lexicalScope = getEnclosingBlockScopeContainer(node); - !!lexicalScope; - lexicalScope = getEnclosingBlockScopeContainer(lexicalScope)) - { - getNodeLinks(lexicalScope).flags |= NodeCheckFlags - .ContainsClassWithPrivateIdentifiers; - } - } - } - - function checkPropertySignature(node: PropertySignature) { - if (isPrivateIdentifier(node.name)) { - error( - node, - Diagnostics - .Private_identifiers_are_not_allowed_outside_class_bodies - ); - } - return checkPropertyDeclaration(node); - } - - function checkMethodDeclaration(node: MethodDeclaration - | MethodSignature) - { - // Grammar checking - if (!checkGrammarMethod(node)) { - checkGrammarComputedPropertyName(node.name); - } - - if (isPrivateIdentifier(node.name)) { - error( - node, - Diagnostics - .A_method_cannot_be_named_with_a_private_identifier - ); - } - - // Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration - checkFunctionOrMethodDeclaration(node); - - // Abstract methods cannot have an implementation. - // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. - if (hasModifier(node, ModifierFlags.Abstract) - && node.kind === SyntaxKind.MethodDeclaration && node.body) - { - error( - node, - Diagnostics - .Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, - declarationNameToString(node.name) - ); - } - } - - function checkConstructorDeclaration(node: ConstructorDeclaration) { - // Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function. - checkSignatureDeclaration(node); - // Grammar check for checking only related to constructorDeclaration - if (!checkGrammarConstructorTypeParameters(node)) checkGrammarConstructorTypeAnnotation(node); - - checkSourceElement(node.body); - - const symbol = getSymbolOfNode(node); - const firstDeclaration = getDeclarationOfKind(symbol, node.kind); - - // Only type check the symbol once - if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(symbol); - } - - // exit early in the case of signature - super checks are not relevant to them - if (nodeIsMissing(node.body)) { - return; - } - - if (!produceDiagnostics) { - return; - } - - function isInstancePropertyWithInitializerOrPrivateIdentifierProperty(n: - Node): boolean - { - if (isPrivateIdentifierPropertyDeclaration(n)) { - return true; - } - return n.kind === SyntaxKind.PropertyDeclaration - && !hasModifier(n, ModifierFlags.Static) - && !!( n).initializer; - } - - // TS 1.0 spec (April 2014): 8.3.2 - // Constructors of classes with no extends clause may not contain super calls, whereas - // constructors of derived classes must contain at least one super call somewhere in their function body. - const containingClassDecl = node.parent; - if (getClassExtendsHeritageElement(containingClassDecl)) { - captureLexicalThis(node.parent, containingClassDecl); - const classExtendsNull = - classDeclarationExtendsNull(containingClassDecl); - const superCall = getSuperCallInConstructor(node); - if (superCall) { - if (classExtendsNull) { - error( - superCall, - Diagnostics - .A_constructor_cannot_contain_a_super_call_when_its_class_extends_null - ); - } - - // The first statement in the body of a constructor (excluding prologue directives) must be a super call - // if both of the following are true: - // - The containing class is a derived class. - // - The constructor declares parameter properties - // or the containing class declares instance member variables with initializers. - const superCallShouldBeFirst = - some( - ( node.parent).members, - isInstancePropertyWithInitializerOrPrivateIdentifierProperty - ) - || some( - node.parameters, - p => hasModifier( - p, - ModifierFlags.ParameterPropertyModifier - ) - ); - - // Skip past any prologue directives to find the first statement - // to ensure that it was a super call. - if (superCallShouldBeFirst) { - const statements = node.body!.statements; - let superCallStatement: ExpressionStatement - | undefined; - - for (const statement of statements) { - if (statement.kind - === SyntaxKind.ExpressionStatement - && isSuperCall(( statement) - .expression)) - { - superCallStatement = statement; - break; - } - if (!isPrologueDirective(statement)) { - break; - } - } - if (!superCallStatement) { - error( - node, - Diagnostics - .A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_parameter_properties_or_private_identifiers - ); - } - } - } else if (!classExtendsNull) { - error( - node, - Diagnostics - .Constructors_for_derived_classes_must_contain_a_super_call - ); - } - } - } - - function checkAccessorDeclaration(node: AccessorDeclaration) { - if (produceDiagnostics) { - // Grammar checking accessors - if (!checkGrammarFunctionLikeDeclaration(node) - && !checkGrammarAccessor(node)) - { - checkGrammarComputedPropertyName(node.name); - } - - checkDecorators(node); - checkSignatureDeclaration(node); - if (node.kind === SyntaxKind.GetAccessor) { - if (!(node.flags & NodeFlags.Ambient) - && nodeIsPresent(node.body) - && (node.flags & NodeFlags.HasImplicitReturn)) - { - if (!(node.flags & NodeFlags.HasExplicitReturn)) { - error( - node.name, - Diagnostics.A_get_accessor_must_return_a_value - ); - } - } - } - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - } - if (isPrivateIdentifier(node.name)) { - error( - node.name, - Diagnostics - .An_accessor_cannot_be_named_with_a_private_identifier - ); - } - if (!hasNonBindableDynamicName(node)) { - // TypeScript 1.0 spec (April 2014): 8.4.3 - // Accessors for the same member name must specify the same accessibility. - const otherKind = node.kind === SyntaxKind.GetAccessor - ? SyntaxKind.SetAccessor - : SyntaxKind.GetAccessor; - const otherAccessor = - getDeclarationOfKind( - getSymbolOfNode(node), - otherKind - ); - if (otherAccessor) { - const nodeFlags = getModifierFlags(node); - const otherFlags = getModifierFlags(otherAccessor); - if ((nodeFlags & ModifierFlags.AccessibilityModifier) - !== (otherFlags - & ModifierFlags.AccessibilityModifier)) - { - error( - node.name, - Diagnostics - .Getter_and_setter_accessors_do_not_agree_in_visibility - ); - } - if ((nodeFlags & ModifierFlags.Abstract) - !== (otherFlags & ModifierFlags.Abstract)) - { - error( - node.name, - Diagnostics - .Accessors_must_both_be_abstract_or_non_abstract - ); - } - - // TypeScript 1.0 spec (April 2014): 4.5 - // If both accessors include type annotations, the specified types must be identical. - checkAccessorDeclarationTypesIdentical( - node, - otherAccessor, - getAnnotatedAccessorType, - Diagnostics - .get_and_set_accessor_must_have_the_same_type - ); - checkAccessorDeclarationTypesIdentical( - node, - otherAccessor, - getThisTypeOfDeclaration, - Diagnostics - .get_and_set_accessor_must_have_the_same_this_type - ); - } - } - const returnType = getTypeOfAccessors(getSymbolOfNode(node)); - if (node.kind === SyntaxKind.GetAccessor) { - checkAllCodePathsInNonVoidFunctionReturnOrThrow( - node, - returnType - ); - } - } - checkSourceElement(node.body); - } - - function checkAccessorDeclarationTypesIdentical( - first: AccessorDeclaration, - second: AccessorDeclaration, - getAnnotatedType: (a: AccessorDeclaration) => Type | undefined, - message: DiagnosticMessage - ) { - const firstType = getAnnotatedType(first); - const secondType = getAnnotatedType(second); - if (firstType && secondType - && !isTypeIdenticalTo(firstType, secondType)) - { - error(first, message); - } - } - - function checkMissingDeclaration(node: Node) { - checkDecorators(node); - } - - function getEffectiveTypeArguments( - node: TypeReferenceNode | ExpressionWithTypeArguments, - typeParameters: readonly TypeParameter[] - ): Type[] { - return fillMissingTypeArguments( - map(node.typeArguments!, getTypeFromTypeNode), - typeParameters, - getMinTypeArgumentCount(typeParameters), - isInJSFile(node) - ); - } - - function checkTypeArgumentConstraints( - node: TypeReferenceNode | ExpressionWithTypeArguments, - typeParameters: readonly TypeParameter[] - ): boolean { - let typeArguments: Type[] | undefined; - let mapper: TypeMapper | undefined; - let result = true; - for (let i = 0; i < typeParameters.length; i++) { - const constraint = - getConstraintOfTypeParameter(typeParameters[i]); - if (constraint) { - if (!typeArguments) { - typeArguments = getEffectiveTypeArguments( - node, - typeParameters - ); - mapper = createTypeMapper( - typeParameters, - typeArguments - ); - } - result = result && checkTypeAssignableTo( - typeArguments[i], - instantiateType(constraint, mapper), - node.typeArguments![i], - Diagnostics.Type_0_does_not_satisfy_the_constraint_1 - ); - } - } - return result; - } - - function getTypeParametersForTypeReference(node: TypeReferenceNode - | ExpressionWithTypeArguments) - { - const type = getTypeFromTypeReference(node); - if (type !== errorType) { - const symbol = getNodeLinks(node).resolvedSymbol; - if (symbol) { - return symbol.flags & SymbolFlags.TypeAlias - && getSymbolLinks(symbol).typeParameters - || (getObjectFlags(type) & ObjectFlags.Reference - ? ( type).target.localTypeParameters - : undefined); - } - } - return undefined; - } - - function checkTypeReferenceNode(node: TypeReferenceNode - | ExpressionWithTypeArguments) - { - checkGrammarTypeArguments(node, node.typeArguments); - if (node.kind === SyntaxKind.TypeReference - && node.typeName.jsdocDotPos !== undefined && !isInJSFile(node) - && !isInJSDoc(node)) - { - grammarErrorAtPos( - node, - node.typeName.jsdocDotPos, - 1, - Diagnostics - .JSDoc_types_can_only_be_used_inside_documentation_comments - ); - } - forEach(node.typeArguments, checkSourceElement); - const type = getTypeFromTypeReference(node); - if (type !== errorType) { - if (node.typeArguments && produceDiagnostics) { - const typeParameters = - getTypeParametersForTypeReference(node); - if (typeParameters) { - checkTypeArgumentConstraints(node, typeParameters); - } - } - if (type.flags & TypeFlags.Enum - && getNodeLinks(node).resolvedSymbol!.flags - & SymbolFlags.EnumMember) - { - error( - node, - Diagnostics - .Enum_type_0_has_members_with_initializers_that_are_not_literals, - typeToString(type) - ); - } - } - } - - function getTypeArgumentConstraint(node: TypeNode): Type | undefined { - const typeReferenceNode = tryCast( - node.parent, - isTypeReferenceType - ); - if (!typeReferenceNode) return undefined; - const typeParameters = - getTypeParametersForTypeReference(typeReferenceNode)!; // TODO: GH#18217 - const constraint = - getConstraintOfTypeParameter(typeParameters[typeReferenceNode - .typeArguments!.indexOf(node)]); - return constraint - && instantiateType( - constraint, - createTypeMapper( - typeParameters, - getEffectiveTypeArguments( - typeReferenceNode, - typeParameters - ) - ) - ); - } - - function checkTypeQuery(node: TypeQueryNode) { - getTypeFromTypeQueryNode(node); - } - - function checkTypeLiteral(node: TypeLiteralNode) { - forEach(node.members, checkSourceElement); - if (produceDiagnostics) { - const type = - getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); - checkIndexConstraints(type); - checkTypeForDuplicateIndexSignatures(node); - checkObjectTypeForDuplicateDeclarations(node); - } - } - - function checkArrayType(node: ArrayTypeNode) { - checkSourceElement(node.elementType); - } - - function checkTupleType(node: TupleTypeNode) { - const elementTypes = node.elementTypes; - let seenOptionalElement = false; - for (let i = 0; i < elementTypes.length; i++) { - const e = elementTypes[i]; - if (e.kind === SyntaxKind.RestType) { - if (i !== elementTypes.length - 1) { - grammarErrorOnNode( - e, - Diagnostics - .A_rest_element_must_be_last_in_a_tuple_type - ); - break; - } - if (!isArrayType(getTypeFromTypeNode(( e) - .type))) - { - error( - e, - Diagnostics - .A_rest_element_type_must_be_an_array_type - ); - } - } else if (e.kind === SyntaxKind.OptionalType) { - seenOptionalElement = true; - } else if (seenOptionalElement) { - grammarErrorOnNode( - e, - Diagnostics - .A_required_element_cannot_follow_an_optional_element - ); - break; - } - } - forEach(node.elementTypes, checkSourceElement); - } - - function checkUnionOrIntersectionType(node: - UnionOrIntersectionTypeNode) - { - forEach(node.types, checkSourceElement); - } - - function checkIndexedAccessIndexType( - type: Type, - accessNode: IndexedAccessTypeNode | ElementAccessExpression - ) { - if (!(type.flags & TypeFlags.IndexedAccess)) { - return type; - } - // Check if the index type is assignable to 'keyof T' for the object type. - const objectType = ( type).objectType; - const indexType = ( type).indexType; - if (isTypeAssignableTo( - indexType, - getIndexType(objectType, /*stringsOnly*/ false) - )) { - if (accessNode.kind === SyntaxKind.ElementAccessExpression - && isAssignmentTarget(accessNode) - && getObjectFlags(objectType) & ObjectFlags.Mapped - && getMappedTypeModifiers( objectType) - & MappedTypeModifiers.IncludeReadonly) - { - error( - accessNode, - Diagnostics - .Index_signature_in_type_0_only_permits_reading, - typeToString(objectType) - ); - } - return type; - } - // Check if we're indexing with a numeric type and if either object or index types - // is a generic type with a constraint that has a numeric index signature. - const apparentObjectType = getApparentType(objectType); - if (getIndexInfoOfType(apparentObjectType, IndexKind.Number) - && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) - { - return type; - } - if (isGenericObjectType(objectType)) { - const propertyName = getPropertyNameFromIndex( - indexType, - accessNode - ); - if (propertyName) { - const propertySymbol = forEachType( - apparentObjectType, - t => getPropertyOfType(t, propertyName) - ); - if (propertySymbol - && getDeclarationModifierFlagsFromSymbol(propertySymbol) - & ModifierFlags.NonPublicAccessibilityModifier) - { - error( - accessNode, - Diagnostics - .Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, - unescapeLeadingUnderscores(propertyName) - ); - return errorType; - } - } - } - error( - accessNode, - Diagnostics.Type_0_cannot_be_used_to_index_type_1, - typeToString(indexType), - typeToString(objectType) - ); - return errorType; - } - - function checkIndexedAccessType(node: IndexedAccessTypeNode) { - checkSourceElement(node.objectType); - checkSourceElement(node.indexType); - checkIndexedAccessIndexType( - getTypeFromIndexedAccessTypeNode(node), - node - ); - } - - function checkMappedType(node: MappedTypeNode) { - checkSourceElement(node.typeParameter); - checkSourceElement(node.type); - - if (!node.type) { - reportImplicitAny(node, anyType); - } - - const type = getTypeFromMappedTypeNode(node); - const constraintType = getConstraintTypeFromMappedType(type); - checkTypeAssignableTo( - constraintType, - keyofConstraintType, - getEffectiveConstraintOfTypeParameter(node.typeParameter) - ); - } - - function checkThisType(node: ThisTypeNode) { - getTypeFromThisTypeNode(node); - } - - function checkTypeOperator(node: TypeOperatorNode) { - checkGrammarTypeOperatorNode(node); - checkSourceElement(node.type); - } - - function checkConditionalType(node: ConditionalTypeNode) { - forEachChild(node, checkSourceElement); - } - - function checkInferType(node: InferTypeNode) { - if (!findAncestor( - node, - n => n.parent && n.parent.kind === SyntaxKind.ConditionalType - && ( n.parent).extendsType === n - )) { - grammarErrorOnNode( - node, - Diagnostics - .infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type - ); - } - checkSourceElement(node.typeParameter); - registerForUnusedIdentifiersCheck(node); - } - - function checkImportType(node: ImportTypeNode) { - checkSourceElement(node.argument); - getTypeFromTypeNode(node); - } - - function isPrivateWithinAmbient(node: Node): boolean { - return hasModifier(node, ModifierFlags.Private) - && !!(node.flags & NodeFlags.Ambient); - } - - function getEffectiveDeclarationFlags( - n: Declaration, - flagsToCheck: ModifierFlags - ): ModifierFlags { - let flags = getCombinedModifierFlags(n); - - // children of classes (even ambient classes) should not be marked as ambient or export - // because those flags have no useful semantics there. - if (n.parent.kind !== SyntaxKind.InterfaceDeclaration - && n.parent.kind !== SyntaxKind.ClassDeclaration - && n.parent.kind !== SyntaxKind.ClassExpression - && n.flags & NodeFlags.Ambient) - { - if (!(flags & ModifierFlags.Ambient) - && !(isModuleBlock(n.parent) - && isModuleDeclaration(n.parent.parent) - && isGlobalScopeAugmentation(n.parent.parent))) - { - // It is nested in an ambient context, which means it is automatically exported - flags |= ModifierFlags.Export; - } - flags |= ModifierFlags.Ambient; - } - - return flags & flagsToCheck; - } - - function checkFunctionOrConstructorSymbol(symbol: Symbol): void { - if (!produceDiagnostics) { - return; - } - - function getCanonicalOverload( - overloads: Declaration[], - implementation: FunctionLikeDeclaration | undefined - ): Declaration { - // Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration - // Error on all deviations from this canonical set of flags - // The caveat is that if some overloads are defined in lib.d.ts, we don't want to - // report the errors on those. To achieve this, we will say that the implementation is - // the canonical signature only if it is in the same container as the first overload - const implementationSharesContainerWithFirstOverload = - implementation !== undefined - && implementation.parent === overloads[0].parent; - return implementationSharesContainerWithFirstOverload - ? implementation! - : overloads[0]; - } - - function checkFlagAgreementBetweenOverloads( - overloads: Declaration[], - implementation: FunctionLikeDeclaration | undefined, - flagsToCheck: ModifierFlags, - someOverloadFlags: ModifierFlags, - allOverloadFlags: ModifierFlags - ): void { - // Error if some overloads have a flag that is not shared by all overloads. To find the - // deviations, we XOR someOverloadFlags with allOverloadFlags - const someButNotAllOverloadFlags = - someOverloadFlags ^ allOverloadFlags; - if (someButNotAllOverloadFlags !== 0) { - const canonicalFlags = getEffectiveDeclarationFlags( - getCanonicalOverload(overloads, implementation), - flagsToCheck - ); - - forEach(overloads, o => { - const deviation = - getEffectiveDeclarationFlags(o, flagsToCheck) - ^ canonicalFlags; - if (deviation & ModifierFlags.Export) { - error( - getNameOfDeclaration(o), - Diagnostics - .Overload_signatures_must_all_be_exported_or_non_exported - ); - } else if (deviation & ModifierFlags.Ambient) { - error( - getNameOfDeclaration(o), - Diagnostics - .Overload_signatures_must_all_be_ambient_or_non_ambient - ); - } else if (deviation - & (ModifierFlags.Private - | ModifierFlags.Protected)) - { - error( - getNameOfDeclaration(o) || o, - Diagnostics - .Overload_signatures_must_all_be_public_private_or_protected - ); - } else if (deviation & ModifierFlags.Abstract) { - error( - getNameOfDeclaration(o), - Diagnostics - .Overload_signatures_must_all_be_abstract_or_non_abstract - ); - } - }); - } - } - - function checkQuestionTokenAgreementBetweenOverloads( - overloads: Declaration[], - implementation: FunctionLikeDeclaration | undefined, - someHaveQuestionToken: boolean, - allHaveQuestionToken: boolean - ): void { - if (someHaveQuestionToken !== allHaveQuestionToken) { - const canonicalHasQuestionToken = - hasQuestionToken(getCanonicalOverload( - overloads, - implementation - )); - forEach(overloads, o => { - const deviation = - hasQuestionToken(o) !== canonicalHasQuestionToken; - if (deviation) { - error( - getNameOfDeclaration(o), - Diagnostics - .Overload_signatures_must_all_be_optional_or_required - ); - } - }); - } - } - - const flagsToCheck: ModifierFlags = - ModifierFlags.Export | ModifierFlags.Ambient - | ModifierFlags.Private | ModifierFlags.Protected - | ModifierFlags.Abstract; - let someNodeFlags: ModifierFlags = ModifierFlags.None; - let allNodeFlags = flagsToCheck; - let someHaveQuestionToken = false; - let allHaveQuestionToken = true; - let hasOverloads = false; - let bodyDeclaration: FunctionLikeDeclaration | undefined; - let lastSeenNonAmbientDeclaration: FunctionLikeDeclaration - | undefined; - let previousDeclaration: SignatureDeclaration | undefined; - - const declarations = symbol.declarations; - const isConstructor = - (symbol.flags & SymbolFlags.Constructor) !== 0; - - function reportImplementationExpectedError(node: - SignatureDeclaration): void - { - if (node.name && nodeIsMissing(node.name)) { - return; - } - - let seen = false; - const subsequentNode = forEachChild(node.parent, c => { - if (seen) { - return c; - } else { - seen = c === node; - } - }); - // We may be here because of some extra nodes between overloads that could not be parsed into a valid node. - // In this case the subsequent node is not really consecutive (.pos !== node.end), and we must ignore it here. - if (subsequentNode && subsequentNode.pos === node.end) { - if (subsequentNode.kind === node.kind) { - const errorNode: Node = - ( subsequentNode).name - || subsequentNode; - const subsequentName = - ( subsequentNode).name; - if (node.name && subsequentName && ( - // both are private identifiers - isPrivateIdentifier(node.name) - && isPrivateIdentifier(subsequentName) - && node.name.escapedText - === subsequentName.escapedText - // Both are computed property names - // TODO: GH#17345: These are methods, so handle computed name case. (`Always allowing computed property names is *not* the correct behavior!) - || isComputedPropertyName(node.name) - && isComputedPropertyName(subsequentName) - // Both are literal property names that are the same. - || isPropertyNameLiteral(node.name) - && isPropertyNameLiteral(subsequentName) - && getEscapedTextOfIdentifierOrLiteral(node.name) - === getEscapedTextOfIdentifierOrLiteral(subsequentName) - )) { - const reportError = - (node.kind === SyntaxKind.MethodDeclaration - || node.kind - === SyntaxKind.MethodSignature) - && hasModifier(node, ModifierFlags.Static) - !== hasModifier( - subsequentNode, - ModifierFlags.Static - ); - // we can get here in two cases - // 1. mixed static and instance class members - // 2. something with the same name was defined before the set of overloads that prevents them from merging - // here we'll report error only for the first case since for second we should already report error in binder - if (reportError) { - const diagnostic = - hasModifier(node, ModifierFlags.Static) - ? Diagnostics - .Function_overload_must_be_static - : Diagnostics - .Function_overload_must_not_be_static; - error(errorNode, diagnostic); - } - return; - } - if (nodeIsPresent(( subsequentNode) - .body)) - { - error( - errorNode, - Diagnostics - .Function_implementation_name_must_be_0, - declarationNameToString(node.name) - ); - return; - } - } - } - const errorNode: Node = node.name || node; - if (isConstructor) { - error( - errorNode, - Diagnostics.Constructor_implementation_is_missing - ); - } else { - // Report different errors regarding non-consecutive blocks of declarations depending on whether - // the node in question is abstract. - if (hasModifier(node, ModifierFlags.Abstract)) { - error( - errorNode, - Diagnostics - .All_declarations_of_an_abstract_method_must_be_consecutive - ); - } else { - error( - errorNode, - Diagnostics - .Function_implementation_is_missing_or_not_immediately_following_the_declaration - ); - } - } - } - - let duplicateFunctionDeclaration = false; - let multipleConstructorImplementation = false; - let hasNonAmbientClass = false; - for (const current of declarations) { - const node = current; - const inAmbientContext = node.flags & NodeFlags.Ambient; - const inAmbientContextOrInterface = - node.parent.kind === SyntaxKind.InterfaceDeclaration - || node.parent.kind === SyntaxKind.TypeLiteral - || inAmbientContext; - if (inAmbientContextOrInterface) { - // check if declarations are consecutive only if they are non-ambient - // 1. ambient declarations can be interleaved - // i.e. this is legal - // declare function foo(); - // declare function bar(); - // declare function foo(); - // 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one - previousDeclaration = undefined; - } - - if ((node.kind === SyntaxKind.ClassDeclaration - || node.kind === SyntaxKind.ClassExpression) - && !inAmbientContext) - { - hasNonAmbientClass = true; - } - - if (node.kind === SyntaxKind.FunctionDeclaration - || node.kind === SyntaxKind.MethodDeclaration - || node.kind === SyntaxKind.MethodSignature - || node.kind === SyntaxKind.Constructor) - { - const currentNodeFlags = getEffectiveDeclarationFlags( - node, - flagsToCheck - ); - someNodeFlags |= currentNodeFlags; - allNodeFlags &= currentNodeFlags; - someHaveQuestionToken = someHaveQuestionToken - || hasQuestionToken(node); - allHaveQuestionToken = allHaveQuestionToken - && hasQuestionToken(node); - - if (nodeIsPresent((node as FunctionLikeDeclaration).body) - && bodyDeclaration) - { - if (isConstructor) { - multipleConstructorImplementation = true; - } else { - duplicateFunctionDeclaration = true; - } - } else if (previousDeclaration - && previousDeclaration.parent === node.parent - && previousDeclaration.end !== node.pos) - { - reportImplementationExpectedError(previousDeclaration); - } - - if (nodeIsPresent((node as FunctionLikeDeclaration) - .body)) - { - if (!bodyDeclaration) { - bodyDeclaration = node as FunctionLikeDeclaration; - } - } else { - hasOverloads = true; - } - - previousDeclaration = node; - - if (!inAmbientContextOrInterface) { - lastSeenNonAmbientDeclaration = node as FunctionLikeDeclaration; - } - } - } - - if (multipleConstructorImplementation) { - forEach(declarations, declaration => { - error( - declaration, - Diagnostics - .Multiple_constructor_implementations_are_not_allowed - ); - }); - } - - if (duplicateFunctionDeclaration) { - forEach(declarations, declaration => { - error( - getNameOfDeclaration(declaration), - Diagnostics.Duplicate_function_implementation - ); - }); - } - - if (hasNonAmbientClass && !isConstructor - && symbol.flags & SymbolFlags.Function) - { - // A non-ambient class cannot be an implementation for a non-constructor function/class merge - // TODO: The below just replicates our older error from when classes and functions were - // entirely unable to merge - a more helpful message like "Class declaration cannot implement overload list" - // might be warranted. :shrug: - forEach(declarations, declaration => { - addDuplicateDeclarationError( - getNameOfDeclaration(declaration) || declaration, - Diagnostics.Duplicate_identifier_0, - symbolName(symbol), - filter(declarations, d => d !== declaration) - ); - }); - } - - // Abstract methods can't have an implementation -- in particular, they don't need one. - if (lastSeenNonAmbientDeclaration - && !lastSeenNonAmbientDeclaration.body - && !hasModifier( - lastSeenNonAmbientDeclaration, - ModifierFlags.Abstract - ) && !lastSeenNonAmbientDeclaration.questionToken) - { - reportImplementationExpectedError(lastSeenNonAmbientDeclaration); - } - - if (hasOverloads) { - checkFlagAgreementBetweenOverloads( - declarations, - bodyDeclaration, - flagsToCheck, - someNodeFlags, - allNodeFlags - ); - checkQuestionTokenAgreementBetweenOverloads( - declarations, - bodyDeclaration, - someHaveQuestionToken, - allHaveQuestionToken - ); - - if (bodyDeclaration) { - const signatures = getSignaturesOfSymbol(symbol); - const bodySignature = - getSignatureFromDeclaration(bodyDeclaration); - for (const signature of signatures) { - if (!isImplementationCompatibleWithOverload( - bodySignature, - signature - )) { - addRelatedInfo( - error( - signature.declaration, - Diagnostics - .This_overload_signature_is_not_compatible_with_its_implementation_signature - ), - createDiagnosticForNode( - bodyDeclaration, - Diagnostics - .The_implementation_signature_is_declared_here - ) - ); - break; - } - } - } - } - } - - function checkExportsOnMergedDeclarations(node: Node): void { - if (!produceDiagnostics) { - return; - } - - // if localSymbol is defined on node then node itself is exported - check is required - let symbol = node.localSymbol; - if (!symbol) { - // local symbol is undefined => this declaration is non-exported. - // however symbol might contain other declarations that are exported - symbol = getSymbolOfNode(node)!; - if (!symbol.exportSymbol) { - // this is a pure local symbol (all declarations are non-exported) - no need to check anything - return; - } - } - - // run the check only for the first declaration in the list - if (getDeclarationOfKind(symbol, node.kind) !== node) { - return; - } - - let exportedDeclarationSpaces = DeclarationSpaces.None; - let nonExportedDeclarationSpaces = DeclarationSpaces.None; - let defaultExportedDeclarationSpaces = DeclarationSpaces.None; - for (const d of symbol.declarations) { - const declarationSpaces = getDeclarationSpaces(d); - const effectiveDeclarationFlags = getEffectiveDeclarationFlags( - d, - ModifierFlags.Export | ModifierFlags.Default - ); - - if (effectiveDeclarationFlags & ModifierFlags.Export) { - if (effectiveDeclarationFlags & ModifierFlags.Default) { - defaultExportedDeclarationSpaces |= declarationSpaces; - } else { - exportedDeclarationSpaces |= declarationSpaces; - } - } else { - nonExportedDeclarationSpaces |= declarationSpaces; - } - } - - // Spaces for anything not declared a 'default export'. - const nonDefaultExportedDeclarationSpaces = - exportedDeclarationSpaces | nonExportedDeclarationSpaces; - - const commonDeclarationSpacesForExportsAndLocals = - exportedDeclarationSpaces & nonExportedDeclarationSpaces; - const commonDeclarationSpacesForDefaultAndNonDefault = - defaultExportedDeclarationSpaces - & nonDefaultExportedDeclarationSpaces; - - if (commonDeclarationSpacesForExportsAndLocals - || commonDeclarationSpacesForDefaultAndNonDefault) - { - // declaration spaces for exported and non-exported declarations intersect - for (const d of symbol.declarations) { - const declarationSpaces = getDeclarationSpaces(d); - - const name = getNameOfDeclaration(d); - // Only error on the declarations that contributed to the intersecting spaces. - if (declarationSpaces - & commonDeclarationSpacesForDefaultAndNonDefault) - { - error( - name, - Diagnostics - .Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, - declarationNameToString(name) - ); - } else if (declarationSpaces - & commonDeclarationSpacesForExportsAndLocals) - { - error( - name, - Diagnostics - .Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, - declarationNameToString(name) - ); - } - } - } - - function getDeclarationSpaces(decl: - Declaration): DeclarationSpaces - { - let d = decl as Node; - switch (d.kind) { - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - - // A jsdoc typedef and callback are, by definition, type aliases. - // falls through - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - return DeclarationSpaces.ExportType; - case SyntaxKind.ModuleDeclaration: - return isAmbientModule(d as ModuleDeclaration) - || getModuleInstanceState(d as ModuleDeclaration) - !== ModuleInstanceState.NonInstantiated - ? DeclarationSpaces.ExportNamespace - | DeclarationSpaces.ExportValue - : DeclarationSpaces.ExportNamespace; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - return DeclarationSpaces.ExportType - | DeclarationSpaces.ExportValue; - case SyntaxKind.SourceFile: - return DeclarationSpaces.ExportType - | DeclarationSpaces.ExportValue - | DeclarationSpaces.ExportNamespace; - case SyntaxKind.ExportAssignment: - // Export assigned entity name expressions act as aliases and should fall through, otherwise they export values - if (!isEntityNameExpression((d as ExportAssignment) - .expression)) - { - return DeclarationSpaces.ExportValue; - } - d = (d as ExportAssignment).expression; - - // The below options all declare an Alias, which is allowed to merge with other values within the importing module. - // falls through - - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportClause: - let result = DeclarationSpaces.None; - const target = resolveAlias(getSymbolOfNode(d)!); - forEach(target.declarations, d => { - result |= getDeclarationSpaces(d); - }); - return result; - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind - .ImportSpecifier: // https://github.com/Microsoft/TypeScript/pull/7591 - return DeclarationSpaces.ExportValue; - default: - return Debug.failBadSyntaxKind(d); - } - } - } - - function getAwaitedTypeOfPromise( - type: Type, - errorNode?: Node, - diagnosticMessage?: DiagnosticMessage, - arg0?: string | number - ): Type | undefined { - const promisedType = getPromisedTypeOfPromise(type, errorNode); - return promisedType - && getAwaitedType( - promisedType, - errorNode, - diagnosticMessage, - arg0 - ); - } - - /** - * Gets the "promised type" of a promise. - * @param type The type of the promise. - * @remarks The "promised type" of a type is the type of the "value" parameter of the "onfulfilled" callback. - */ - function getPromisedTypeOfPromise( - promise: Type, - errorNode?: Node - ): Type | undefined { - // - // { // promise - // then( // thenFunction - // onfulfilled: ( // onfulfilledParameterType - // value: T // valueParameterType - // ) => any - // ): any; - // } - // - - if (isTypeAny(promise)) { - return undefined; - } - - const typeAsPromise = promise; - if (typeAsPromise.promisedTypeOfPromise) { - return typeAsPromise.promisedTypeOfPromise; - } - - if (isReferenceToType( - promise, - getGlobalPromiseType(/*reportErrors*/ false) - )) { - return typeAsPromise - .promisedTypeOfPromise = getTypeArguments( promise) - [0]; - } - - const thenFunction = getTypeOfPropertyOfType( - promise, - 'then' as __String - )!; // TODO: GH#18217 - if (isTypeAny(thenFunction)) { - return undefined; - } - - const thenSignatures = thenFunction - ? getSignaturesOfType(thenFunction, SignatureKind.Call) - : emptyArray; - if (thenSignatures.length === 0) { - if (errorNode) { - error( - errorNode, - Diagnostics.A_promise_must_have_a_then_method - ); - } - return undefined; - } - - const onfulfilledParameterType = getTypeWithFacts( - getUnionType(map( - thenSignatures, - getTypeOfFirstParameterOfSignature - )), - TypeFacts.NEUndefinedOrNull - ); - if (isTypeAny(onfulfilledParameterType)) { - return undefined; - } - - const onfulfilledParameterSignatures = getSignaturesOfType( - onfulfilledParameterType, - SignatureKind.Call - ); - if (onfulfilledParameterSignatures.length === 0) { - if (errorNode) { - error( - errorNode, - Diagnostics - .The_first_parameter_of_the_then_method_of_a_promise_must_be_a_callback - ); - } - return undefined; - } - - return typeAsPromise.promisedTypeOfPromise = getUnionType( - map( - onfulfilledParameterSignatures, - getTypeOfFirstParameterOfSignature - ), - UnionReduction.Subtype - ); - } - - /** - * Gets the "awaited type" of a type. - * @param type The type to await. - * @remarks The "awaited type" of an expression is its "promised type" if the expression is a - * Promise-like type; otherwise, it is the type of the expression. This is used to reflect - * The runtime behavior of the `await` keyword. - */ - function checkAwaitedType( - type: Type, - errorNode: Node, - diagnosticMessage: DiagnosticMessage, - arg0?: string | number - ): Type { - const awaitedType = getAwaitedType( - type, - errorNode, - diagnosticMessage, - arg0 - ); - return awaitedType || errorType; - } - - function getAwaitedType( - type: Type, - errorNode?: Node, - diagnosticMessage?: DiagnosticMessage, - arg0?: string | number - ): Type | undefined { - const typeAsAwaitable = type; - if (typeAsAwaitable.awaitedTypeOfType) { - return typeAsAwaitable.awaitedTypeOfType; - } - - if (isTypeAny(type)) { - return typeAsAwaitable.awaitedTypeOfType = type; - } - - if (type.flags & TypeFlags.Union) { - let types: Type[] | undefined; - for (const constituentType of ( type).types) { - types = append( - types, - getAwaitedType( - constituentType, - errorNode, - diagnosticMessage, - arg0 - ) - ); - } - - if (!types) { - return undefined; - } - - return typeAsAwaitable.awaitedTypeOfType = getUnionType(types); - } - - const promisedType = getPromisedTypeOfPromise(type); - if (promisedType) { - if (type.id === promisedType.id - || awaitedTypeStack.indexOf(promisedType.id) >= 0) - { - // Verify that we don't have a bad actor in the form of a promise whose - // promised type is the same as the promise type, or a mutually recursive - // promise. If so, we return undefined as we cannot guess the shape. If this - // were the actual case in the JavaScript, this Promise would never resolve. - // - // An example of a bad actor with a singly-recursive promise type might - // be: - // - // interface BadPromise { - // then( - // onfulfilled: (value: BadPromise) => any, - // onrejected: (error: any) => any): BadPromise; - // } - // The above interface will pass the PromiseLike check, and return a - // promised type of `BadPromise`. Since this is a self reference, we - // don't want to keep recursing ad infinitum. - // - // An example of a bad actor in the form of a mutually-recursive - // promise type might be: - // - // interface BadPromiseA { - // then( - // onfulfilled: (value: BadPromiseB) => any, - // onrejected: (error: any) => any): BadPromiseB; - // } - // - // interface BadPromiseB { - // then( - // onfulfilled: (value: BadPromiseA) => any, - // onrejected: (error: any) => any): BadPromiseA; - // } - // - if (errorNode) { - error( - errorNode, - Diagnostics - .Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method - ); - } - return undefined; - } - - // Keep track of the type we're about to unwrap to avoid bad recursive promise types. - // See the comments above for more information. - awaitedTypeStack.push(type.id); - const awaitedType = getAwaitedType( - promisedType, - errorNode, - diagnosticMessage, - arg0 - ); - awaitedTypeStack.pop(); - - if (!awaitedType) { - return undefined; - } - - return typeAsAwaitable.awaitedTypeOfType = awaitedType; - } - - // The type was not a promise, so it could not be unwrapped any further. - // As long as the type does not have a callable "then" property, it is - // safe to return the type; otherwise, an error will be reported in - // the call to getNonThenableType and we will return undefined. - // - // An example of a non-promise "thenable" might be: - // - // await { then(): void {} } - // - // The "thenable" does not match the minimal definition for a promise. When - // a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise - // will never settle. We treat this as an error to help flag an early indicator - // of a runtime problem. If the user wants to return this value from an async - // function, they would need to wrap it in some other value. If they want it to - // be treated as a promise, they can cast to . - const thenFunction = getTypeOfPropertyOfType( - type, - 'then' as __String - ); - if (thenFunction - && getSignaturesOfType(thenFunction, SignatureKind.Call).length - > 0) - { - if (errorNode) { - if (!diagnosticMessage) return Debug.fail(); - error(errorNode, diagnosticMessage, arg0); - } - return undefined; - } - - return typeAsAwaitable.awaitedTypeOfType = type; - } - - /** - * Checks the return type of an async function to ensure it is a compatible - * Promise implementation. - * - * This checks that an async function has a valid Promise-compatible return type. - * An async function has a valid Promise-compatible return type if the resolved value - * of the return type has a construct signature that takes in an `initializer` function - * that in turn supplies a `resolve` function as one of its arguments and results in an - * object with a callable `then` signature. - * - * @param node The signature to check - */ - function checkAsyncFunctionReturnType( - node: FunctionLikeDeclaration | MethodSignature, - returnTypeNode: TypeNode - ) { - // As part of our emit for an async function, we will need to emit the entity name of - // the return type annotation as an expression. To meet the necessary runtime semantics - // for __awaiter, we must also check that the type of the declaration (e.g. the static - // side or "constructor" of the promise type) is compatible `PromiseConstructorLike`. - // - // An example might be (from lib.es6.d.ts): - // - // interface Promise { ... } - // interface PromiseConstructor { - // new (...): Promise; - // } - // declare var Promise: PromiseConstructor; - // - // When an async function declares a return type annotation of `Promise`, we - // need to get the type of the `Promise` variable declaration above, which would - // be `PromiseConstructor`. - // - // The same case applies to a class: - // - // declare class Promise { - // constructor(...); - // then(...): Promise; - // } - // - const returnType = getTypeFromTypeNode(returnTypeNode); - - if (languageVersion >= ScriptTarget.ES2015) { - if (returnType === errorType) { - return; - } - const globalPromiseType = - getGlobalPromiseType(/*reportErrors*/ true); - if (globalPromiseType !== emptyGenericType - && !isReferenceToType(returnType, globalPromiseType)) - { - // The promise type was not a valid type reference to the global promise type, so we - // report an error and return the unknown type. - error( - returnTypeNode, - Diagnostics - .The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type - ); - return; - } - } else { - // Always mark the type node as referenced if it points to a value - markTypeNodeAsReferenced(returnTypeNode); - - if (returnType === errorType) { - return; - } - - const promiseConstructorName = - getEntityNameFromTypeNode(returnTypeNode); - if (promiseConstructorName === undefined) { - error( - returnTypeNode, - Diagnostics - .Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, - typeToString(returnType) - ); - return; - } - - const promiseConstructorSymbol = resolveEntityName( - promiseConstructorName, - SymbolFlags.Value, /*ignoreErrors*/ - true - ); - const promiseConstructorType = promiseConstructorSymbol - ? getTypeOfSymbol(promiseConstructorSymbol) - : errorType; - if (promiseConstructorType === errorType) { - if (promiseConstructorName.kind === SyntaxKind.Identifier - && promiseConstructorName.escapedText === 'Promise' - && getTargetType(returnType) - === getGlobalPromiseType(/*reportErrors*/ false)) - { - error( - returnTypeNode, - Diagnostics - .An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option - ); - } else { - error( - returnTypeNode, - Diagnostics - .Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, - entityNameToString(promiseConstructorName) - ); - } - return; - } - - const globalPromiseConstructorLikeType = - getGlobalPromiseConstructorLikeType(/*reportErrors*/ true); - if (globalPromiseConstructorLikeType === emptyObjectType) { - // If we couldn't resolve the global PromiseConstructorLike type we cannot verify - // compatibility with __awaiter. - error( - returnTypeNode, - Diagnostics - .Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, - entityNameToString(promiseConstructorName) - ); - return; - } - - if (!checkTypeAssignableTo( - promiseConstructorType, - globalPromiseConstructorLikeType, - returnTypeNode, - Diagnostics - .Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value - )) { - return; - } - - // Verify there is no local declaration that could collide with the promise constructor. - const rootName = promiseConstructorName - && getFirstIdentifier(promiseConstructorName); - const collidingSymbol = getSymbol( - node.locals!, - rootName.escapedText, - SymbolFlags.Value - ); - if (collidingSymbol) { - error( - collidingSymbol.valueDeclaration, - Diagnostics - .Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, - idText(rootName), - entityNameToString(promiseConstructorName) - ); - return; - } - } - checkAwaitedType( - returnType, - node, - Diagnostics - .The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - ); - } - - /** Check a decorator */ - function checkDecorator(node: Decorator): void { - const signature = getResolvedSignature(node); - const returnType = getReturnTypeOfSignature(signature); - if (returnType.flags & TypeFlags.Any) { - return; - } - - let expectedReturnType: Type; - const headMessage = - getDiagnosticHeadMessageForDecoratorResolution(node); - let errorInfo: DiagnosticMessageChain | undefined; - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - const classSymbol = getSymbolOfNode(node.parent); - const classConstructorType = getTypeOfSymbol(classSymbol); - expectedReturnType = getUnionType([classConstructorType, - voidType]); - break; - - case SyntaxKind.Parameter: - expectedReturnType = voidType; - errorInfo = chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any - ); - - break; - - case SyntaxKind.PropertyDeclaration: - expectedReturnType = voidType; - errorInfo = chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .The_return_type_of_a_property_decorator_function_must_be_either_void_or_any - ); - break; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - const methodType = getTypeOfNode(node.parent); - const descriptorType = - createTypedPropertyDescriptorType(methodType); - expectedReturnType = getUnionType([descriptorType, - voidType]); - break; - - default: - return Debug.fail(); - } - - checkTypeAssignableTo( - returnType, - expectedReturnType, - node, - headMessage, - () => errorInfo - ); - } - - /** - * If a TypeNode can be resolved to a value symbol imported from an external module, it is - * marked as referenced to prevent import elision. - */ - function markTypeNodeAsReferenced(node: TypeNode) { - markEntityNameOrEntityExpressionAsReference(node - && getEntityNameFromTypeNode(node)); - } - - function markEntityNameOrEntityExpressionAsReference(typeName: - EntityNameOrEntityNameExpression | undefined) - { - if (!typeName) return; - - const rootName = getFirstIdentifier(typeName); - const meaning = - (typeName.kind === SyntaxKind.Identifier - ? SymbolFlags.Type - : SymbolFlags.Namespace) | SymbolFlags.Alias; - const rootSymbol = resolveName( - rootName, - rootName.escapedText, - meaning, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isRefernce*/ - true - ); - if (rootSymbol - && rootSymbol.flags & SymbolFlags.Alias - && symbolIsValue(rootSymbol) - && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) - { - markAliasSymbolAsReferenced(rootSymbol); - } - } - - /** - * This function marks the type used for metadata decorator as referenced if it is import - * from external module. - * This is different from markTypeNodeAsReferenced because it tries to simplify type nodes in - * union and intersection type - * @param node - */ - function markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode - | undefined): void - { - const entityName = getEntityNameForDecoratorMetadata(node); - if (entityName && isEntityName(entityName)) { - markEntityNameOrEntityExpressionAsReference(entityName); - } - } - - function getEntityNameForDecoratorMetadata(node: TypeNode - | undefined): EntityName | undefined - { - if (node) { - switch (node.kind) { - case SyntaxKind.IntersectionType: - case SyntaxKind.UnionType: - return getEntityNameForDecoratorMetadataFromTypeList(( node) - .types); - - case SyntaxKind.ConditionalType: - return getEntityNameForDecoratorMetadataFromTypeList([( node) - .trueType, - ( node).falseType]); - - case SyntaxKind.ParenthesizedType: - return getEntityNameForDecoratorMetadata(( node) - .type); - - case SyntaxKind.TypeReference: - return ( node).typeName; - } - } - } - - function getEntityNameForDecoratorMetadataFromTypeList(types: - readonly TypeNode[]): EntityName | undefined - { - let commonEntityName: EntityName | undefined; - for (let typeNode of types) { - while (typeNode.kind === SyntaxKind.ParenthesizedType) { - typeNode = (typeNode as ParenthesizedTypeNode) - .type; // Skip parens if need be - } - if (typeNode.kind === SyntaxKind.NeverKeyword) { - continue; // Always elide `never` from the union/intersection if possible - } - if (!strictNullChecks - && (typeNode.kind === SyntaxKind.NullKeyword - || typeNode.kind === SyntaxKind.UndefinedKeyword)) - { - continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks - } - const individualEntityName = - getEntityNameForDecoratorMetadata(typeNode); - if (!individualEntityName) { - // Individual is something like string number - // So it would be serialized to either that type or object - // Safe to return here - return undefined; - } - - if (commonEntityName) { - // Note this is in sync with the transformation that happens for type node. - // Keep this in sync with serializeUnionOrIntersectionType - // Verify if they refer to same entity and is identifier - // return undefined if they dont match because we would emit object - if (!isIdentifier(commonEntityName) - || !isIdentifier(individualEntityName) - || commonEntityName.escapedText - !== individualEntityName.escapedText) - { - return undefined; - } - } else { - commonEntityName = individualEntityName; - } - } - return commonEntityName; - } - - function getParameterTypeNodeForDecoratorCheck(node: - ParameterDeclaration): TypeNode | undefined - { - const typeNode = getEffectiveTypeAnnotationNode(node); - return isRestParameter(node) - ? getRestParameterElementType(typeNode) - : typeNode; - } - - /** Check the decorators of a node */ - function checkDecorators(node: Node): void { - if (!node.decorators) { - return; - } - - // skip this check for nodes that cannot have decorators. These should have already had an error reported by - // checkGrammarDecorators. - if (!nodeCanBeDecorated(node, node.parent, node.parent.parent)) { - return; - } - - if (!compilerOptions.experimentalDecorators) { - error( - node, - Diagnostics - .Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_in_your_tsconfig_or_jsconfig_to_remove_this_warning - ); - } - - const firstDecorator = node.decorators[0]; - checkExternalEmitHelpers( - firstDecorator, - ExternalEmitHelpers.Decorate - ); - if (node.kind === SyntaxKind.Parameter) { - checkExternalEmitHelpers( - firstDecorator, - ExternalEmitHelpers.Param - ); - } - - if (compilerOptions.emitDecoratorMetadata) { - checkExternalEmitHelpers( - firstDecorator, - ExternalEmitHelpers.Metadata - ); - - // we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator. - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - const constructor = - getFirstConstructorWithBody( node); - if (constructor) { - for (const parameter of constructor.parameters) { - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); - } - } - break; - - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - const otherKind = node.kind === SyntaxKind.GetAccessor - ? SyntaxKind.SetAccessor - : SyntaxKind.GetAccessor; - const otherAccessor = - getDeclarationOfKind( - getSymbolOfNode(node as AccessorDeclaration), - otherKind - ); - markDecoratorMedataDataTypeNodeAsReferenced(getAnnotatedAccessorTypeNode(node as AccessorDeclaration) - || otherAccessor - && getAnnotatedAccessorTypeNode(otherAccessor)); - break; - case SyntaxKind.MethodDeclaration: - for (const parameter - of ( node).parameters) - { - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); - } - - markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveReturnTypeNode( node)); - break; - - case SyntaxKind.PropertyDeclaration: - markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveTypeAnnotationNode( node)); - break; - - case SyntaxKind.Parameter: - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck( node)); - const containingSignature = - (node as ParameterDeclaration).parent; - for (const parameter of containingSignature - .parameters) - { - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); - } - break; - } - } - - forEach(node.decorators, checkDecorator); - } - - function checkFunctionDeclaration(node: FunctionDeclaration): void { - if (produceDiagnostics) { - checkFunctionOrMethodDeclaration(node); - checkGrammarForGenerator(node); - checkCollisionWithRequireExportsInGeneratedCode( - node, - node.name! - ); - checkCollisionWithGlobalPromiseInGeneratedCode( - node, - node.name! - ); - } - } - - function checkJSDocTypeAliasTag(node: JSDocTypedefTag - | JSDocCallbackTag) - { - if (!node.typeExpression) { - // If the node had `@property` tags, `typeExpression` would have been set to the first property tag. - error( - node.name, - Diagnostics - .JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags - ); - } - - if (node.name) { - checkTypeNameIsReserved( - node.name, - Diagnostics.Type_alias_name_cannot_be_0 - ); - } - checkSourceElement(node.typeExpression); - } - - function checkJSDocTemplateTag(node: JSDocTemplateTag): void { - checkSourceElement(node.constraint); - for (const tp of node.typeParameters) { - checkSourceElement(tp); - } - } - - function checkJSDocTypeTag(node: JSDocTypeTag) { - checkSourceElement(node.typeExpression); - } - - function checkJSDocParameterTag(node: JSDocParameterTag) { - checkSourceElement(node.typeExpression); - if (!getParameterSymbolFromJSDoc(node)) { - const decl = getHostSignatureFromJSDoc(node); - // don't issue an error for invalid hosts -- just functions -- - // and give a better error message when the host function mentions `arguments` - // but the tag doesn't have an array type - if (decl) { - const i = getJSDocTags(decl).filter(isJSDocParameterTag) - .indexOf(node); - if (i > -1 && i < decl.parameters.length - && isBindingPattern(decl.parameters[i].name)) - { - return; - } - if (!containsArgumentsReference(decl)) { - if (isQualifiedName(node.name)) { - error( - node.name, - Diagnostics - .Qualified_name_0_is_not_allowed_without_a_leading_param_object_1, - entityNameToString(node.name), - entityNameToString(node.name.left) - ); - } else { - error( - node.name, - Diagnostics - .JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, - idText(node.name) - ); - } - } else if (findLast( - getJSDocTags(decl), - isJSDocParameterTag - ) === node - && node.typeExpression && node.typeExpression.type - && !isArrayType(getTypeFromTypeNode(node.typeExpression - .type))) - { - error( - node.name, - Diagnostics - .JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type, - idText(node.name.kind === SyntaxKind.QualifiedName - ? node.name.right - : node.name) - ); - } - } - } - } - - function checkJSDocFunctionType(node: JSDocFunctionType): void { - if (produceDiagnostics && !node.type - && !isJSDocConstructSignature(node)) - { - reportImplicitAny(node, anyType); - } - checkSignatureDeclaration(node); - } - - function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { - const classLike = getJSDocHost(node); - if (!isClassDeclaration(classLike) - && !isClassExpression(classLike)) - { - error( - classLike, - Diagnostics.JSDoc_0_is_not_attached_to_a_class, - idText(node.tagName) - ); - return; - } - - const augmentsTags = getJSDocTags(classLike) - .filter(isJSDocAugmentsTag); - Debug.assert(augmentsTags.length > 0); - if (augmentsTags.length > 1) { - error( - augmentsTags[1], - Diagnostics - .Class_declarations_cannot_have_more_than_one_augments_or_extends_tag - ); - } - - const name = - getIdentifierFromEntityNameExpression(node.class.expression); - const extend = getClassExtendsHeritageElement(classLike); - if (extend) { - const className = - getIdentifierFromEntityNameExpression(extend.expression); - if (className && name.escapedText !== className.escapedText) { - error( - name, - Diagnostics - .JSDoc_0_1_does_not_match_the_extends_2_clause, - idText(node.tagName), - idText(name), - idText(className) - ); - } - } - } - - function getIdentifierFromEntityNameExpression(node: Identifier - | PropertyAccessExpression): Identifier | PrivateIdentifier; - function getIdentifierFromEntityNameExpression(node: - Expression): Identifier | PrivateIdentifier | undefined; - function getIdentifierFromEntityNameExpression(node: - Expression): Identifier | PrivateIdentifier | undefined - { - switch (node.kind) { - case SyntaxKind.Identifier: - return node as Identifier; - case SyntaxKind.PropertyAccessExpression: - return (node as PropertyAccessExpression).name; - default: - return undefined; - } - } - - function checkFunctionOrMethodDeclaration(node: FunctionDeclaration - | MethodDeclaration | MethodSignature): void - { - checkDecorators(node); - checkSignatureDeclaration(node); - const functionFlags = getFunctionFlags(node); - - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name - && node.name.kind === SyntaxKind.ComputedPropertyName) - { - // This check will account for methods in class/interface declarations, - // as well as accessors in classes/object literals - checkComputedPropertyName(node.name); - } - - if (!hasNonBindableDynamicName(node)) { - // first we want to check the local symbol that contain this declaration - // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol - // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode - const symbol = getSymbolOfNode(node); - const localSymbol = node.localSymbol || symbol; - - // Since the javascript won't do semantic analysis like typescript, - // if the javascript file comes before the typescript file and both contain same name functions, - // checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function. - const firstDeclaration = find( - localSymbol.declarations, - // Get first non javascript function declaration - declaration => declaration.kind === node.kind - && !(declaration.flags & NodeFlags.JavaScriptFile) - ); - - // Only type check the symbol once - if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(localSymbol); - } - - if (symbol.parent) { - // run check once for the first declaration - if (getDeclarationOfKind(symbol, node.kind) === node) { - // run check on export symbol to check that modifiers agree across all exported declarations - checkFunctionOrConstructorSymbol(symbol); - } - } - } - - const body = node.kind === SyntaxKind.MethodSignature - ? undefined - : node.body; - checkSourceElement(body); - checkAllCodePathsInNonVoidFunctionReturnOrThrow( - node, - getReturnTypeFromAnnotation(node) - ); - - if (produceDiagnostics && !getEffectiveReturnTypeNode(node)) { - // Report an implicit any error if there is no body, no explicit return type, and node is not a private method - // in an ambient context - if (nodeIsMissing(body) && !isPrivateWithinAmbient(node)) { - reportImplicitAny(node, anyType); - } - - if (functionFlags & FunctionFlags.Generator - && nodeIsPresent(body)) - { - // A generator with a body and no type annotation can still cause errors. It can error if the - // yielded values have no common supertype, or it can give an implicit any error if it has no - // yielded values. The only way to trigger these errors is to try checking its return type. - getReturnTypeOfSignature(getSignatureFromDeclaration(node)); - } - } - - // A js function declaration can have a @type tag instead of a return type node, but that type must have a call signature - if (isInJSFile(node)) { - const typeTag = getJSDocTypeTag(node); - if (typeTag && typeTag.typeExpression - && !getContextualCallSignature( - getTypeFromTypeNode(typeTag.typeExpression), - node - )) - { - error( - typeTag, - Diagnostics - .The_type_of_a_function_declaration_must_match_the_function_s_signature - ); - } - } - } - - function registerForUnusedIdentifiersCheck(node: - PotentiallyUnusedIdentifier): void - { - // May be in a call such as getTypeOfNode that happened to call this. But potentiallyUnusedIdentifiers is only defined in the scope of `checkSourceFile`. - if (produceDiagnostics) { - const sourceFile = getSourceFileOfNode(node); - let potentiallyUnusedIdentifiers = - allPotentiallyUnusedIdentifiers.get(sourceFile.path); - if (!potentiallyUnusedIdentifiers) { - potentiallyUnusedIdentifiers = []; - allPotentiallyUnusedIdentifiers.set( - sourceFile.path, - potentiallyUnusedIdentifiers - ); - } - // TODO: GH#22580 - // Debug.assert(addToSeen(seenPotentiallyUnusedIdentifiers, getNodeId(node)), "Adding potentially-unused identifier twice"); - potentiallyUnusedIdentifiers.push(node); - } - } - - type PotentiallyUnusedIdentifier = SourceFile | ModuleDeclaration - | ClassLikeDeclaration | InterfaceDeclaration | Block | CaseBlock - | ForStatement | ForInStatement | ForOfStatement - | Exclude - | TypeAliasDeclaration | InferTypeNode; - - function checkUnusedIdentifiers( - potentiallyUnusedIdentifiers: - readonly PotentiallyUnusedIdentifier[], - addDiagnostic: AddUnusedDiagnostic - ) { - for (const node of potentiallyUnusedIdentifiers) { - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - checkUnusedClassMembers(node, addDiagnostic); - checkUnusedTypeParameters(node, addDiagnostic); - break; - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.Block: - case SyntaxKind.CaseBlock: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - checkUnusedLocalsAndParameters(node, addDiagnostic); - break; - case SyntaxKind.Constructor: - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - if (node - .body) - { // Don't report unused parameters in overloads - checkUnusedLocalsAndParameters( - node, - addDiagnostic - ); - } - checkUnusedTypeParameters(node, addDiagnostic); - break; - case SyntaxKind.MethodSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.InterfaceDeclaration: - checkUnusedTypeParameters(node, addDiagnostic); - break; - case SyntaxKind.InferType: - checkUnusedInferTypeParameter(node, addDiagnostic); - break; - default: - Debug.assertNever( - node, - 'Node should not have been registered for unused identifiers check' - ); - } - } - } - - function errorUnusedLocal( - declaration: Declaration, - name: string, - addDiagnostic: AddUnusedDiagnostic - ) { - const node = getNameOfDeclaration(declaration) || declaration; - const message = isTypeDeclaration(declaration) - ? Diagnostics._0_is_declared_but_never_used - : Diagnostics._0_is_declared_but_its_value_is_never_read; - addDiagnostic( - declaration, - UnusedKind.Local, - createDiagnosticForNode(node, message, name) - ); - } - - function isIdentifierThatStartsWithUnderscore(node: Node) { - return isIdentifier(node) - && idText(node).charCodeAt(0) === CharacterCodes._; - } - - function checkUnusedClassMembers( - node: ClassDeclaration | ClassExpression, - addDiagnostic: AddUnusedDiagnostic - ): void { - for (const member of node.members) { - switch (member.kind) { - case SyntaxKind.MethodDeclaration: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - if (member.kind === SyntaxKind.SetAccessor - && member.symbol.flags & SymbolFlags.GetAccessor) - { - // Already would have reported an error on the getter. - break; - } - const symbol = getSymbolOfNode(member); - if (!symbol.isReferenced - && (hasModifier(member, ModifierFlags.Private) - || isNamedDeclaration(member) - && isPrivateIdentifier(member.name))) - { - addDiagnostic( - member, - UnusedKind.Local, - createDiagnosticForNode( - member.name!, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - symbolToString(symbol) - ) - ); - } - break; - case SyntaxKind.Constructor: - for (const parameter - of ( member).parameters) - { - if (!parameter.symbol.isReferenced - && hasModifier( - parameter, - ModifierFlags.Private - )) - { - addDiagnostic( - parameter, - UnusedKind.Local, - createDiagnosticForNode( - parameter.name, - Diagnostics - .Property_0_is_declared_but_its_value_is_never_read, - symbolName(parameter.symbol) - ) - ); - } - } - break; - case SyntaxKind.IndexSignature: - case SyntaxKind.SemicolonClassElement: - // Can't be private - break; - default: - Debug.fail(); - } - } - } - - function checkUnusedInferTypeParameter( - node: InferTypeNode, - addDiagnostic: AddUnusedDiagnostic - ): void { - const { typeParameter } = node; - if (isTypeParameterUnused(typeParameter)) { - addDiagnostic( - node, - UnusedKind.Parameter, - createDiagnosticForNode( - node, - Diagnostics._0_is_declared_but_its_value_is_never_read, - idText(typeParameter.name) - ) - ); - } - } - - function checkUnusedTypeParameters( - node: ClassLikeDeclaration | SignatureDeclaration - | InterfaceDeclaration | TypeAliasDeclaration, - addDiagnostic: AddUnusedDiagnostic - ): void { - // Only report errors on the last declaration for the type parameter container; - // this ensures that all uses have been accounted for. - if (last(getSymbolOfNode(node).declarations) !== node) return; - - const typeParameters = getEffectiveTypeParameterDeclarations(node); - const seenParentsWithEveryUnused = - new NodeSet(); - - for (const typeParameter of typeParameters) { - if (!isTypeParameterUnused(typeParameter)) continue; - - const name = idText(typeParameter.name); - const { parent } = typeParameter; - if (parent.kind !== SyntaxKind.InferType - && parent.typeParameters!.every(isTypeParameterUnused)) - { - if (seenParentsWithEveryUnused.tryAdd(parent)) { - const range = isJSDocTemplateTag(parent) - ? // Whole @template tag - rangeOfNode(parent) - : // Include the `<>` in the error message - rangeOfTypeParameters(parent.typeParameters!); - const only = typeParameters.length === 1; - const message = only - ? Diagnostics - ._0_is_declared_but_its_value_is_never_read - : Diagnostics.All_type_parameters_are_unused; - const arg0 = only ? name : undefined; - addDiagnostic( - typeParameter, - UnusedKind.Parameter, - createFileDiagnostic( - getSourceFileOfNode(parent), - range.pos, - range.end - range.pos, - message, - arg0 - ) - ); - } - } else { - addDiagnostic( - typeParameter, - UnusedKind.Parameter, - createDiagnosticForNode( - typeParameter, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - name - ) - ); - } - } - } - function isTypeParameterUnused(typeParameter: - TypeParameterDeclaration): boolean - { - return !(getMergedSymbol(typeParameter.symbol).isReferenced! - & SymbolFlags.TypeParameter) - && !isIdentifierThatStartsWithUnderscore(typeParameter.name); - } - - function addToGroup( - map: Map<[K, V[]]>, - key: K, - value: V, - getKey: (key: K) => number | string - ): void { - const keyString = String(getKey(key)); - const group = map.get(keyString); - if (group) { - group[1].push(value); - } else { - map.set(keyString, [key, [value]]); - } - } - - function tryGetRootParameterDeclaration(node: - Node): ParameterDeclaration | undefined - { - return tryCast(getRootDeclaration(node), isParameter); - } - - function checkUnusedLocalsAndParameters( - nodeWithLocals: Node, - addDiagnostic: AddUnusedDiagnostic - ): void { - // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value. - const unusedImports = - createMap<[ImportClause, ImportedDeclaration[]]>(); - const unusedDestructures = - createMap<[ObjectBindingPattern, BindingElement[]]>(); - const unusedVariables = - createMap<[VariableDeclarationList, VariableDeclaration[]]>(); - nodeWithLocals.locals!.forEach(local => { - // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. - // If it's a type parameter merged with a parameter, check if the parameter-side is used. - if (local.flags & SymbolFlags.TypeParameter - ? !(local.flags & SymbolFlags.Variable - && !(local.isReferenced! & SymbolFlags.Variable)) - : local.isReferenced || local.exportSymbol) - { - return; - } - - for (const declaration of local.declarations) { - if (isAmbientModule(declaration) - || (isVariableDeclaration(declaration) - && isForInOrOfStatement(declaration.parent.parent) - || isImportedDeclaration(declaration)) - && isIdentifierThatStartsWithUnderscore(declaration - .name!)) - { - continue; - } - - if (isImportedDeclaration(declaration)) { - addToGroup( - unusedImports, - importClauseFromImported(declaration), - declaration, - getNodeId - ); - } else if (isBindingElement(declaration) - && isObjectBindingPattern(declaration.parent)) - { - // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. - const lastElement = last(declaration.parent.elements); - if (declaration === lastElement - || !last(declaration.parent.elements) - .dotDotDotToken) - { - addToGroup( - unusedDestructures, - declaration.parent, - declaration, - getNodeId - ); - } - } else if (isVariableDeclaration(declaration)) { - addToGroup( - unusedVariables, - declaration.parent, - declaration, - getNodeId - ); - } else { - const parameter = local.valueDeclaration - && tryGetRootParameterDeclaration(local - .valueDeclaration); - const name = local.valueDeclaration - && getNameOfDeclaration(local.valueDeclaration); - if (parameter && name) { - if (!isParameterPropertyDeclaration( - parameter, - parameter.parent - ) && !parameterIsThisKeyword(parameter) - && !isIdentifierThatStartsWithUnderscore(name)) - { - addDiagnostic( - parameter, - UnusedKind.Parameter, - createDiagnosticForNode( - name, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - symbolName(local) - ) - ); - } - } else { - errorUnusedLocal( - declaration, - symbolName(local), - addDiagnostic - ); - } - } - } - }); - unusedImports.forEach(([importClause, unuseds]) => { - const importDecl = importClause.parent; - const nDeclarations = (importClause.name ? 1 : 0) - + (importClause.namedBindings - ? (importClause.namedBindings.kind - === SyntaxKind.NamespaceImport - ? 1 - : importClause.namedBindings.elements.length) - : 0); - if (nDeclarations === unuseds.length) { - addDiagnostic( - importDecl, - UnusedKind.Local, - unuseds.length === 1 - ? createDiagnosticForNode( - importDecl, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - idText(first(unuseds).name!) - ) - : createDiagnosticForNode( - importDecl, - Diagnostics - .All_imports_in_import_declaration_are_unused - ) - ); - } else { - for (const unused of unuseds) { - errorUnusedLocal( - unused, - idText(unused.name!), - addDiagnostic - ); - } - } - }); - unusedDestructures.forEach(([bindingPattern, bindingElements]) => { - const kind = - tryGetRootParameterDeclaration(bindingPattern.parent) - ? UnusedKind.Parameter - : UnusedKind.Local; - if (bindingPattern.elements.length - === bindingElements.length) - { - if (bindingElements.length === 1 - && bindingPattern.parent.kind - === SyntaxKind.VariableDeclaration - && bindingPattern.parent.parent.kind - === SyntaxKind.VariableDeclarationList) - { - addToGroup( - unusedVariables, - bindingPattern.parent.parent, - bindingPattern.parent, - getNodeId - ); - } else { - addDiagnostic( - bindingPattern, - kind, - bindingElements.length === 1 - ? createDiagnosticForNode( - bindingPattern, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - bindingNameText(first(bindingElements) - .name) - ) - : createDiagnosticForNode( - bindingPattern, - Diagnostics - .All_destructured_elements_are_unused - ) - ); - } - } else { - for (const e of bindingElements) { - addDiagnostic( - e, - kind, - createDiagnosticForNode( - e, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - bindingNameText(e.name) - ) - ); - } - } - }); - unusedVariables.forEach(([declarationList, declarations]) => { - if (declarationList.declarations.length - === declarations.length) - { - addDiagnostic( - declarationList, - UnusedKind.Local, - declarations.length === 1 - ? createDiagnosticForNode( - first(declarations).name, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - bindingNameText(first(declarations).name) - ) - : createDiagnosticForNode( - declarationList.parent.kind - === SyntaxKind.VariableStatement - ? declarationList.parent - : declarationList, - Diagnostics.All_variables_are_unused - ) - ); - } else { - for (const decl of declarations) { - addDiagnostic( - decl, - UnusedKind.Local, - createDiagnosticForNode( - decl, - Diagnostics - ._0_is_declared_but_its_value_is_never_read, - bindingNameText(decl.name) - ) - ); - } - } - }); - } - - function bindingNameText(name: BindingName): string { - switch (name.kind) { - case SyntaxKind.Identifier: - return idText(name); - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ObjectBindingPattern: - return bindingNameText(cast( - first(name.elements), - isBindingElement - ).name); - default: - return Debug.assertNever(name); - } - } - - type ImportedDeclaration = ImportClause | ImportSpecifier - | NamespaceImport; - function isImportedDeclaration(node: - Node): node is ImportedDeclaration - { - return node.kind === SyntaxKind.ImportClause - || node.kind === SyntaxKind.ImportSpecifier - || node.kind === SyntaxKind.NamespaceImport; - } - function importClauseFromImported(decl: - ImportedDeclaration): ImportClause - { - return decl.kind === SyntaxKind.ImportClause - ? decl - : decl.kind === SyntaxKind.NamespaceImport ? decl.parent - : decl.parent.parent; - } - - function checkBlock(node: Block) { - // Grammar checking for SyntaxKind.Block - if (node.kind === SyntaxKind.Block) { - checkGrammarStatementInAmbientContext(node); - } - if (isFunctionOrModuleBlock(node)) { - const saveFlowAnalysisDisabled = flowAnalysisDisabled; - forEach(node.statements, checkSourceElement); - flowAnalysisDisabled = saveFlowAnalysisDisabled; - } else { - forEach(node.statements, checkSourceElement); - } - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkCollisionWithArgumentsInGeneratedCode(node: - SignatureDeclaration) - { - // no rest parameters \ declaration context \ overload - no codegen impact - if (languageVersion >= ScriptTarget.ES2015 - || compilerOptions.noEmit || !hasRestParameter(node) - || node.flags & NodeFlags.Ambient - || nodeIsMissing(( node).body)) - { - return; - } - - forEach(node.parameters, p => { - if (p.name && !isBindingPattern(p.name) - && p.name.escapedText === argumentsSymbol.escapedName) - { - error( - p, - Diagnostics - .Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters - ); - } - }); - } - - function needCollisionCheckForIdentifier( - node: Node, - identifier: Identifier | undefined, - name: string - ): boolean { - if (!(identifier && identifier.escapedText === name)) { - return false; - } - - if (node.kind === SyntaxKind.PropertyDeclaration - || node.kind === SyntaxKind.PropertySignature - || node.kind === SyntaxKind.MethodDeclaration - || node.kind === SyntaxKind.MethodSignature - || node.kind === SyntaxKind.GetAccessor - || node.kind === SyntaxKind.SetAccessor) - { - // it is ok to have member named '_super' or '_this' - member access is always qualified - return false; - } - - if (node.flags & NodeFlags.Ambient) { - // ambient context - no codegen impact - return false; - } - - const root = getRootDeclaration(node); - if (root.kind === SyntaxKind.Parameter - && nodeIsMissing(( root.parent).body)) - { - // just an overload - no codegen impact - return false; - } - - return true; - } - - // this function will run after checking the source file so 'CaptureThis' is correct for all nodes - function checkIfThisIsCapturedInEnclosingScope(node: Node): void { - findAncestor(node, current => { - if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) { - const isDeclaration = node.kind !== SyntaxKind.Identifier; - if (isDeclaration) { - error( - getNameOfDeclaration( node), - Diagnostics - .Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference - ); - } else { - error( - node, - Diagnostics - .Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference - ); - } - return true; - } - return false; - }); - } - - function checkIfNewTargetIsCapturedInEnclosingScope(node: Node): void { - findAncestor(node, current => { - if (getNodeCheckFlags(current) - & NodeCheckFlags.CaptureNewTarget) - { - const isDeclaration = node.kind !== SyntaxKind.Identifier; - if (isDeclaration) { - error( - getNameOfDeclaration( node), - Diagnostics - .Duplicate_identifier_newTarget_Compiler_uses_variable_declaration_newTarget_to_capture_new_target_meta_property_reference - ); - } else { - error( - node, - Diagnostics - .Expression_resolves_to_variable_declaration_newTarget_that_compiler_uses_to_capture_new_target_meta_property_reference - ); - } - return true; - } - return false; - }); - } - - function checkWeakMapCollision(node: Node) { - const enclosingBlockScope = getEnclosingBlockScopeContainer(node); - if (getNodeCheckFlags(enclosingBlockScope) - & NodeCheckFlags.ContainsClassWithPrivateIdentifiers) - { - error( - node, - Diagnostics - .Compiler_reserves_name_0_when_emitting_private_identifier_downlevel, - 'WeakMap' - ); - } - } - - function checkCollisionWithRequireExportsInGeneratedCode( - node: Node, - name: Identifier - ) { - // No need to check for require or exports for ES6 modules and later - if (moduleKind >= ModuleKind.ES2015 || compilerOptions.noEmit) { - return; - } - - if (!needCollisionCheckForIdentifier(node, name, 'require') - && !needCollisionCheckForIdentifier(node, name, 'exports')) - { - return; - } - - // Uninstantiated modules shouldnt do this check - if (isModuleDeclaration(node) - && getModuleInstanceState(node) - !== ModuleInstanceState.Instantiated) - { - return; - } - - // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent - const parent = getDeclarationContainer(node); - if (parent.kind === SyntaxKind.SourceFile - && isExternalOrCommonJsModule( parent)) - { - // If the declaration happens to be in external module, report error that require and exports are reserved keywords - error( - name, - Diagnostics - .Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, - declarationNameToString(name), - declarationNameToString(name) - ); - } - } - - function checkCollisionWithGlobalPromiseInGeneratedCode( - node: Node, - name: Identifier - ): void { - if (languageVersion >= ScriptTarget.ES2017 - || compilerOptions.noEmit - || !needCollisionCheckForIdentifier(node, name, 'Promise')) - { - return; - } - - // Uninstantiated modules shouldnt do this check - if (isModuleDeclaration(node) - && getModuleInstanceState(node) - !== ModuleInstanceState.Instantiated) - { - return; - } - - // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent - const parent = getDeclarationContainer(node); - if (parent.kind === SyntaxKind.SourceFile - && isExternalOrCommonJsModule( parent) - && parent.flags & NodeFlags.HasAsyncFunctions) - { - // If the declaration happens to be in external module, report error that Promise is a reserved identifier. - error( - name, - Diagnostics - .Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions, - declarationNameToString(name), - declarationNameToString(name) - ); - } - } - - function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration - | BindingElement) - { - // - ScriptBody : StatementList - // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList - // also occurs in the VarDeclaredNames of StatementList. - - // - Block : { StatementList } - // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList - // also occurs in the VarDeclaredNames of StatementList. - - // Variable declarations are hoisted to the top of their function scope. They can shadow - // block scoped declarations, which bind tighter. this will not be flagged as duplicate definition - // by the binder as the declaration scope is different. - // A non-initialized declaration is a no-op as the block declaration will resolve before the var - // declaration. the problem is if the declaration has an initializer. this will act as a write to the - // block declared value. this is fine for let, but not const. - // Only consider declarations with initializers, uninitialized const declarations will not - // step on a let/const variable. - // Do not consider const and const declarations, as duplicate block-scoped declarations - // are handled by the binder. - // We are only looking for const declarations that step on let\const declarations from a - // different scope. e.g.: - // { - // const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration - // const x = 0; // symbol for this declaration will be 'symbol' - // } - - // skip block-scoped variables and parameters - if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 - || isParameterDeclaration(node)) - { - return; - } - - // skip variable declarations that don't have initializers - // NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern - // so we'll always treat binding elements as initialized - if (node.kind === SyntaxKind.VariableDeclaration - && !node.initializer) - { - return; - } - - const symbol = getSymbolOfNode(node); - if (symbol.flags & SymbolFlags.FunctionScopedVariable) { - if (!isIdentifier(node.name)) return Debug.fail(); - const localDeclarationSymbol = resolveName( - node, - node.name.escapedText, - SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - ); - if (localDeclarationSymbol - && localDeclarationSymbol !== symbol - && localDeclarationSymbol.flags - & SymbolFlags.BlockScopedVariable) - { - if (getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol) - & NodeFlags.BlockScoped) - { - const varDeclList = getAncestor( - localDeclarationSymbol.valueDeclaration, - SyntaxKind.VariableDeclarationList - )!; - const container = - varDeclList.parent.kind - === SyntaxKind.VariableStatement - && varDeclList.parent.parent - ? varDeclList.parent.parent - : undefined; - - // names of block-scoped and function scoped variables can collide only - // if block scoped variable is defined in the function\module\source file scope (because of variable hoisting) - const namesShareScope = container - && (container.kind === SyntaxKind.Block - && isFunctionLike(container.parent) - || container.kind === SyntaxKind.ModuleBlock - || container.kind - === SyntaxKind.ModuleDeclaration - || container.kind === SyntaxKind.SourceFile); - - // here we know that function scoped variable is shadowed by block scoped one - // if they are defined in the same scope - binder has already reported redeclaration error - // otherwise if variable has an initializer - show error that initialization will fail - // since LHS will be block scoped name instead of function scoped - if (!namesShareScope) { - const name = - symbolToString(localDeclarationSymbol); - error( - node, - Diagnostics - .Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, - name, - name - ); - } - } - } - } - } - - function convertAutoToAny(type: Type) { - return type === autoType - ? anyType - : type === autoArrayType ? anyArrayType : type; - } - - // Check variable, parameter, or property declaration - function checkVariableLikeDeclaration(node: ParameterDeclaration - | PropertyDeclaration | PropertySignature | VariableDeclaration - | BindingElement) - { - checkDecorators(node); - if (!isBindingElement(node)) { - checkSourceElement(node.type); - } - - // JSDoc `function(string, string): string` syntax results in parameters with no name - if (!node.name) { - return; - } - // For a computed property, just check the initializer and exit - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - checkComputedPropertyName(node.name); - if (node.initializer) { - checkExpressionCached(node.initializer); - } - } - - if (node.kind === SyntaxKind.BindingElement) { - if (node.parent.kind === SyntaxKind.ObjectBindingPattern - && languageVersion < ScriptTarget.ESNext) - { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest); - } - // check computed properties inside property names of binding elements - if (node.propertyName - && node.propertyName.kind - === SyntaxKind.ComputedPropertyName) - { - checkComputedPropertyName(node.propertyName); - } - - // check private/protected variable access - const parent = node.parent.parent; - const parentType = getTypeForBindingElementParent(parent); - const name = node.propertyName || node.name; - if (parentType && !isBindingPattern(name)) { - const exprType = getLiteralTypeFromPropertyName(name); - if (isTypeUsableAsPropertyName(exprType)) { - const nameText = getPropertyNameFromType(exprType); - const property = getPropertyOfType( - parentType, - nameText - ); - if (property) { - markPropertyAsReferenced( - property, /*nodeForCheckWriteOnly*/ - undefined, /*isThisAccess*/ - false - ); // A destructuring is never a write-only reference. - checkPropertyAccessibility( - parent, - !!parent.initializer - && parent.initializer.kind - === SyntaxKind.SuperKeyword, - parentType, - property - ); - } - } - } - } - - // For a binding pattern, check contained binding elements - if (isBindingPattern(node.name)) { - if (node.name.kind === SyntaxKind.ArrayBindingPattern - && languageVersion < ScriptTarget.ES2015 - && compilerOptions.downlevelIteration) - { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Read); - } - - forEach(node.name.elements, checkSourceElement); - } - // For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body - if (node.initializer - && getRootDeclaration(node).kind === SyntaxKind.Parameter - && nodeIsMissing((getContainingFunction(node) as FunctionLikeDeclaration) - .body)) - { - error( - node, - Diagnostics - .A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation - ); - return; - } - // For a binding pattern, validate the initializer and exit - if (isBindingPattern(node.name)) { - const needCheckInitializer = node.initializer - && node.parent.parent.kind !== SyntaxKind.ForInStatement; - const needCheckWidenedType = node.name.elements.length === 0; - if (needCheckInitializer || needCheckWidenedType) { - // Don't validate for-in initializer as it is already an error - const widenedType = - getWidenedTypeForVariableLikeDeclaration(node); - if (needCheckInitializer) { - const initializerType = - checkExpressionCached(node.initializer!); - if (strictNullChecks && needCheckWidenedType) { - checkNonNullNonVoidType(initializerType, node); - } else { - checkTypeAssignableToAndOptionallyElaborate( - initializerType, - getWidenedTypeForVariableLikeDeclaration(node), - node, - node.initializer - ); - } - } - // check the binding pattern with empty elements - if (needCheckWidenedType) { - if (isArrayBindingPattern(node.name)) { - checkIteratedTypeOrElementType( - IterationUse.Destructuring, - widenedType, - undefinedType, - node - ); - } else if (strictNullChecks) { - checkNonNullNonVoidType(widenedType, node); - } - } - } - return; - } - const symbol = getSymbolOfNode(node); - const type = convertAutoToAny(getTypeOfSymbol(symbol)); - if (node === symbol.valueDeclaration) { - // Node is the primary declaration of the symbol, just validate the initializer - // Don't validate for-in initializer as it is already an error - const initializer = getEffectiveInitializer(node); - if (initializer) { - const isJSObjectLiteralInitializer = isInJSFile(node) - && isObjectLiteralExpression(initializer) - && (initializer.properties.length === 0 - || isPrototypeAccess(node.name)) - && hasEntries(symbol.exports); - if (!isJSObjectLiteralInitializer - && node.parent.parent.kind - !== SyntaxKind.ForInStatement) - { - checkTypeAssignableToAndOptionallyElaborate( - checkExpressionCached(initializer), - type, - node, - initializer, /*headMessage*/ - undefined - ); - } - } - if (symbol.declarations.length > 1) { - if (some( - symbol.declarations, - d => d !== node && isVariableLike(d) - && !areDeclarationFlagsIdentical(d, node) - )) { - error( - node.name, - Diagnostics - .All_declarations_of_0_must_have_identical_modifiers, - declarationNameToString(node.name) - ); - } - } - } else { - // Node is a secondary declaration, check that type is identical to primary declaration and check that - // initializer is consistent with type associated with the node - const declarationType = - convertAutoToAny(getWidenedTypeForVariableLikeDeclaration(node)); - - if (type !== errorType && declarationType !== errorType - && !isTypeIdenticalTo(type, declarationType) - && !(symbol.flags & SymbolFlags.Assignment)) - { - errorNextVariableOrPropertyDeclarationMustHaveSameType( - symbol.valueDeclaration, - type, - node, - declarationType - ); - } - if (node.initializer) { - checkTypeAssignableToAndOptionallyElaborate( - checkExpressionCached(node.initializer), - declarationType, - node, - node.initializer, /*headMessage*/ - undefined - ); - } - if (!areDeclarationFlagsIdentical( - node, - symbol.valueDeclaration - )) { - error( - node.name, - Diagnostics - .All_declarations_of_0_must_have_identical_modifiers, - declarationNameToString(node.name) - ); - } - } - if (node.kind !== SyntaxKind.PropertyDeclaration - && node.kind !== SyntaxKind.PropertySignature) - { - // We know we don't have a binding pattern or computed name here - checkExportsOnMergedDeclarations(node); - if (node.kind === SyntaxKind.VariableDeclaration - || node.kind === SyntaxKind.BindingElement) - { - checkVarDeclaredNamesNotShadowed(node); - } - checkCollisionWithRequireExportsInGeneratedCode( - node, - node.name - ); - checkCollisionWithGlobalPromiseInGeneratedCode( - node, - node.name - ); - if (!compilerOptions.noEmit - && languageVersion < ScriptTarget.ESNext - && needCollisionCheckForIdentifier( - node, - node.name as Identifier, - 'WeakMap' - )) - { - potentialWeakMapCollisions.push(node); - } - } - } - - function errorNextVariableOrPropertyDeclarationMustHaveSameType( - firstDeclaration: Declaration | undefined, - firstType: Type, - nextDeclaration: Declaration, - nextType: Type - ): void { - const nextDeclarationName = getNameOfDeclaration(nextDeclaration); - const message = - nextDeclaration.kind === SyntaxKind.PropertyDeclaration - || nextDeclaration.kind === SyntaxKind.PropertySignature - ? Diagnostics - .Subsequent_property_declarations_must_have_the_same_type_Property_0_must_be_of_type_1_but_here_has_type_2 - : Diagnostics - .Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2; - const declName = declarationNameToString(nextDeclarationName); - const err = error( - nextDeclarationName, - message, - declName, - typeToString(firstType), - typeToString(nextType) - ); - if (firstDeclaration) { - addRelatedInfo( - err, - createDiagnosticForNode( - firstDeclaration, - Diagnostics._0_was_also_declared_here, - declName - ) - ); - } - } - - function areDeclarationFlagsIdentical( - left: Declaration, - right: Declaration - ) { - if ((left.kind === SyntaxKind.Parameter - && right.kind === SyntaxKind.VariableDeclaration) - || (left.kind === SyntaxKind.VariableDeclaration - && right.kind === SyntaxKind.Parameter)) - { - // Differences in optionality between parameters and variables are allowed. - return true; - } - - if (hasQuestionToken(left) !== hasQuestionToken(right)) { - return false; - } - - const interestingFlags = ModifierFlags.Private - | ModifierFlags.Protected - | ModifierFlags.Async - | ModifierFlags.Abstract - | ModifierFlags.Readonly - | ModifierFlags.Static; - - return getSelectedModifierFlags(left, interestingFlags) - === getSelectedModifierFlags(right, interestingFlags); - } - - function checkVariableDeclaration(node: VariableDeclaration) { - checkGrammarVariableDeclaration(node); - return checkVariableLikeDeclaration(node); - } - - function checkBindingElement(node: BindingElement) { - checkGrammarBindingElement(node); - return checkVariableLikeDeclaration(node); - } - - function checkVariableStatement(node: VariableStatement) { - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node) - && !checkGrammarVariableDeclarationList(node.declarationList)) - { - checkGrammarForDisallowedLetOrConstStatement(node); - } - forEach(node.declarationList.declarations, checkSourceElement); - } - - function checkExpressionStatement(node: ExpressionStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkExpression(node.expression); - } - - function checkIfStatement(node: IfStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - const type = checkTruthinessExpression(node.expression); - checkTestingKnownTruthyCallableType(node, type); - checkSourceElement(node.thenStatement); - - if (node.thenStatement.kind === SyntaxKind.EmptyStatement) { - error( - node.thenStatement, - Diagnostics - .The_body_of_an_if_statement_cannot_be_the_empty_statement - ); - } - - checkSourceElement(node.elseStatement); - } - - function checkTestingKnownTruthyCallableType( - ifStatement: IfStatement, - type: Type - ) { - if (!strictNullChecks) { - return; - } - - const testedNode = isIdentifier(ifStatement.expression) - ? ifStatement.expression - : isPropertyAccessExpression(ifStatement.expression) - ? ifStatement.expression.name - : undefined; - - if (!testedNode) { - return; - } - - const possiblyFalsy = getFalsyFlags(type); - if (possiblyFalsy) { - return; - } - - // While it technically should be invalid for any known-truthy value - // to be tested, we de-scope to functions unrefenced in the block as a - // heuristic to identify the most common bugs. There are too many - // false positives for values sourced from type definitions without - // strictNullChecks otherwise. - const callSignatures = getSignaturesOfType( - type, - SignatureKind.Call - ); - if (callSignatures.length === 0) { - return; - } - - const testedFunctionSymbol = getSymbolAtLocation(testedNode); - if (!testedFunctionSymbol) { - return; - } - - const functionIsUsedInBody = forEachChild( - ifStatement.thenStatement, - function check(childNode): boolean | undefined { - if (isIdentifier(childNode)) { - const childSymbol = getSymbolAtLocation(childNode); - if (childSymbol - && childSymbol.id === testedFunctionSymbol.id) - { - return true; - } - } - - return forEachChild(childNode, check); - } - ); - - if (!functionIsUsedInBody) { - error( - ifStatement.expression, - Diagnostics - .This_condition_will_always_return_true_since_the_function_is_always_defined_Did_you_mean_to_call_it_instead - ); - } - } - - function checkDoStatement(node: DoStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkSourceElement(node.statement); - checkTruthinessExpression(node.expression); - } - - function checkWhileStatement(node: WhileStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkTruthinessExpression(node.expression); - checkSourceElement(node.statement); - } - - function checkTruthinessExpression( - node: Expression, - checkMode?: CheckMode - ) { - const type = checkExpression(node, checkMode); - if (type.flags & TypeFlags.Void) { - error( - node, - Diagnostics - .An_expression_of_type_void_cannot_be_tested_for_truthiness - ); - } - return type; - } - - function checkForStatement(node: ForStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.initializer - && node.initializer.kind - === SyntaxKind.VariableDeclarationList) - { - checkGrammarVariableDeclarationList( node - .initializer); - } - } - - if (node.initializer) { - if (node.initializer.kind - === SyntaxKind.VariableDeclarationList) - { - forEach( - ( node.initializer) - .declarations, - checkVariableDeclaration - ); - } else { - checkExpression(node.initializer); - } - } - - if (node.condition) checkTruthinessExpression(node.condition); - if (node.incrementor) checkExpression(node.incrementor); - checkSourceElement(node.statement); - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkForOfStatement(node: ForOfStatement): void { - checkGrammarForInOrForOfStatement(node); - - if (node.awaitModifier) { - const functionFlags = - getFunctionFlags(getContainingFunction(node)); - if ((functionFlags - & (FunctionFlags.Invalid | FunctionFlags.Async)) - === FunctionFlags.Async - && languageVersion < ScriptTarget.ESNext) - { - // for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.ForAwaitOfIncludes - ); - } - } else if (compilerOptions.downlevelIteration - && languageVersion < ScriptTarget.ES2015) - { - // for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.ForOfIncludes - ); - } - - // Check the LHS and RHS - // If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS - // via checkRightHandSideOfForOf. - // If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference. - // Then check that the RHS is assignable to it. - if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { - checkForInOrForOfVariableDeclaration(node); - } else { - const varExpr = node.initializer; - const iteratedType = checkRightHandSideOfForOf( - node.expression, - node.awaitModifier - ); - - // There may be a destructuring assignment on the left side - if (varExpr.kind === SyntaxKind.ArrayLiteralExpression - || varExpr.kind === SyntaxKind.ObjectLiteralExpression) - { - // iteratedType may be undefined. In this case, we still want to check the structure of - // varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like - // to short circuit the type relation checking as much as possible, so we pass the unknownType. - checkDestructuringAssignment( - varExpr, - iteratedType || errorType - ); - } else { - const leftType = checkExpression(varExpr); - checkReferenceExpression( - varExpr, - Diagnostics - .The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access, - Diagnostics - .The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access - ); - - // iteratedType will be undefined if the rightType was missing properties/signatures - // required to get its iteratedType (like [Symbol.iterator] or next). This may be - // because we accessed properties from anyType, or it may have led to an error inside - // getElementTypeOfIterable. - if (iteratedType) { - checkTypeAssignableToAndOptionallyElaborate( - iteratedType, - leftType, - varExpr, - node.expression - ); - } - } - } - - checkSourceElement(node.statement); - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkForInStatement(node: ForInStatement) { - // Grammar checking - checkGrammarForInOrForOfStatement(node); - - const rightType = - getNonNullableTypeIfNeeded(checkExpression(node.expression)); - // TypeScript 1.0 spec (April 2014): 5.4 - // In a 'for-in' statement of the form - // for (let VarDecl in Expr) Statement - // VarDecl must be a variable declaration without a type annotation that declares a variable of type Any, - // and Expr must be an expression of type Any, an object type, or a type parameter type. - if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { - const variable = ( node.initializer) - .declarations[0]; - if (variable && isBindingPattern(variable.name)) { - error( - variable.name, - Diagnostics - .The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern - ); - } - checkForInOrForOfVariableDeclaration(node); - } else { - // In a 'for-in' statement of the form - // for (Var in Expr) Statement - // Var must be an expression classified as a reference of type Any or the String primitive type, - // and Expr must be an expression of type Any, an object type, or a type parameter type. - const varExpr = node.initializer; - const leftType = checkExpression(varExpr); - if (varExpr.kind === SyntaxKind.ArrayLiteralExpression - || varExpr.kind === SyntaxKind.ObjectLiteralExpression) - { - error( - varExpr, - Diagnostics - .The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern - ); - } else if (!isTypeAssignableTo( - getIndexTypeOrString(rightType), - leftType - )) { - error( - varExpr, - Diagnostics - .The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any - ); - } else { - // run check only former check succeeded to avoid cascading errors - checkReferenceExpression( - varExpr, - Diagnostics - .The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access, - Diagnostics - .The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access - ); - } - } - - // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved - // in this case error about missing name is already reported - do not report extra one - if (rightType === neverType - || !isTypeAssignableToKind( - rightType, - TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive - )) - { - error( - node.expression, - Diagnostics - .The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter_but_here_has_type_0, - typeToString(rightType) - ); - } - - checkSourceElement(node.statement); - if (node.locals) { - registerForUnusedIdentifiersCheck(node); - } - } - - function checkForInOrForOfVariableDeclaration(iterationStatement: - ForInOrOfStatement): void - { - const variableDeclarationList = - iterationStatement.initializer; - // checkGrammarForInOrForOfStatement will check that there is exactly one declaration. - if (variableDeclarationList.declarations.length >= 1) { - const decl = variableDeclarationList.declarations[0]; - checkVariableDeclaration(decl); - } - } - - function checkRightHandSideOfForOf( - rhsExpression: Expression, - awaitModifier: AwaitKeywordToken | undefined - ): Type { - const expressionType = checkNonNullExpression(rhsExpression); - const use = awaitModifier - ? IterationUse.ForAwaitOf - : IterationUse.ForOf; - return checkIteratedTypeOrElementType( - use, - expressionType, - undefinedType, - rhsExpression - ); - } - - function checkIteratedTypeOrElementType( - use: IterationUse, - inputType: Type, - sentType: Type, - errorNode: Node | undefined - ): Type { - if (isTypeAny(inputType)) { - return inputType; - } - - return getIteratedTypeOrElementType( - use, - inputType, - sentType, - errorNode, /*checkAssignability*/ - true - ) || anyType; - } - - /** - * When consuming an iterable type in a for..of, spread, or iterator destructuring assignment - * we want to get the iterated type of an iterable for ES2015 or later, or the iterated type - * of a iterable (if defined globally) or element type of an array like for ES2015 or earlier. - */ - function getIteratedTypeOrElementType( - use: IterationUse, - inputType: Type, - sentType: Type, - errorNode: Node | undefined, - checkAssignability: boolean - ): Type | undefined { - const allowAsyncIterables = - (use & IterationUse.AllowsAsyncIterablesFlag) !== 0; - if (inputType === neverType) { - reportTypeNotIterableError( - errorNode!, - inputType, - allowAsyncIterables - ); // TODO: GH#18217 - return undefined; - } - - const uplevelIteration = languageVersion >= ScriptTarget.ES2015; - const downlevelIteration = !uplevelIteration - && compilerOptions.downlevelIteration; - - // Get the iterated type of an `Iterable` or `IterableIterator` only in ES2015 - // or higher, when inside of an async generator or for-await-if, or when - // downlevelIteration is requested. - if (uplevelIteration || downlevelIteration - || allowAsyncIterables) - { - // We only report errors for an invalid iterable type in ES2015 or higher. - const iterationTypes = getIterationTypesOfIterable( - inputType, - use, - uplevelIteration ? errorNode : undefined - ); - if (checkAssignability) { - if (iterationTypes) { - const diagnostic = use & IterationUse.ForOfFlag - ? Diagnostics - .Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_for_of_will_always_send_0 - : use & IterationUse.SpreadFlag - ? Diagnostics - .Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_spread_will_always_send_0 - : use & IterationUse.DestructuringFlag - ? Diagnostics - .Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_destructuring_will_always_send_0 - : use & IterationUse.YieldStarFlag - ? Diagnostics - .Cannot_delegate_iteration_to_value_because_the_next_method_of_its_iterator_expects_type_1_but_the_containing_generator_will_always_send_0 - : undefined; - if (diagnostic) { - checkTypeAssignableTo( - sentType, - iterationTypes.nextType, - errorNode, - diagnostic - ); - } - } - } - if (iterationTypes || uplevelIteration) { - return iterationTypes && iterationTypes.yieldType; - } - } - - let arrayType = inputType; - let reportedError = false; - let hasStringConstituent = false; - - // If strings are permitted, remove any string-like constituents from the array type. - // This allows us to find other non-string element types from an array unioned with - // a string. - if (use & IterationUse.AllowsStringInputFlag) { - if (arrayType.flags & TypeFlags.Union) { - // After we remove all types that are StringLike, we will know if there was a string constituent - // based on whether the result of filter is a new array. - const arrayTypes = ( inputType).types; - const filteredTypes = filter( - arrayTypes, - t => !(t.flags & TypeFlags.StringLike) - ); - if (filteredTypes !== arrayTypes) { - arrayType = getUnionType( - filteredTypes, - UnionReduction.Subtype - ); - } - } else if (arrayType.flags & TypeFlags.StringLike) { - arrayType = neverType; - } - - hasStringConstituent = arrayType !== inputType; - if (hasStringConstituent) { - if (languageVersion < ScriptTarget.ES5) { - if (errorNode) { - error( - errorNode, - Diagnostics - .Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher - ); - reportedError = true; - } - } - - // Now that we've removed all the StringLike types, if no constituents remain, then the entire - // arrayOrStringType was a string. - if (arrayType.flags & TypeFlags.Never) { - return stringType; - } - } - } - - if (!isArrayLikeType(arrayType)) { - if (errorNode && !reportedError) { - // Which error we report depends on whether we allow strings or if there was a - // string constituent. For example, if the input type is number | string, we - // want to say that number is not an array type. But if the input was just - // number and string input is allowed, we want to say that number is not an - // array type or a string type. - const yieldType = getIterationTypeOfIterable( - use, - IterationTypeKind.Yield, - inputType, /*errorNode*/ - undefined - ); - const [defaultDiagnostic, maybeMissingAwait]: - [DiagnosticMessage, boolean] = - !(use & IterationUse.AllowsStringInputFlag) - || hasStringConstituent - ? downlevelIteration - ? [Diagnostics - .Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, - true] - : yieldType - ? [Diagnostics - .Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators, - false] - : [Diagnostics.Type_0_is_not_an_array_type, - true] - : downlevelIteration - ? [Diagnostics - .Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, - true] - : yieldType - ? [Diagnostics - .Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators, - false] - : [Diagnostics - .Type_0_is_not_an_array_type_or_a_string_type, - true]; - errorAndMaybeSuggestAwait( - errorNode, - maybeMissingAwait - && !!getAwaitedTypeOfPromise(arrayType), - defaultDiagnostic, - typeToString(arrayType) - ); - } - return hasStringConstituent ? stringType : undefined; - } - - const arrayElementType = getIndexTypeOfType( - arrayType, - IndexKind.Number - ); - if (hasStringConstituent && arrayElementType) { - // This is just an optimization for the case where arrayOrStringType is string | string[] - if (arrayElementType.flags & TypeFlags.StringLike) { - return stringType; - } - - return getUnionType( - [arrayElementType, stringType], - UnionReduction.Subtype - ); - } - - return arrayElementType; - } - - /** - * Gets the requested "iteration type" from an `Iterable`-like or `AsyncIterable`-like type. - */ - function getIterationTypeOfIterable( - use: IterationUse, - typeKind: IterationTypeKind, - inputType: Type, - errorNode: Node | undefined - ): Type | undefined { - if (isTypeAny(inputType)) { - return undefined; - } - - const iterationTypes = getIterationTypesOfIterable( - inputType, - use, - errorNode - ); - return iterationTypes - && iterationTypes - [getIterationTypesKeyFromIterationTypeKind(typeKind)]; - } - - function createIterationTypes( - yieldType: Type = neverType, - returnType: Type = neverType, - nextType: Type = unknownType - ): IterationTypes { - // `yieldType` and `returnType` are defaulted to `neverType` they each will be combined - // via `getUnionType` when merging iteration types. `nextType` is defined as `unknownType` - // as it is combined via `getIntersectionType` when merging iteration types. - - // Use the cache only for intrinsic types to keep it small as they are likely to be - // more frequently created (i.e. `Iterator`). Iteration types - // are also cached on the type they are requested for, so we shouldn't need to maintain - // the cache for less-frequently used types. - if (yieldType.flags & TypeFlags.Intrinsic - && returnType.flags - & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown - | TypeFlags.Void | TypeFlags.Undefined) - && nextType.flags - & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown - | TypeFlags.Void | TypeFlags.Undefined)) - { - const id = getTypeListId([yieldType, returnType, nextType]); - let iterationTypes = iterationTypesCache.get(id); - if (!iterationTypes) { - iterationTypes = { yieldType, returnType, nextType }; - iterationTypesCache.set(id, iterationTypes); - } - return iterationTypes; - } - return { yieldType, returnType, nextType }; - } - - /** - * Combines multiple `IterationTypes` records. - * - * If `array` is empty or all elements are missing or are references to `noIterationTypes`, - * then `noIterationTypes` is returned. Otherwise, an `IterationTypes` record is returned - * for the combined iteration types. - */ - function combineIterationTypes(array: (IterationTypes | undefined)[]) { - let yieldTypes: Type[] | undefined; - let returnTypes: Type[] | undefined; - let nextTypes: Type[] | undefined; - for (const iterationTypes of array) { - if (iterationTypes === undefined - || iterationTypes === noIterationTypes) - { - continue; - } - if (iterationTypes === anyIterationTypes) { - return anyIterationTypes; - } - yieldTypes = append(yieldTypes, iterationTypes.yieldType); - returnTypes = append(returnTypes, iterationTypes.returnType); - nextTypes = append(nextTypes, iterationTypes.nextType); - } - if (yieldTypes || returnTypes || nextTypes) { - return createIterationTypes( - yieldTypes && getUnionType(yieldTypes), - returnTypes && getUnionType(returnTypes), - nextTypes && getIntersectionType(nextTypes) - ); - } - return noIterationTypes; - } - - /** - * Gets the *yield*, *return*, and *next* types from an `Iterable`-like or `AsyncIterable`-like type. - * - * At every level that involves analyzing return types of signatures, we union the return types of all the signatures. - * - * Another thing to note is that at any step of this process, we could run into a dead end, - * meaning either the property is missing, or we run into the anyType. If either of these things - * happens, we return `undefined` to signal that we could not find the iteration type. If a property - * is missing, and the previous step did not result in `any`, then we also give an error if the - * caller requested it. Then the caller can decide what to do in the case where there is no iterated - * type. - * - * For a **for-of** statement, `yield*` (in a normal generator), spread, array - * destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()` - * method. - * - * For an async generator we will only ever look at the `[Symbol.asyncIterator]()` method. - * - * For a **for-await-of** statement or a `yield*` in an async generator we will look for - * the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method. - */ - function getIterationTypesOfIterable( - type: Type, - use: IterationUse, - errorNode: Node | undefined - ) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - if (!(type.flags & TypeFlags.Union)) { - const iterationTypes = getIterationTypesOfIterableWorker( - type, - use, - errorNode - ); - if (iterationTypes === noIterationTypes) { - if (errorNode) { - reportTypeNotIterableError( - errorNode, - type, - !!(use & IterationUse.AllowsAsyncIterablesFlag) - ); - } - return undefined; - } - return iterationTypes; - } - - const cacheKey = use & IterationUse.AllowsAsyncIterablesFlag - ? 'iterationTypesOfAsyncIterable' - : 'iterationTypesOfIterable'; - const cachedTypes = (type as IterableOrIteratorType)[cacheKey]; - if (cachedTypes) { - return cachedTypes === noIterationTypes - ? undefined - : cachedTypes; - } - - let allIterationTypes: IterationTypes[] | undefined; - for (const constituent of (type as UnionType).types) { - const iterationTypes = getIterationTypesOfIterableWorker( - constituent, - use, - errorNode - ); - if (iterationTypes === noIterationTypes) { - if (errorNode) { - reportTypeNotIterableError( - errorNode, - type, - !!(use & IterationUse.AllowsAsyncIterablesFlag) - ); - errorNode = undefined; - } - } else { - allIterationTypes = append( - allIterationTypes, - iterationTypes - ); - } - } - - const iterationTypes = allIterationTypes - ? combineIterationTypes(allIterationTypes) - : noIterationTypes; - (type as IterableOrIteratorType)[cacheKey] = iterationTypes; - return iterationTypes === noIterationTypes - ? undefined - : iterationTypes; - } - - function getAsyncFromSyncIterationTypes( - iterationTypes: IterationTypes, - errorNode: Node | undefined - ) { - if (iterationTypes === noIterationTypes) return noIterationTypes; - if (iterationTypes === anyIterationTypes) return anyIterationTypes; - const { yieldType, returnType, nextType } = iterationTypes; - return createIterationTypes( - getAwaitedType(yieldType, errorNode) || anyType, - getAwaitedType(returnType, errorNode) || anyType, - nextType - ); - } - - /** - * Gets the *yield*, *return*, and *next* types from a non-union type. - * - * If we are unable to find the *yield*, *return*, and *next* types, `noIterationTypes` is - * returned to indicate to the caller that it should report an error. Otherwise, an - * `IterationTypes` record is returned. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableWorker( - type: Type, - use: IterationUse, - errorNode: Node | undefined - ) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - if (use & IterationUse.AllowsAsyncIterablesFlag) { - const iterationTypes = - getIterationTypesOfIterableCached( - type, - asyncIterationTypesResolver - ) - || getIterationTypesOfIterableFast( - type, - asyncIterationTypesResolver - ); - if (iterationTypes) { - return iterationTypes; - } - } - - if (use & IterationUse.AllowsSyncIterablesFlag) { - const iterationTypes = - getIterationTypesOfIterableCached( - type, - syncIterationTypesResolver - ) - || getIterationTypesOfIterableFast( - type, - syncIterationTypesResolver - ); - if (iterationTypes) { - if (use & IterationUse.AllowsAsyncIterablesFlag) { - // for a sync iterable in an async context, only use the cached types if they are valid. - if (iterationTypes !== noIterationTypes) { - return (type as IterableOrIteratorType) - .iterationTypesOfAsyncIterable = getAsyncFromSyncIterationTypes( - iterationTypes, - errorNode - ); - } - } else { - return iterationTypes; - } - } - } - - if (use & IterationUse.AllowsAsyncIterablesFlag) { - const iterationTypes = getIterationTypesOfIterableSlow( - type, - asyncIterationTypesResolver, - errorNode - ); - if (iterationTypes !== noIterationTypes) { - return iterationTypes; - } - } - - if (use & IterationUse.AllowsSyncIterablesFlag) { - const iterationTypes = getIterationTypesOfIterableSlow( - type, - syncIterationTypesResolver, - errorNode - ); - if (iterationTypes !== noIterationTypes) { - if (use & IterationUse.AllowsAsyncIterablesFlag) { - return (type as IterableOrIteratorType) - .iterationTypesOfAsyncIterable = iterationTypes - ? getAsyncFromSyncIterationTypes( - iterationTypes, - errorNode - ) - : noIterationTypes; - } else { - return iterationTypes; - } - } - } - - return noIterationTypes; - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or - * `AsyncIterable`-like type from the cache. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableCached( - type: Type, - resolver: IterationTypesResolver - ) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey]; - } - - function getIterationTypesOfGlobalIterableType( - globalType: Type, - resolver: IterationTypesResolver - ) { - const globalIterationTypes = - getIterationTypesOfIterableCached(globalType, resolver) - || getIterationTypesOfIterableSlow( - globalType, - resolver, /*errorNode*/ - undefined - ); - return globalIterationTypes === noIterationTypes - ? defaultIterationTypes - : globalIterationTypes; - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like - * type from from common heuristics. - * - * If we previously analyzed this type and found no iteration types, `noIterationTypes` is - * returned. If we found iteration types, an `IterationTypes` record is returned. - * Otherwise, we return `undefined` to indicate to the caller it should perform a more - * exhaustive analysis. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableFast( - type: Type, - resolver: IterationTypesResolver - ) { - // As an optimization, if the type is an instantiation of one of the following global types, then - // just grab its related type argument: - // - `Iterable` or `AsyncIterable` - // - `IterableIterator` or `AsyncIterableIterator` - let globalType: Type; - if (isReferenceToType( - type, - globalType = resolver - .getGlobalIterableType(/*reportErrors*/ false) - ) - || isReferenceToType( - type, - globalType = resolver - .getGlobalIterableIteratorType(/*reportErrors*/ false) - )) - { - const [yieldType] = getTypeArguments(type as GenericType); - // The "return" and "next" types of `Iterable` and `IterableIterator` are defined by the - // iteration types of their `[Symbol.iterator]()` method. The same is true for their async cousins. - // While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use - // different definitions. - const { returnType, nextType } = - getIterationTypesOfGlobalIterableType( - globalType, - resolver - ); - return (type as IterableOrIteratorType)[resolver - .iterableCacheKey] = createIterationTypes( - yieldType, - returnType, - nextType - ); - } - - // As an optimization, if the type is an instantiation of the following global type, then - // just grab its related type arguments: - // - `Generator` or `AsyncGenerator` - if (isReferenceToType( - type, - resolver.getGlobalGeneratorType(/*reportErrors*/ false) - )) { - const [yieldType, returnType, nextType] = - getTypeArguments(type as GenericType); - return (type as IterableOrIteratorType)[resolver - .iterableCacheKey] = createIterationTypes( - yieldType, - returnType, - nextType - ); - } - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like - * type from its members. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, `noIterationTypes` is returned. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterable` instead. - */ - function getIterationTypesOfIterableSlow( - type: Type, - resolver: IterationTypesResolver, - errorNode: Node | undefined - ) { - const method = getPropertyOfType( - type, - getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName) - ); - const methodType = method && !(method.flags & SymbolFlags.Optional) - ? getTypeOfSymbol(method) - : undefined; - if (isTypeAny(methodType)) { - return (type as IterableOrIteratorType)[resolver - .iterableCacheKey] = anyIterationTypes; - } - - const signatures = methodType - ? getSignaturesOfType(methodType, SignatureKind.Call) - : undefined; - if (!some(signatures)) { - return (type as IterableOrIteratorType)[resolver - .iterableCacheKey] = noIterationTypes; - } - - const iteratorType = getUnionType( - map(signatures, getReturnTypeOfSignature), - UnionReduction.Subtype - ); - const iterationTypes = - getIterationTypesOfIterator(iteratorType, resolver, errorNode) - || noIterationTypes; - return (type as IterableOrIteratorType)[resolver - .iterableCacheKey] = iterationTypes; - } - - function reportTypeNotIterableError( - errorNode: Node, - type: Type, - allowAsyncIterables: boolean - ): void { - const message = allowAsyncIterables - ? Diagnostics - .Type_0_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator - : Diagnostics - .Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator; - errorAndMaybeSuggestAwait( - errorNode, - !!getAwaitedTypeOfPromise(type), - message, - typeToString(type) - ); - } - - /** - * Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, `undefined` is returned. - */ - function getIterationTypesOfIterator( - type: Type, - resolver: IterationTypesResolver, - errorNode: Node | undefined - ) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - const iterationTypes = - getIterationTypesOfIteratorCached(type, resolver) - || getIterationTypesOfIteratorFast(type, resolver) - || getIterationTypesOfIteratorSlow( - type, - resolver, - errorNode - ); - return iterationTypes === noIterationTypes - ? undefined - : iterationTypes; - } - - /** - * Gets the iteration types of an `Iterator`-like or `AsyncIterator`-like type from the - * cache. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterator` instead. - */ - function getIterationTypesOfIteratorCached( - type: Type, - resolver: IterationTypesResolver - ) { - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey]; - } - - /** - * Gets the iteration types of an `Iterator`-like or `AsyncIterator`-like type from the - * cache or from common heuristics. - * - * If we previously analyzed this type and found no iteration types, `noIterationTypes` is - * returned. If we found iteration types, an `IterationTypes` record is returned. - * Otherwise, we return `undefined` to indicate to the caller it should perform a more - * exhaustive analysis. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterator` instead. - */ - function getIterationTypesOfIteratorFast( - type: Type, - resolver: IterationTypesResolver - ) { - // As an optimization, if the type is an instantiation of one of the following global types, - // then just grab its related type argument: - // - `IterableIterator` or `AsyncIterableIterator` - // - `Iterator` or `AsyncIterator` - // - `Generator` or `AsyncGenerator` - const globalType = resolver - .getGlobalIterableIteratorType(/*reportErrors*/ false); - if (isReferenceToType(type, globalType)) { - const [yieldType] = getTypeArguments(type as GenericType); - // The "return" and "next" types of `IterableIterator` and `AsyncIterableIterator` are defined by the - // iteration types of their `next`, `return`, and `throw` methods. While we define these as `any` - // and `undefined` in our libs by default, a custom lib *could* use different definitions. - const globalIterationTypes = - getIterationTypesOfIteratorCached(globalType, resolver) - || getIterationTypesOfIteratorSlow( - globalType, - resolver, /*errorNode*/ - undefined - ); - const { returnType, nextType } = - globalIterationTypes === noIterationTypes - ? defaultIterationTypes - : globalIterationTypes; - return (type as IterableOrIteratorType)[resolver - .iteratorCacheKey] = createIterationTypes( - yieldType, - returnType, - nextType - ); - } - if (isReferenceToType( - type, - resolver.getGlobalIteratorType(/*reportErrors*/ false) - ) - || isReferenceToType( - type, - resolver.getGlobalGeneratorType(/*reportErrors*/ false) - )) - { - const [yieldType, returnType, nextType] = - getTypeArguments(type as GenericType); - return (type as IterableOrIteratorType)[resolver - .iteratorCacheKey] = createIterationTypes( - yieldType, - returnType, - nextType - ); - } - } - - function isIteratorResult( - type: Type, - kind: IterationTypeKind.Yield | IterationTypeKind.Return - ) { - // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface: - // > [done] is the result status of an iterator `next` method call. If the end of the iterator was reached `done` is `true`. - // > If the end was not reached `done` is `false` and a value is available. - // > If a `done` property (either own or inherited) does not exist, it is consider to have the value `false`. - const doneType = getTypeOfPropertyOfType(type, 'done' as __String) - || falseType; - return isTypeAssignableTo( - kind === IterationTypeKind.Yield ? falseType : trueType, - doneType - ); - } - - function isYieldIteratorResult(type: Type) { - return isIteratorResult(type, IterationTypeKind.Yield); - } - - function isReturnIteratorResult(type: Type) { - return isIteratorResult(type, IterationTypeKind.Return); - } - - /** - * Gets the *yield* and *return* types of an `IteratorResult`-like type. - * - * If we are unable to determine a *yield* or a *return* type, `noIterationTypes` is - * returned to indicate to the caller that it should handle the error. Otherwise, an - * `IterationTypes` record is returned. - */ - function getIterationTypesOfIteratorResult(type: Type) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - const cachedTypes = (type as IterableOrIteratorType) - .iterationTypesOfIteratorResult; - if (cachedTypes) { - return cachedTypes; - } - - // As an optimization, if the type is an instantiation of one of the global `IteratorYieldResult` - // or `IteratorReturnResult` types, then just grab its type argument. - if (isReferenceToType( - type, - getGlobalIteratorYieldResultType(/*reportErrors*/ false) - )) { - const yieldType = getTypeArguments(type as GenericType)[0]; - return (type as IterableOrIteratorType) - .iterationTypesOfIteratorResult = createIterationTypes( - yieldType, /*returnType*/ - undefined, /*nextType*/ - undefined - ); - } - if (isReferenceToType( - type, - getGlobalIteratorReturnResultType(/*reportErrors*/ false) - )) { - const returnType = getTypeArguments(type as GenericType)[0]; - return (type as IterableOrIteratorType) - .iterationTypesOfIteratorResult = createIterationTypes( - /*yieldType*/ undefined, - returnType, /*nextType*/ - undefined - ); - } - - // Choose any constituents that can produce the requested iteration type. - const yieldIteratorResult = filterType( - type, - isYieldIteratorResult - ); - const yieldType = yieldIteratorResult !== neverType - ? getTypeOfPropertyOfType( - yieldIteratorResult, - 'value' as __String - ) - : undefined; - - const returnIteratorResult = filterType( - type, - isReturnIteratorResult - ); - const returnType = returnIteratorResult !== neverType - ? getTypeOfPropertyOfType( - returnIteratorResult, - 'value' as __String - ) - : undefined; - - if (!yieldType && !returnType) { - return (type as IterableOrIteratorType) - .iterationTypesOfIteratorResult = noIterationTypes; - } - - // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface - // > ... If the iterator does not have a return value, `value` is `undefined`. In that case, the - // > `value` property may be absent from the conforming object if it does not inherit an explicit - // > `value` property. - return (type as IterableOrIteratorType) - .iterationTypesOfIteratorResult = createIterationTypes( - yieldType, - returnType || voidType, /*nextType*/ - undefined - ); - } - - /** - * Gets the *yield*, *return*, and *next* types of a the `next()`, `return()`, or - * `throw()` method of an `Iterator`-like or `AsyncIterator`-like type. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, we return `undefined`. - */ - function getIterationTypesOfMethod( - type: Type, - resolver: IterationTypesResolver, - methodName: 'next' | 'return' | 'throw', - errorNode: Node | undefined - ): IterationTypes | undefined { - const method = getPropertyOfType(type, methodName as __String); - - // Ignore 'return' or 'throw' if they are missing. - if (!method && methodName !== 'next') { - return undefined; - } - - const methodType = - method - && !(methodName === 'next' - && (method.flags & SymbolFlags.Optional)) - ? methodName === 'next' - ? getTypeOfSymbol(method) - : getTypeWithFacts( - getTypeOfSymbol(method), - TypeFacts.NEUndefinedOrNull - ) - : undefined; - - if (isTypeAny(methodType)) { - // `return()` and `throw()` don't provide a *next* type. - return methodName === 'next' - ? anyIterationTypes - : anyIterationTypesExceptNext; - } - - // Both async and non-async iterators *must* have a `next` method. - const methodSignatures = methodType - ? getSignaturesOfType(methodType, SignatureKind.Call) - : emptyArray; - if (methodSignatures.length === 0) { - if (errorNode) { - const diagnostic = methodName === 'next' - ? resolver.mustHaveANextMethodDiagnostic - : resolver.mustBeAMethodDiagnostic; - error(errorNode, diagnostic, methodName); - } - return methodName === 'next' ? anyIterationTypes : undefined; - } - - // Extract the first parameter and return type of each signature. - let methodParameterTypes: Type[] | undefined; - let methodReturnTypes: Type[] | undefined; - for (const signature of methodSignatures) { - if (methodName !== 'throw' && some(signature.parameters)) { - methodParameterTypes = append( - methodParameterTypes, - getTypeAtPosition(signature, 0) - ); - } - methodReturnTypes = append( - methodReturnTypes, - getReturnTypeOfSignature(signature) - ); - } - - // Resolve the *next* or *return* type from the first parameter of a `next()` or - // `return()` method, respectively. - let returnTypes: Type[] | undefined; - let nextType: Type | undefined; - if (methodName !== 'throw') { - const methodParameterType = methodParameterTypes - ? getUnionType(methodParameterTypes) - : unknownType; - if (methodName === 'next') { - // The value of `next(value)` is *not* awaited by async generators - nextType = methodParameterType; - } else if (methodName === 'return') { - // The value of `return(value)` *is* awaited by async generators - const resolvedMethodParameterType = - resolver.resolveIterationType( - methodParameterType, - errorNode - ) || anyType; - returnTypes = append( - returnTypes, - resolvedMethodParameterType - ); - } - } - - // Resolve the *yield* and *return* types from the return type of the method (i.e. `IteratorResult`) - let yieldType: Type; - const methodReturnType = methodReturnTypes - ? getUnionType(methodReturnTypes, UnionReduction.Subtype) - : neverType; - const resolvedMethodReturnType = - resolver.resolveIterationType(methodReturnType, errorNode) - || anyType; - const iterationTypes = - getIterationTypesOfIteratorResult(resolvedMethodReturnType); - if (iterationTypes === noIterationTypes) { - if (errorNode) { - error( - errorNode, - resolver.mustHaveAValueDiagnostic, - methodName - ); - } - yieldType = anyType; - returnTypes = append(returnTypes, anyType); - } else { - yieldType = iterationTypes.yieldType; - returnTypes = append(returnTypes, iterationTypes.returnType); - } - - return createIterationTypes( - yieldType, - getUnionType(returnTypes), - nextType - ); - } - - /** - * Gets the *yield*, *return*, and *next* types of an `Iterator`-like or `AsyncIterator`-like - * type from its members. - * - * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` - * record is returned. Otherwise, `noIterationTypes` is returned. - * - * NOTE: You probably don't want to call this directly and should be calling - * `getIterationTypesOfIterator` instead. - */ - function getIterationTypesOfIteratorSlow( - type: Type, - resolver: IterationTypesResolver, - errorNode: Node | undefined - ) { - const iterationTypes = combineIterationTypes([ - getIterationTypesOfMethod(type, resolver, 'next', errorNode), - getIterationTypesOfMethod(type, resolver, 'return', errorNode), - getIterationTypesOfMethod(type, resolver, 'throw', errorNode) - ]); - return (type as IterableOrIteratorType)[resolver - .iteratorCacheKey] = iterationTypes; - } - - /** - * Gets the requested "iteration type" from a type that is either `Iterable`-like, `Iterator`-like, - * `IterableIterator`-like, or `Generator`-like (for a non-async generator); or `AsyncIterable`-like, - * `AsyncIterator`-like, `AsyncIterableIterator`-like, or `AsyncGenerator`-like (for an async generator). - */ - function getIterationTypeOfGeneratorFunctionReturnType( - kind: IterationTypeKind, - returnType: Type, - isAsyncGenerator: boolean - ): Type | undefined { - if (isTypeAny(returnType)) { - return undefined; - } - - const iterationTypes = - getIterationTypesOfGeneratorFunctionReturnType( - returnType, - isAsyncGenerator - ); - return iterationTypes - && iterationTypes - [getIterationTypesKeyFromIterationTypeKind(kind)]; - } - - function getIterationTypesOfGeneratorFunctionReturnType( - type: Type, - isAsyncGenerator: boolean - ) { - if (isTypeAny(type)) { - return anyIterationTypes; - } - - const use = isAsyncGenerator - ? IterationUse.AsyncGeneratorReturnType - : IterationUse.GeneratorReturnType; - const resolver = isAsyncGenerator - ? asyncIterationTypesResolver - : syncIterationTypesResolver; - return getIterationTypesOfIterable( - type, - use, /*errorNode*/ - undefined - ) - || getIterationTypesOfIterator( - type, - resolver, /*errorNode*/ - undefined - ); - } - - function checkBreakOrContinueStatement(node: - BreakOrContinueStatement) - { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) checkGrammarBreakOrContinueStatement(node); - - // TODO: Check that target label is valid - } - - function unwrapReturnType( - returnType: Type, - functionFlags: FunctionFlags - ) { - const isGenerator = !!(functionFlags & FunctionFlags.Generator); - const isAsync = !!(functionFlags & FunctionFlags.Async); - return isGenerator - ? getIterationTypeOfGeneratorFunctionReturnType( - IterationTypeKind.Return, - returnType, - isAsync - ) || errorType - : isAsync - ? getPromisedTypeOfPromise(returnType) || errorType - : returnType; - } - - function isUnwrappedReturnTypeVoidOrAny( - func: SignatureDeclaration, - returnType: Type - ): boolean { - const unwrappedReturnType = unwrapReturnType( - returnType, - getFunctionFlags(func) - ); - return !!unwrappedReturnType - && maybeTypeOfKind( - unwrappedReturnType, - TypeFlags.Void | TypeFlags.AnyOrUnknown - ); - } - - function checkReturnStatement(node: ReturnStatement) { - // Grammar checking - if (checkGrammarStatementInAmbientContext(node)) { - return; - } - - const func = getContainingFunction(node); - if (!func) { - grammarErrorOnFirstToken( - node, - Diagnostics - .A_return_statement_can_only_be_used_within_a_function_body - ); - return; - } - - const signature = getSignatureFromDeclaration(func); - const returnType = getReturnTypeOfSignature(signature); - const functionFlags = getFunctionFlags(func); - if (strictNullChecks || node.expression - || returnType.flags & TypeFlags.Never) - { - const exprType = node.expression - ? checkExpressionCached(node.expression) - : undefinedType; - if (func.kind === SyntaxKind.SetAccessor) { - if (node.expression) { - error(node, Diagnostics.Setters_cannot_return_a_value); - } - } else if (func.kind === SyntaxKind.Constructor) { - if (node.expression - && !checkTypeAssignableToAndOptionallyElaborate( - exprType, - returnType, - node, - node.expression - )) - { - error( - node, - Diagnostics - .Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class - ); - } - } else if (getReturnTypeFromAnnotation(func)) { - const unwrappedReturnType = unwrapReturnType( - returnType, - functionFlags - ); - const unwrappedExprType = - functionFlags & FunctionFlags.Async - ? checkAwaitedType( - exprType, - node, - Diagnostics - .The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member - ) - : exprType; - if (unwrappedReturnType) { - // If the function has a return type, but promisedType is - // undefined, an error will be reported in checkAsyncFunctionReturnType - // so we don't need to report one here. - checkTypeAssignableToAndOptionallyElaborate( - unwrappedExprType, - unwrappedReturnType, - node, - node.expression - ); - } - } - } else if (func.kind !== SyntaxKind.Constructor - && compilerOptions.noImplicitReturns - && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) - { - // The function has a return type, but the return statement doesn't have an expression. - error(node, Diagnostics.Not_all_code_paths_return_a_value); - } - } - - function checkWithStatement(node: WithStatement) { - // Grammar checking for withStatement - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.flags & NodeFlags.AwaitContext) { - grammarErrorOnFirstToken( - node, - Diagnostics - .with_statements_are_not_allowed_in_an_async_function_block - ); - } - } - - checkExpression(node.expression); - - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const start = getSpanOfTokenAtPosition(sourceFile, node.pos) - .start; - const end = node.statement.pos; - grammarErrorAtPos( - sourceFile, - start, - end - start, - Diagnostics - .The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any - ); - } - } - - function checkSwitchStatement(node: SwitchStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - let firstDefaultClause: CaseOrDefaultClause; - let hasDuplicateDefaultClause = false; - - const expressionType = checkExpression(node.expression); - const expressionIsLiteral = isLiteralType(expressionType); - forEach(node.caseBlock.clauses, clause => { - // Grammar check for duplicate default clauses, skip if we already report duplicate default clause - if (clause.kind === SyntaxKind.DefaultClause - && !hasDuplicateDefaultClause) - { - if (firstDefaultClause === undefined) { - firstDefaultClause = clause; - } else { - grammarErrorOnNode( - clause, - Diagnostics - .A_default_clause_cannot_appear_more_than_once_in_a_switch_statement - ); - hasDuplicateDefaultClause = true; - } - } - - if (produceDiagnostics - && clause.kind === SyntaxKind.CaseClause) - { - // TypeScript 1.0 spec (April 2014): 5.9 - // In a 'switch' statement, each 'case' expression must be of a type that is comparable - // to or from the type of the 'switch' expression. - let caseType = checkExpression(clause.expression); - const caseIsLiteral = isLiteralType(caseType); - let comparedExpressionType = expressionType; - if (!caseIsLiteral || !expressionIsLiteral) { - caseType = caseIsLiteral - ? getBaseTypeOfLiteralType(caseType) - : caseType; - comparedExpressionType = getBaseTypeOfLiteralType(expressionType); - } - if (!isTypeEqualityComparableTo( - comparedExpressionType, - caseType - )) { - // expressionType is not comparable to caseType, try the reversed check and report errors if it fails - checkTypeComparableTo( - caseType, - comparedExpressionType, - clause.expression, /*headMessage*/ - undefined - ); - } - } - forEach(clause.statements, checkSourceElement); - if (compilerOptions.noFallthroughCasesInSwitch - && clause.fallthroughFlowNode - && isReachableFlowNode(clause.fallthroughFlowNode)) - { - error(clause, Diagnostics.Fallthrough_case_in_switch); - } - }); - if (node.caseBlock.locals) { - registerForUnusedIdentifiersCheck(node.caseBlock); - } - } - - function checkLabeledStatement(node: LabeledStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - findAncestor(node.parent, current => { - if (isFunctionLike(current)) { - return 'quit'; - } - if (current.kind === SyntaxKind.LabeledStatement - && ( current).label.escapedText - === node.label.escapedText) - { - grammarErrorOnNode( - node.label, - Diagnostics.Duplicate_label_0, - getTextOfNode(node.label) - ); - return true; - } - return false; - }); - } - - // ensure that label is unique - checkSourceElement(node.statement); - } - - function checkThrowStatement(node: ThrowStatement) { - // Grammar checking - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.expression === undefined) { - grammarErrorAfterFirstToken( - node, - Diagnostics.Line_break_not_permitted_here - ); - } - } - - if (node.expression) { - checkExpression(node.expression); - } - } - - function checkTryStatement(node: TryStatement) { - // Grammar checking - checkGrammarStatementInAmbientContext(node); - - checkBlock(node.tryBlock); - const catchClause = node.catchClause; - if (catchClause) { - // Grammar checking - if (catchClause.variableDeclaration) { - if (catchClause.variableDeclaration.type) { - grammarErrorOnFirstToken( - catchClause.variableDeclaration.type, - Diagnostics - .Catch_clause_variable_cannot_have_a_type_annotation - ); - } else if (catchClause.variableDeclaration.initializer) { - grammarErrorOnFirstToken( - catchClause.variableDeclaration.initializer, - Diagnostics - .Catch_clause_variable_cannot_have_an_initializer - ); - } else { - const blockLocals = catchClause.block.locals; - if (blockLocals) { - forEachKey(catchClause.locals!, caughtName => { - const blockLocal = blockLocals.get(caughtName); - if (blockLocal - && (blockLocal.flags - & SymbolFlags.BlockScopedVariable) - !== 0) - { - grammarErrorOnNode( - blockLocal.valueDeclaration, - Diagnostics - .Cannot_redeclare_identifier_0_in_catch_clause, - caughtName - ); - } - }); - } - } - } - - checkBlock(catchClause.block); - } - - if (node.finallyBlock) { - checkBlock(node.finallyBlock); - } - } - - function checkIndexConstraints(type: Type) { - const declaredNumberIndexer = getIndexDeclarationOfSymbol( - type.symbol, - IndexKind.Number - ); - const declaredStringIndexer = getIndexDeclarationOfSymbol( - type.symbol, - IndexKind.String - ); - - const stringIndexType = getIndexTypeOfType(type, IndexKind.String); - const numberIndexType = getIndexTypeOfType(type, IndexKind.Number); - - if (stringIndexType || numberIndexType) { - forEach(getPropertiesOfObjectType(type), prop => { - const propType = getTypeOfSymbol(prop); - checkIndexConstraintForProperty( - prop, - propType, - type, - declaredStringIndexer, - stringIndexType, - IndexKind.String - ); - checkIndexConstraintForProperty( - prop, - propType, - type, - declaredNumberIndexer, - numberIndexType, - IndexKind.Number - ); - }); - - const classDeclaration = type.symbol.valueDeclaration; - if (getObjectFlags(type) & ObjectFlags.Class - && isClassLike(classDeclaration)) - { - for (const member of classDeclaration.members) { - // Only process instance properties with computed names here. - // Static properties cannot be in conflict with indexers, - // and properties with literal names were already checked. - if (!hasModifier(member, ModifierFlags.Static) - && hasNonBindableDynamicName(member)) - { - const symbol = getSymbolOfNode(member); - const propType = getTypeOfSymbol(symbol); - checkIndexConstraintForProperty( - symbol, - propType, - type, - declaredStringIndexer, - stringIndexType, - IndexKind.String - ); - checkIndexConstraintForProperty( - symbol, - propType, - type, - declaredNumberIndexer, - numberIndexType, - IndexKind.Number - ); - } - } - } - } - - let errorNode: Node | undefined; - if (stringIndexType && numberIndexType) { - errorNode = declaredNumberIndexer || declaredStringIndexer; - // condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer - if (!errorNode - && (getObjectFlags(type) & ObjectFlags.Interface)) - { - const someBaseTypeHasBothIndexers = forEach( - getBaseTypes( type), - base => getIndexTypeOfType(base, IndexKind.String) - && getIndexTypeOfType(base, IndexKind.Number) - ); - errorNode = someBaseTypeHasBothIndexers - ? undefined - : type.symbol.declarations[0]; - } - } - - if (errorNode - && !isTypeAssignableTo( - numberIndexType!, - stringIndexType! - )) - { // TODO: GH#18217 - error( - errorNode, - Diagnostics - .Numeric_index_type_0_is_not_assignable_to_string_index_type_1, - typeToString(numberIndexType!), - typeToString(stringIndexType!) - ); - } - - function checkIndexConstraintForProperty( - prop: Symbol, - propertyType: Type, - containingType: Type, - indexDeclaration: Declaration | undefined, - indexType: Type | undefined, - indexKind: IndexKind - ): void { - // ESSymbol properties apply to neither string nor numeric indexers. - if (!indexType || isKnownSymbol(prop)) { - return; - } - - const propDeclaration = prop.valueDeclaration; - const name = propDeclaration - && getNameOfDeclaration(propDeclaration); - - // index is numeric and property name is not valid numeric literal - if (indexKind === IndexKind.Number - && !(name - ? isNumericName(name) - : isNumericLiteralName(prop.escapedName))) - { - return; - } - - // perform property check if property or indexer is declared in 'type' - // this allows us to rule out cases when both property and indexer are inherited from the base class - let errorNode: Node | undefined; - if (propDeclaration && name - && (propDeclaration.kind === SyntaxKind.BinaryExpression - || name.kind === SyntaxKind.ComputedPropertyName - || prop.parent === containingType.symbol)) - { - errorNode = propDeclaration; - } else if (indexDeclaration) { - errorNode = indexDeclaration; - } else if (getObjectFlags(containingType) - & ObjectFlags.Interface) - { - // for interfaces property and indexer might be inherited from different bases - // check if any base class already has both property and indexer. - // check should be performed only if 'type' is the first type that brings property\indexer together - const someBaseClassHasBothPropertyAndIndexer = forEach( - getBaseTypes( containingType), - base => getPropertyOfObjectType(base, prop.escapedName) - && getIndexTypeOfType(base, indexKind) - ); - errorNode = someBaseClassHasBothPropertyAndIndexer - ? undefined - : containingType.symbol.declarations[0]; - } - - if (errorNode - && !isTypeAssignableTo(propertyType, indexType)) - { - const errorMessage = indexKind === IndexKind.String - ? Diagnostics - .Property_0_of_type_1_is_not_assignable_to_string_index_type_2 - : Diagnostics - .Property_0_of_type_1_is_not_assignable_to_numeric_index_type_2; - error( - errorNode, - errorMessage, - symbolToString(prop), - typeToString(propertyType), - typeToString(indexType) - ); - } - } - } - - function checkTypeNameIsReserved( - name: Identifier, - message: DiagnosticMessage - ): void { - // TS 1.0 spec (April 2014): 3.6.1 - // The predefined type keywords are reserved and cannot be used as names of user defined types. - switch (name.escapedText) { - case 'any': - case 'unknown': - case 'number': - case 'bigint': - case 'boolean': - case 'string': - case 'symbol': - case 'void': - case 'object': - error(name, message, name.escapedText as string); - } - } - - /** - * The name cannot be used as 'Object' of user defined types with special target. - */ - function checkClassNameCollisionWithObject(name: Identifier): void { - if (languageVersion === ScriptTarget.ES5 - && name.escapedText === 'Object' - && moduleKind < ModuleKind.ES2015) - { - error( - name, - Diagnostics - .Class_name_cannot_be_Object_when_targeting_ES5_with_module_0, - ModuleKind[moduleKind] - ); // https://github.com/Microsoft/TypeScript/issues/17494 - } - } - - /** - * Check each type parameter and check that type parameters have no duplicate type parameter declarations - */ - function checkTypeParameters(typeParameterDeclarations: - readonly TypeParameterDeclaration[] | undefined) - { - if (typeParameterDeclarations) { - let seenDefault = false; - for (let i = 0; i < typeParameterDeclarations.length; i++) { - const node = typeParameterDeclarations[i]; - checkTypeParameter(node); - - if (produceDiagnostics) { - if (node.default) { - seenDefault = true; - checkTypeParametersNotReferenced( - node.default, - typeParameterDeclarations, - i - ); - } else if (seenDefault) { - error( - node, - Diagnostics - .Required_type_parameters_may_not_follow_optional_type_parameters - ); - } - for (let j = 0; j < i; j++) { - if (typeParameterDeclarations[j].symbol - === node.symbol) - { - error( - node.name, - Diagnostics.Duplicate_identifier_0, - declarationNameToString(node.name) - ); - } - } - } - } - } - } - - /** Check that type parameter defaults only reference previously declared type parameters */ - function checkTypeParametersNotReferenced( - root: TypeNode, - typeParameters: readonly TypeParameterDeclaration[], - index: number - ) { - visit(root); - function visit(node: Node) { - if (node.kind === SyntaxKind.TypeReference) { - const type = - getTypeFromTypeReference( node); - if (type.flags & TypeFlags.TypeParameter) { - for (let i = index; i < typeParameters.length; i++) { - if (type.symbol - === getSymbolOfNode(typeParameters[i])) - { - error( - node, - Diagnostics - .Type_parameter_defaults_can_only_reference_previously_declared_type_parameters - ); - } - } - } - } - forEachChild(node, visit); - } - } - - /** Check that type parameter lists are identical across multiple declarations */ - function checkTypeParameterListsIdentical(symbol: Symbol) { - if (symbol.declarations.length === 1) { - return; - } - - const links = getSymbolLinks(symbol); - if (!links.typeParametersChecked) { - links.typeParametersChecked = true; - const declarations = - getClassOrInterfaceDeclarationsOfSymbol(symbol); - if (declarations.length <= 1) { - return; - } - - const type = getDeclaredTypeOfSymbol(symbol); - if (!areTypeParametersIdentical( - declarations, - type.localTypeParameters! - )) { - // Report an error on every conflicting declaration. - const name = symbolToString(symbol); - for (const declaration of declarations) { - error( - declaration.name, - Diagnostics - .All_declarations_of_0_must_have_identical_type_parameters, - name - ); - } - } - } - } - - function areTypeParametersIdentical( - declarations: readonly (ClassDeclaration | InterfaceDeclaration)[], - targetParameters: TypeParameter[] - ) { - const maxTypeArgumentCount = length(targetParameters); - const minTypeArgumentCount = - getMinTypeArgumentCount(targetParameters); - - for (const declaration of declarations) { - // If this declaration has too few or too many type parameters, we report an error - const sourceParameters = - getEffectiveTypeParameterDeclarations(declaration); - const numTypeParameters = sourceParameters.length; - if (numTypeParameters < minTypeArgumentCount - || numTypeParameters > maxTypeArgumentCount) - { - return false; - } - - for (let i = 0; i < numTypeParameters; i++) { - const source = sourceParameters[i]; - const target = targetParameters[i]; - - // If the type parameter node does not have the same as the resolved type - // parameter at this position, we report an error. - if (source.name.escapedText - !== target.symbol.escapedName) - { - return false; - } - - // If the type parameter node does not have an identical constraint as the resolved - // type parameter at this position, we report an error. - const constraint = - getEffectiveConstraintOfTypeParameter(source); - const sourceConstraint = constraint - && getTypeFromTypeNode(constraint); - const targetConstraint = - getConstraintOfTypeParameter(target); - // relax check if later interface augmentation has no constraint, it's more broad and is OK to merge with - // a more constrained interface (this could be generalized to a full heirarchy check, but that's maybe overkill) - if (sourceConstraint && targetConstraint - && !isTypeIdenticalTo( - sourceConstraint, - targetConstraint - )) - { - return false; - } - - // If the type parameter node has a default and it is not identical to the default - // for the type parameter at this position, we report an error. - const sourceDefault = source.default - && getTypeFromTypeNode(source.default); - const targetDefault = getDefaultFromTypeParameter(target); - if (sourceDefault && targetDefault - && !isTypeIdenticalTo(sourceDefault, targetDefault)) - { - return false; - } - } - } - - return true; - } - - function checkClassExpression(node: ClassExpression): Type { - checkClassLikeDeclaration(node); - checkNodeDeferred(node); - return getTypeOfSymbol(getSymbolOfNode(node)); - } - - function checkClassExpressionDeferred(node: ClassExpression) { - forEach(node.members, checkSourceElement); - registerForUnusedIdentifiersCheck(node); - } - - function checkClassDeclaration(node: ClassDeclaration) { - if (!node.name && !hasModifier(node, ModifierFlags.Default)) { - grammarErrorOnFirstToken( - node, - Diagnostics - .A_class_declaration_without_the_default_modifier_must_have_a_name - ); - } - checkClassLikeDeclaration(node); - forEach(node.members, checkSourceElement); - - registerForUnusedIdentifiersCheck(node); - } - - function checkClassLikeDeclaration(node: ClassLikeDeclaration) { - checkGrammarClassLikeDeclaration(node); - checkDecorators(node); - if (node.name) { - checkTypeNameIsReserved( - node.name, - Diagnostics.Class_name_cannot_be_0 - ); - checkCollisionWithRequireExportsInGeneratedCode( - node, - node.name - ); - checkCollisionWithGlobalPromiseInGeneratedCode( - node, - node.name - ); - if (!(node.flags & NodeFlags.Ambient)) { - checkClassNameCollisionWithObject(node.name); - } - } - checkTypeParameters(getEffectiveTypeParameterDeclarations(node)); - checkExportsOnMergedDeclarations(node); - const symbol = getSymbolOfNode(node); - const type = getDeclaredTypeOfSymbol(symbol); - const typeWithThis = getTypeWithThisArgument(type); - const staticType = getTypeOfSymbol(symbol); - checkTypeParameterListsIdentical(symbol); - checkClassForDuplicateDeclarations(node); - - // Only check for reserved static identifiers on non-ambient context. - if (!(node.flags & NodeFlags.Ambient)) { - checkClassForStaticPropertyNameConflicts(node); - } - - const baseTypeNode = getEffectiveBaseTypeNode(node); - if (baseTypeNode) { - forEach(baseTypeNode.typeArguments, checkSourceElement); - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers( - baseTypeNode.parent, - ExternalEmitHelpers.Extends - ); - } - // check both @extends and extends if both are specified. - const extendsNode = getClassExtendsHeritageElement(node); - if (extendsNode && extendsNode !== baseTypeNode) { - checkExpression(extendsNode.expression); - } - - const baseTypes = getBaseTypes(type); - if (baseTypes.length && produceDiagnostics) { - const baseType = baseTypes[0]; - const baseConstructorType = - getBaseConstructorTypeOfClass(type); - const staticBaseType = - getApparentType(baseConstructorType); - checkBaseTypeAccessibility(staticBaseType, baseTypeNode); - checkSourceElement(baseTypeNode.expression); - if (some(baseTypeNode.typeArguments)) { - forEach( - baseTypeNode.typeArguments, - checkSourceElement - ); - for (const constructor - of getConstructorsForTypeArguments( - staticBaseType, - baseTypeNode.typeArguments, - baseTypeNode - )) - { - if (!checkTypeArgumentConstraints( - baseTypeNode, - constructor.typeParameters! - )) { - break; - } - } - } - const baseWithThis = getTypeWithThisArgument( - baseType, - type.thisType - ); - if (!checkTypeAssignableTo( - typeWithThis, - baseWithThis, /*errorNode*/ - undefined - )) { - issueMemberSpecificError( - node, - typeWithThis, - baseWithThis, - Diagnostics - .Class_0_incorrectly_extends_base_class_1 - ); - } else { - // Report static side error only when instance type is assignable - checkTypeAssignableTo( - staticType, - getTypeWithoutSignatures(staticBaseType), - node.name || node, - Diagnostics - .Class_static_side_0_incorrectly_extends_base_class_static_side_1 - ); - } - if (baseConstructorType.flags & TypeFlags.TypeVariable - && !isMixinConstructorType(staticType)) - { - error( - node.name || node, - Diagnostics - .A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any - ); - } - - if (!(staticBaseType.symbol - && staticBaseType.symbol.flags & SymbolFlags.Class) - && !(baseConstructorType.flags - & TypeFlags.TypeVariable)) - { - // When the static base type is a "class-like" constructor function (but not actually a class), we verify - // that all instantiated base constructor signatures return the same type. - const constructors = - getInstantiatedConstructorsForTypeArguments( - staticBaseType, - baseTypeNode.typeArguments, - baseTypeNode - ); - if (forEach( - constructors, - sig => !isJSConstructor(sig.declaration) - && !isTypeIdenticalTo( - getReturnTypeOfSignature(sig), - baseType - ) - )) { - error( - baseTypeNode.expression, - Diagnostics - .Base_constructors_must_all_have_the_same_return_type - ); - } - } - checkKindsOfPropertyMemberOverrides(type, baseType); - } - } - - const implementedTypeNodes = - getClassImplementsHeritageClauseElements(node); - if (implementedTypeNodes) { - for (const typeRefNode of implementedTypeNodes) { - if (!isEntityNameExpression(typeRefNode.expression)) { - error( - typeRefNode.expression, - Diagnostics - .A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments - ); - } - checkTypeReferenceNode(typeRefNode); - if (produceDiagnostics) { - const t = getTypeFromTypeNode(typeRefNode); - if (t !== errorType) { - if (isValidBaseType(t)) { - const genericDiag = - t.symbol - && t.symbol.flags & SymbolFlags.Class - ? Diagnostics - .Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass - : Diagnostics - .Class_0_incorrectly_implements_interface_1; - const baseWithThis = getTypeWithThisArgument( - t, - type.thisType - ); - if (!checkTypeAssignableTo( - typeWithThis, - baseWithThis, /*errorNode*/ - undefined - )) { - issueMemberSpecificError( - node, - typeWithThis, - baseWithThis, - genericDiag - ); - } - } else { - error( - typeRefNode, - Diagnostics - .A_class_can_only_implement_an_object_type_or_intersection_of_object_types_with_statically_known_members - ); - } - } - } - } - } - - if (produceDiagnostics) { - checkIndexConstraints(type); - checkTypeForDuplicateIndexSignatures(node); - checkPropertyInitialization(node); - } - } - - function issueMemberSpecificError( - node: ClassLikeDeclaration, - typeWithThis: Type, - baseWithThis: Type, - broadDiag: DiagnosticMessage - ) { - // iterate over all implemented properties and issue errors on each one which isn't compatible, rather than the class as a whole, if possible - let issuedMemberError = false; - for (const member of node.members) { - if (hasStaticModifier(member)) { - continue; - } - const declaredProp = member.name - && getSymbolAtLocation(member.name) - || getSymbolAtLocation(member); - if (declaredProp) { - const prop = getPropertyOfType( - typeWithThis, - declaredProp.escapedName - ); - const baseProp = getPropertyOfType( - baseWithThis, - declaredProp.escapedName - ); - if (prop && baseProp) { - const rootChain = () => chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2, - symbolToString(declaredProp), - typeToString(typeWithThis), - typeToString(baseWithThis) - ); - if (!checkTypeAssignableTo( - getTypeOfSymbol(prop), - getTypeOfSymbol(baseProp), - member.name || member, /*message*/ - undefined, - rootChain - )) { - issuedMemberError = true; - } - } - } - } - if (!issuedMemberError) { - // check again with diagnostics to generate a less-specific error - checkTypeAssignableTo( - typeWithThis, - baseWithThis, - node.name || node, - broadDiag - ); - } - } - - function checkBaseTypeAccessibility( - type: Type, - node: ExpressionWithTypeArguments - ) { - const signatures = getSignaturesOfType( - type, - SignatureKind.Construct - ); - if (signatures.length) { - const declaration = signatures[0].declaration; - if (declaration - && hasModifier(declaration, ModifierFlags.Private)) - { - const typeClassDeclaration = - getClassLikeDeclarationOfSymbol(type.symbol)!; - if (!isNodeWithinClass(node, typeClassDeclaration)) { - error( - node, - Diagnostics - .Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, - getFullyQualifiedName(type.symbol) - ); - } - } - } - } - - function getTargetSymbol(s: Symbol) { - // if symbol is instantiated its flags are not copied from the 'target' - // so we'll need to get back original 'target' symbol to work with correct set of flags - return getCheckFlags(s) & CheckFlags.Instantiated - ? ( s).target! - : s; - } - - function getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) { - return filter( - symbol.declarations, - (d: Declaration): d is ClassDeclaration - | InterfaceDeclaration => - d.kind === SyntaxKind.ClassDeclaration - || d.kind === SyntaxKind.InterfaceDeclaration - ); - } - - function checkKindsOfPropertyMemberOverrides( - type: InterfaceType, - baseType: BaseType - ): void { - // TypeScript 1.0 spec (April 2014): 8.2.3 - // A derived class inherits all members from its base class it doesn't override. - // Inheritance means that a derived class implicitly contains all non - overridden members of the base class. - // Both public and private property members are inherited, but only public property members can be overridden. - // A property member in a derived class is said to override a property member in a base class - // when the derived class property member has the same name and kind(instance or static) - // as the base class property member. - // The type of an overriding property member must be assignable(section 3.8.4) - // to the type of the overridden property member, or otherwise a compile - time error occurs. - // Base class instance member functions can be overridden by derived class instance member functions, - // but not by other kinds of members. - // Base class instance member variables and accessors can be overridden by - // derived class instance member variables and accessors, but not by other kinds of members. - - // NOTE: assignability is checked in checkClassDeclaration - const baseProperties = getPropertiesOfType(baseType); - basePropertyCheck: - for (const baseProperty of baseProperties) { - const base = getTargetSymbol(baseProperty); - - if (base.flags & SymbolFlags.Prototype) { - continue; - } - const baseSymbol = getPropertyOfObjectType( - type, - base.escapedName - ); - if (!baseSymbol) { - continue; - } - const derived = getTargetSymbol(baseSymbol); - const baseDeclarationFlags = - getDeclarationModifierFlagsFromSymbol(base); - - Debug.assert( - !!derived, - 'derived should point to something, even if it is the base class\' declaration.' - ); - - // In order to resolve whether the inherited method was overridden in the base class or not, - // we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated* - // type declaration, derived and base resolve to the same symbol even in the case of generic classes. - if (derived === base) { - // derived class inherits base without override/redeclaration - const derivedClassDecl = - getClassLikeDeclarationOfSymbol(type.symbol)!; - - // It is an error to inherit an abstract member without implementing it or being declared abstract. - // If there is no declaration for the derived class (as in the case of class expressions), - // then the class cannot be declared abstract. - if (baseDeclarationFlags & ModifierFlags.Abstract - && (!derivedClassDecl - || !hasModifier( - derivedClassDecl, - ModifierFlags.Abstract - ))) - { - // Searches other base types for a declaration that would satisfy the inherited abstract member. - // (The class may have more than one base type via declaration merging with an interface with the - // same name.) - for (const otherBaseType of getBaseTypes(type)) { - if (otherBaseType === baseType) continue; - const baseSymbol = getPropertyOfObjectType( - otherBaseType, - base.escapedName - ); - const derivedElsewhere = baseSymbol - && getTargetSymbol(baseSymbol); - if (derivedElsewhere - && derivedElsewhere !== base) - { - continue basePropertyCheck; - } - } - - if (derivedClassDecl.kind - === SyntaxKind.ClassExpression) - { - error( - derivedClassDecl, - Diagnostics - .Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, - symbolToString(baseProperty), - typeToString(baseType) - ); - } else { - error( - derivedClassDecl, - Diagnostics - .Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, - typeToString(type), - symbolToString(baseProperty), - typeToString(baseType) - ); - } - } - } else { - // derived overrides base. - const derivedDeclarationFlags = - getDeclarationModifierFlagsFromSymbol(derived); - if (baseDeclarationFlags & ModifierFlags.Private - || derivedDeclarationFlags & ModifierFlags.Private) - { - // either base or derived property is private - not override, skip it - continue; - } - - let errorMessage: DiagnosticMessage; - const basePropertyFlags = - base.flags & SymbolFlags.PropertyOrAccessor; - const derivedPropertyFlags = - derived.flags & SymbolFlags.PropertyOrAccessor; - if (basePropertyFlags && derivedPropertyFlags) { - // property/accessor is overridden with property/accessor - if (!compilerOptions.useDefineForClassFields - || baseDeclarationFlags & ModifierFlags.Abstract - && !(base.valueDeclaration - && isPropertyDeclaration(base.valueDeclaration) - && base.valueDeclaration.initializer) - || base.valueDeclaration - && base.valueDeclaration.parent.kind - === SyntaxKind.InterfaceDeclaration - || derived.valueDeclaration - && isBinaryExpression(derived.valueDeclaration)) - { - // when the base property is abstract or from an interface, base/derived flags don't need to match - // same when the derived property is from an assignment - continue; - } - - const overriddenInstanceProperty = - basePropertyFlags !== SymbolFlags.Property - && derivedPropertyFlags - === SymbolFlags.Property; - const overriddenInstanceAccessor = - basePropertyFlags === SymbolFlags.Property - && derivedPropertyFlags - !== SymbolFlags.Property; - if (overriddenInstanceProperty - || overriddenInstanceAccessor) - { - const errorMessage = overriddenInstanceProperty - ? Diagnostics - ._0_is_defined_as_an_accessor_in_class_1_but_is_overridden_here_in_2_as_an_instance_property - : Diagnostics - ._0_is_defined_as_a_property_in_class_1_but_is_overridden_here_in_2_as_an_accessor; - error( - getNameOfDeclaration(derived.valueDeclaration) - || derived.valueDeclaration, - errorMessage, - symbolToString(base), - typeToString(baseType), - typeToString(type) - ); - } else { - const uninitialized = find( - derived.declarations, - d => d.kind === SyntaxKind.PropertyDeclaration - && !(d as PropertyDeclaration).initializer - ); - if (uninitialized - && !(derived.flags & SymbolFlags.Transient) - && !(baseDeclarationFlags - & ModifierFlags.Abstract) - && !(derivedDeclarationFlags - & ModifierFlags.Abstract) - && !derived.declarations - .some(d => !!(d.flags - & NodeFlags.Ambient))) - { - const constructor = - findConstructorDeclaration(getClassLikeDeclarationOfSymbol(type - .symbol)!); - const propName = - (uninitialized as PropertyDeclaration) - .name; - if ((uninitialized as PropertyDeclaration) - .exclamationToken - || !constructor - || !isIdentifier(propName) - || !strictNullChecks - || !isPropertyInitializedInConstructor( - propName, - type, - constructor - )) - { - const errorMessage = Diagnostics - .Property_0_will_overwrite_the_base_property_in_1_If_this_is_intentional_add_an_initializer_Otherwise_add_a_declare_modifier_or_remove_the_redundant_declaration; - error( - getNameOfDeclaration(derived - .valueDeclaration) - || derived.valueDeclaration, - errorMessage, - symbolToString(base), - typeToString(baseType) - ); - } - } - } - - // correct case - continue; - } else if (isPrototypeProperty(base)) { - if (isPrototypeProperty(derived) - || derived.flags & SymbolFlags.Property) - { - // method is overridden with method or property -- correct case - continue; - } else { - Debug - .assert(!!(derived.flags - & SymbolFlags.Accessor)); - errorMessage = Diagnostics - .Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor; - } - } else if (base.flags & SymbolFlags.Accessor) { - errorMessage = Diagnostics - .Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function; - } else { - errorMessage = Diagnostics - .Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function; - } - - error( - getNameOfDeclaration(derived.valueDeclaration) - || derived.valueDeclaration, - errorMessage, - typeToString(baseType), - symbolToString(base), - typeToString(type) - ); - } - } - } - - function checkInheritedPropertiesAreIdentical( - type: InterfaceType, - typeNode: Node - ): boolean { - const baseTypes = getBaseTypes(type); - if (baseTypes.length < 2) { - return true; - } - - interface InheritanceInfoMap { - prop: Symbol; - containingType: Type; - } - const seen = createUnderscoreEscapedMap(); - forEach(resolveDeclaredMembers(type).declaredProperties, p => { - seen.set(p.escapedName, { prop: p, containingType: type }); - }); - let ok = true; - - for (const base of baseTypes) { - const properties = - getPropertiesOfType(getTypeWithThisArgument( - base, - type.thisType - )); - for (const prop of properties) { - const existing = seen.get(prop.escapedName); - if (!existing) { - seen.set( - prop.escapedName, - { prop, containingType: base } - ); - } else { - const isInheritedProperty = - existing.containingType !== type; - if (isInheritedProperty - && !isPropertyIdenticalTo(existing.prop, prop)) - { - ok = false; - - const typeName1 = - typeToString(existing.containingType); - const typeName2 = typeToString(base); - - let errorInfo = chainDiagnosticMessages( - /*details*/ undefined, - Diagnostics - .Named_property_0_of_types_1_and_2_are_not_identical, - symbolToString(prop), - typeName1, - typeName2 - ); - errorInfo = chainDiagnosticMessages( - errorInfo, - Diagnostics - .Interface_0_cannot_simultaneously_extend_types_1_and_2, - typeToString(type), - typeName1, - typeName2 - ); - diagnostics - .add(createDiagnosticForNodeFromMessageChain( - typeNode, - errorInfo - )); - } - } - } - } - - return ok; - } - - function checkPropertyInitialization(node: ClassLikeDeclaration) { - if (!strictNullChecks || !strictPropertyInitialization - || node.flags & NodeFlags.Ambient) - { - return; - } - const constructor = findConstructorDeclaration(node); - for (const member of node.members) { - if (getModifierFlags(member) & ModifierFlags.Ambient) { - continue; - } - if (isInstancePropertyWithoutInitializer(member)) { - const propName = ( member).name; - if (isIdentifier(propName)) { - const type = getTypeOfSymbol(getSymbolOfNode(member)); - if (!(type.flags & TypeFlags.AnyOrUnknown - || getFalsyFlags(type) & TypeFlags.Undefined)) - { - if (!constructor - || !isPropertyInitializedInConstructor( - propName, - type, - constructor - )) - { - error( - member.name, - Diagnostics - .Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, - declarationNameToString(propName) - ); - } - } - } - } - } - } - - function isInstancePropertyWithoutInitializer(node: Node) { - return node.kind === SyntaxKind.PropertyDeclaration - && !hasModifier( - node, - ModifierFlags.Static | ModifierFlags.Abstract - ) - && !( node).exclamationToken - && !( node).initializer; - } - - function isPropertyInitializedInConstructor( - propName: Identifier, - propType: Type, - constructor: ConstructorDeclaration - ) { - const reference = createPropertyAccess(createThis(), propName); - reference.expression.parent = reference; - reference.parent = constructor; - reference.flowNode = constructor.returnFlowNode; - const flowType = getFlowTypeOfReference( - reference, - propType, - getOptionalType(propType) - ); - return !(getFalsyFlags(flowType) & TypeFlags.Undefined); - } - - function checkInterfaceDeclaration(node: InterfaceDeclaration) { - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node); - - checkTypeParameters(node.typeParameters); - if (produceDiagnostics) { - checkTypeNameIsReserved( - node.name, - Diagnostics.Interface_name_cannot_be_0 - ); - - checkExportsOnMergedDeclarations(node); - const symbol = getSymbolOfNode(node); - checkTypeParameterListsIdentical(symbol); - - // Only check this symbol once - const firstInterfaceDecl = - getDeclarationOfKind( - symbol, - SyntaxKind.InterfaceDeclaration - ); - if (node === firstInterfaceDecl) { - const type = - getDeclaredTypeOfSymbol(symbol); - const typeWithThis = getTypeWithThisArgument(type); - // run subsequent checks only if first set succeeded - if (checkInheritedPropertiesAreIdentical( - type, - node.name - )) { - for (const baseType of getBaseTypes(type)) { - checkTypeAssignableTo( - typeWithThis, - getTypeWithThisArgument( - baseType, - type.thisType - ), - node.name, - Diagnostics - .Interface_0_incorrectly_extends_interface_1 - ); - } - checkIndexConstraints(type); - } - } - checkObjectTypeForDuplicateDeclarations(node); - } - forEach(getInterfaceBaseTypeNodes(node), heritageElement => { - if (!isEntityNameExpression(heritageElement.expression)) { - error( - heritageElement.expression, - Diagnostics - .An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments - ); - } - checkTypeReferenceNode(heritageElement); - }); - - forEach(node.members, checkSourceElement); - - if (produceDiagnostics) { - checkTypeForDuplicateIndexSignatures(node); - registerForUnusedIdentifiersCheck(node); - } - } - - function checkTypeAliasDeclaration(node: TypeAliasDeclaration) { - // Grammar checking - checkGrammarDecoratorsAndModifiers(node); - - checkTypeNameIsReserved( - node.name, - Diagnostics.Type_alias_name_cannot_be_0 - ); - checkTypeParameters(node.typeParameters); - checkSourceElement(node.type); - registerForUnusedIdentifiersCheck(node); - } - - function computeEnumMemberValues(node: EnumDeclaration) { - const nodeLinks = getNodeLinks(node); - if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { - nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; - let autoValue: number | undefined = 0; - for (const member of node.members) { - const value = computeMemberValue(member, autoValue); - getNodeLinks(member).enumMemberValue = value; - autoValue = typeof value === 'number' - ? value + 1 - : undefined; - } - } - } - - function computeMemberValue( - member: EnumMember, - autoValue: number | undefined - ) { - if (isComputedNonLiteralName(member.name)) { - error( - member.name, - Diagnostics - .Computed_property_names_are_not_allowed_in_enums - ); - } else { - const text = getTextOfPropertyName(member.name); - if (isNumericLiteralName(text) - && !isInfinityOrNaNString(text)) - { - error( - member.name, - Diagnostics.An_enum_member_cannot_have_a_numeric_name - ); - } - } - if (member.initializer) { - return computeConstantValue(member); - } - // In ambient non-const numeric enum declarations, enum members without initializers are - // considered computed members (as opposed to having auto-incremented values). - if (member.parent.flags & NodeFlags.Ambient - && !isEnumConst(member.parent) - && getEnumKind(getSymbolOfNode(member.parent)) - === EnumKind.Numeric) - { - return undefined; - } - // If the member declaration specifies no value, the member is considered a constant enum member. - // If the member is the first member in the enum declaration, it is assigned the value zero. - // Otherwise, it is assigned the value of the immediately preceding member plus one, and an error - // occurs if the immediately preceding member is not a constant enum member. - if (autoValue !== undefined) { - return autoValue; - } - error(member.name, Diagnostics.Enum_member_must_have_initializer); - return undefined; - } - - function computeConstantValue(member: EnumMember): string | number - | undefined - { - const enumKind = getEnumKind(getSymbolOfNode(member.parent)); - const isConstEnum = isEnumConst(member.parent); - const initializer = member.initializer!; - const value = - enumKind === EnumKind.Literal && !isLiteralEnumMember(member) - ? undefined - : evaluate(initializer); - if (value !== undefined) { - if (isConstEnum && typeof value === 'number' - && !isFinite(value)) - { - error(initializer, isNaN(value) - ? Diagnostics - .const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN - : Diagnostics - .const_enum_member_initializer_was_evaluated_to_a_non_finite_value); - } - } else if (enumKind === EnumKind.Literal) { - error( - initializer, - Diagnostics - .Computed_values_are_not_permitted_in_an_enum_with_string_valued_members - ); - return 0; - } else if (isConstEnum) { - error( - initializer, - Diagnostics - .const_enum_member_initializers_can_only_contain_literal_values_and_other_computed_enum_values - ); - } else if (member.parent.flags & NodeFlags.Ambient) { - error( - initializer, - Diagnostics - .In_ambient_enum_declarations_member_initializer_must_be_constant_expression - ); - } else { - // Only here do we need to check that the initializer is assignable to the enum type. - checkTypeAssignableTo( - checkExpression(initializer), - getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), - initializer, /*headMessage*/ - undefined - ); - } - return value; - - function evaluate(expr: Expression): string | number | undefined { - switch (expr.kind) { - case SyntaxKind.PrefixUnaryExpression: - const value = - evaluate(( expr).operand); - if (typeof value === 'number') { - switch (( expr).operator) { - case SyntaxKind.PlusToken: - return value; - case SyntaxKind.MinusToken: - return -value; - case SyntaxKind.TildeToken: - return ~value; - } - } - break; - case SyntaxKind.BinaryExpression: - const left = evaluate(( expr).left); - const right = - evaluate(( expr).right); - if (typeof left === 'number' - && typeof right === 'number') - { - switch (( expr).operatorToken - .kind) - { - case SyntaxKind.BarToken: - return left | right; - case SyntaxKind.AmpersandToken: - return left & right; - case SyntaxKind.GreaterThanGreaterThanToken: - return left >> right; - case SyntaxKind - .GreaterThanGreaterThanGreaterThanToken: - return left >>> right; - case SyntaxKind.LessThanLessThanToken: - return left << right; - case SyntaxKind.CaretToken: - return left ^ right; - case SyntaxKind.AsteriskToken: - return left * right; - case SyntaxKind.SlashToken: - return left / right; - case SyntaxKind.PlusToken: - return left + right; - case SyntaxKind.MinusToken: - return left - right; - case SyntaxKind.PercentToken: - return left % right; - case SyntaxKind.AsteriskAsteriskToken: - return left ** right; - } - } else if (typeof left === 'string' - && typeof right === 'string' - && ( expr).operatorToken.kind - === SyntaxKind.PlusToken) - { - return left + right; - } - break; - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return ( expr).text; - case SyntaxKind.NumericLiteral: - checkGrammarNumericLiteral( expr); - return +( expr).text; - case SyntaxKind.ParenthesizedExpression: - return evaluate(( expr) - .expression); - case SyntaxKind.Identifier: - const identifier = expr; - if (isInfinityOrNaNString(identifier.escapedText)) { - return +(identifier.escapedText); - } - return nodeIsMissing(expr) - ? 0 - : evaluateEnumMember( - expr, - getSymbolOfNode(member.parent), - identifier.escapedText - ); - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.PropertyAccessExpression: - const ex = expr; - if (isConstantMemberAccess(ex)) { - const type = getTypeOfExpression(ex.expression); - if (type.symbol - && type.symbol.flags & SymbolFlags.Enum) - { - let name: __String; - if (ex.kind - === SyntaxKind.PropertyAccessExpression) - { - name = ex.name.escapedText; - } else { - name = escapeLeadingUnderscores(cast( - ex.argumentExpression, - isLiteralExpression - ).text); - } - return evaluateEnumMember( - expr, - type.symbol, - name - ); - } - } - break; - } - return undefined; - } - - function evaluateEnumMember( - expr: Expression, - enumSymbol: Symbol, - name: __String - ) { - const memberSymbol = enumSymbol.exports!.get(name); - if (memberSymbol) { - const declaration = memberSymbol.valueDeclaration; - if (declaration !== member) { - if (isBlockScopedNameDeclaredBeforeUse( - declaration, - member - )) { - return getEnumMemberValue(declaration as EnumMember); - } - error( - expr, - Diagnostics - .A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums - ); - return 0; - } - } - return undefined; - } - } - - function isConstantMemberAccess(node: Expression): boolean { - return node.kind === SyntaxKind.Identifier - || node.kind === SyntaxKind.PropertyAccessExpression - && isConstantMemberAccess(( node) - .expression) - || node.kind === SyntaxKind.ElementAccessExpression - && isConstantMemberAccess(( node) - .expression) - && isStringLiteralLike(( node) - .argumentExpression); - } - - function checkEnumDeclaration(node: EnumDeclaration) { - if (!produceDiagnostics) { - return; - } - - // Grammar checking - checkGrammarDecoratorsAndModifiers(node); - - checkTypeNameIsReserved( - node.name, - Diagnostics.Enum_name_cannot_be_0 - ); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); - checkExportsOnMergedDeclarations(node); - node.members.forEach(checkEnumMember); - - computeEnumMemberValues(node); - - // Spec 2014 - Section 9.3: - // It isn't possible for one enum declaration to continue the automatic numbering sequence of another, - // and when an enum type has multiple declarations, only one declaration is permitted to omit a value - // for the first member. - // - // Only perform this check once per symbol - const enumSymbol = getSymbolOfNode(node); - const firstDeclaration = getDeclarationOfKind( - enumSymbol, - node.kind - ); - if (node === firstDeclaration) { - if (enumSymbol.declarations.length > 1) { - const enumIsConst = isEnumConst(node); - // check that const is placed\omitted on all enum declarations - forEach(enumSymbol.declarations, decl => { - if (isEnumDeclaration(decl) - && isEnumConst(decl) !== enumIsConst) - { - error( - getNameOfDeclaration(decl), - Diagnostics - .Enum_declarations_must_all_be_const_or_non_const - ); - } - }); - } - - let seenEnumMissingInitialInitializer = false; - forEach(enumSymbol.declarations, declaration => { - // return true if we hit a violation of the rule, false otherwise - if (declaration.kind !== SyntaxKind.EnumDeclaration) { - return false; - } - - const enumDeclaration = declaration; - if (!enumDeclaration.members.length) { - return false; - } - - const firstEnumMember = enumDeclaration.members[0]; - if (!firstEnumMember.initializer) { - if (seenEnumMissingInitialInitializer) { - error( - firstEnumMember.name, - Diagnostics - .In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element - ); - } else { - seenEnumMissingInitialInitializer = true; - } - } - }); - } - } - - function checkEnumMember(node: EnumMember) { - if (isPrivateIdentifier(node.name)) { - error( - node, - Diagnostics - .An_enum_member_cannot_be_named_with_a_private_identifier - ); - } - } - - function getFirstNonAmbientClassOrFunctionDeclaration(symbol: - Symbol): Declaration | undefined - { - const declarations = symbol.declarations; - for (const declaration of declarations) { - if ((declaration.kind === SyntaxKind.ClassDeclaration - || (declaration.kind === SyntaxKind.FunctionDeclaration - && nodeIsPresent(( declaration) - .body))) - && !(declaration.flags & NodeFlags.Ambient)) - { - return declaration; - } - } - return undefined; - } - - function inSameLexicalScope(node1: Node, node2: Node) { - const container1 = getEnclosingBlockScopeContainer(node1); - const container2 = getEnclosingBlockScopeContainer(node2); - if (isGlobalSourceFile(container1)) { - return isGlobalSourceFile(container2); - } else if (isGlobalSourceFile(container2)) { - return false; - } else { - return container1 === container2; - } - } - - function checkModuleDeclaration(node: ModuleDeclaration) { - if (produceDiagnostics) { - // Grammar checking - const isGlobalAugmentation = isGlobalScopeAugmentation(node); - const inAmbientContext = node.flags & NodeFlags.Ambient; - if (isGlobalAugmentation && !inAmbientContext) { - error( - node.name, - Diagnostics - .Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context - ); - } - - const isAmbientExternalModule = isAmbientModule(node); - const contextErrorMessage = isAmbientExternalModule - ? Diagnostics - .An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file - : Diagnostics - .A_namespace_declaration_is_only_allowed_in_a_namespace_or_module; - if (checkGrammarModuleElementContext( - node, - contextErrorMessage - )) { - // If we hit a module declaration in an illegal context, just bail out to avoid cascading errors. - return; - } - - if (!checkGrammarDecoratorsAndModifiers(node)) { - if (!inAmbientContext - && node.name.kind === SyntaxKind.StringLiteral) - { - grammarErrorOnNode( - node.name, - Diagnostics - .Only_ambient_modules_can_use_quoted_names - ); - } - } - - if (isIdentifier(node.name)) { - checkCollisionWithRequireExportsInGeneratedCode( - node, - node.name - ); - checkCollisionWithGlobalPromiseInGeneratedCode( - node, - node.name - ); - } - - checkExportsOnMergedDeclarations(node); - const symbol = getSymbolOfNode(node); - - // The following checks only apply on a non-ambient instantiated module declaration. - if (symbol.flags & SymbolFlags.ValueModule - && !inAmbientContext - && symbol.declarations.length > 1 - && isInstantiatedModule( - node, - !!compilerOptions.preserveConstEnums - || !!compilerOptions.isolatedModules - )) - { - const firstNonAmbientClassOrFunc = - getFirstNonAmbientClassOrFunctionDeclaration(symbol); - if (firstNonAmbientClassOrFunc) { - if (getSourceFileOfNode(node) - !== getSourceFileOfNode(firstNonAmbientClassOrFunc)) - { - error( - node.name, - Diagnostics - .A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged - ); - } else if (node.pos < firstNonAmbientClassOrFunc.pos) { - error( - node.name, - Diagnostics - .A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged - ); - } - } - - // if the module merges with a class declaration in the same lexical scope, - // we need to track this to ensure the correct emit. - const mergedClass = getDeclarationOfKind( - symbol, - SyntaxKind.ClassDeclaration - ); - if (mergedClass - && inSameLexicalScope(node, mergedClass)) - { - getNodeLinks(node).flags |= NodeCheckFlags - .LexicalModuleMergesWithClass; - } - } - - if (isAmbientExternalModule) { - if (isExternalModuleAugmentation(node)) { - // body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module) - // otherwise we'll be swamped in cascading errors. - // We can detect if augmentation was applied using following rules: - // - augmentation for a global scope is always applied - // - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module). - const checkBody = isGlobalAugmentation - || (getSymbolOfNode(node).flags - & SymbolFlags.Transient); - if (checkBody && node.body) { - for (const statement of node.body.statements) { - checkModuleAugmentationElement( - statement, - isGlobalAugmentation - ); - } - } - } else if (isGlobalSourceFile(node.parent)) { - if (isGlobalAugmentation) { - error( - node.name, - Diagnostics - .Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations - ); - } else if (isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(node - .name))) - { - error( - node.name, - Diagnostics - .Ambient_module_declaration_cannot_specify_relative_module_name - ); - } - } else { - if (isGlobalAugmentation) { - error( - node.name, - Diagnostics - .Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations - ); - } else { - // Node is not an augmentation and is not located on the script level. - // This means that this is declaration of ambient module that is located in other module or namespace which is prohibited. - error( - node.name, - Diagnostics - .Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces - ); - } - } - } - } - - if (node.body) { - checkSourceElement(node.body); - if (!isGlobalScopeAugmentation(node)) { - registerForUnusedIdentifiersCheck(node); - } - } - } - - function checkModuleAugmentationElement( - node: Node, - isGlobalAugmentation: boolean - ): void { - switch (node.kind) { - case SyntaxKind.VariableStatement: - // error each individual name in variable statement instead of marking the entire variable statement - for (const decl of ( node) - .declarationList.declarations) - { - checkModuleAugmentationElement( - decl, - isGlobalAugmentation - ); - } - break; - case SyntaxKind.ExportAssignment: - case SyntaxKind.ExportDeclaration: - grammarErrorOnFirstToken( - node, - Diagnostics - .Exports_and_export_assignments_are_not_permitted_in_module_augmentations - ); - break; - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportDeclaration: - grammarErrorOnFirstToken( - node, - Diagnostics - .Imports_are_not_permitted_in_module_augmentations_Consider_moving_them_to_the_enclosing_external_module - ); - break; - case SyntaxKind.BindingElement: - case SyntaxKind.VariableDeclaration: - const name = ( node) - .name; - if (isBindingPattern(name)) { - for (const el of name.elements) { - // mark individual names in binding pattern - checkModuleAugmentationElement( - el, - isGlobalAugmentation - ); - } - break; - } - // falls through - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.TypeAliasDeclaration: - if (isGlobalAugmentation) { - return; - } - const symbol = getSymbolOfNode(node); - if (symbol) { - // module augmentations cannot introduce new names on the top level scope of the module - // this is done it two steps - // 1. quick check - if symbol for node is not merged - this is local symbol to this augmentation - report error - // 2. main check - report error if value declaration of the parent symbol is module augmentation) - let reportError = - !(symbol.flags & SymbolFlags.Transient); - if (!reportError) { - // symbol should not originate in augmentation - reportError = !!symbol.parent - && isExternalModuleAugmentation(symbol.parent - .declarations[0]); - } - } - break; - } - } - - function getFirstNonModuleExportsIdentifier(node: - EntityNameOrEntityNameExpression): Identifier - { - switch (node.kind) { - case SyntaxKind.Identifier: - return node; - case SyntaxKind.QualifiedName: - do { - node = node.left; - } while (node.kind !== SyntaxKind.Identifier); - return node; - case SyntaxKind.PropertyAccessExpression: - do { - if (isModuleExportsAccessExpression(node.expression) - && !isPrivateIdentifier(node.name)) - { - return node.name; - } - node = node.expression; - } while (node.kind !== SyntaxKind.Identifier); - return node; - } - } - - function checkExternalImportOrExportDeclaration(node: ImportDeclaration - | ImportEqualsDeclaration | ExportDeclaration): boolean - { - const moduleName = getExternalModuleName(node); - if (!moduleName || nodeIsMissing(moduleName)) { - // Should be a parse error. - return false; - } - if (!isStringLiteral(moduleName)) { - error(moduleName, Diagnostics.String_literal_expected); - return false; - } - const inAmbientExternalModule = - node.parent.kind === SyntaxKind.ModuleBlock - && isAmbientModule(node.parent.parent); - if (node.parent.kind !== SyntaxKind.SourceFile - && !inAmbientExternalModule) - { - error(moduleName, node.kind === SyntaxKind.ExportDeclaration - ? Diagnostics - .Export_declarations_are_not_permitted_in_a_namespace - : Diagnostics - .Import_declarations_in_a_namespace_cannot_reference_a_module); - return false; - } - if (inAmbientExternalModule - && isExternalModuleNameRelative(moduleName.text)) - { - // we have already reported errors on top level imports\exports in external module augmentations in checkModuleDeclaration - // no need to do this again. - if (!isTopLevelInExternalModuleAugmentation(node)) { - // TypeScript 1.0 spec (April 2013): 12.1.6 - // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference - // other external modules only through top - level external module names. - // Relative external module names are not permitted. - error( - node, - Diagnostics - .Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name - ); - return false; - } - } - return true; - } - - function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause - | NamespaceImport | ImportSpecifier | ExportSpecifier - | NamespaceExport) - { - let symbol = getSymbolOfNode(node); - const target = resolveAlias(symbol); - - const shouldSkipWithJSExpandoTargets = - symbol.flags & SymbolFlags.Assignment; - if (!shouldSkipWithJSExpandoTargets && target !== unknownSymbol) { - // For external modules symbol represents local symbol for an alias. - // This local symbol will merge any other local declarations (excluding other aliases) - // and symbol.flags will contains combined representation for all merged declaration. - // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, - // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* - // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). - symbol = getMergedSymbol(symbol.exportSymbol || symbol); - const excludedMeanings = - (symbol.flags - & (SymbolFlags.Value | SymbolFlags.ExportValue) - ? SymbolFlags.Value - : 0) - | (symbol.flags & SymbolFlags.Type - ? SymbolFlags.Type - : 0) - | (symbol.flags & SymbolFlags.Namespace - ? SymbolFlags.Namespace - : 0); - if (target.flags & excludedMeanings) { - const message = node.kind === SyntaxKind.ExportSpecifier - ? Diagnostics - .Export_declaration_conflicts_with_exported_declaration_of_0 - : Diagnostics - .Import_declaration_conflicts_with_local_declaration_of_0; - error(node, message, symbolToString(symbol)); - } - - // Don't allow to re-export something with no value side when `--isolatedModules` is set. - if (compilerOptions.isolatedModules - && node.kind === SyntaxKind.ExportSpecifier - && !node.parent.parent.isTypeOnly - && !(target.flags & SymbolFlags.Value) - && !(node.flags & NodeFlags.Ambient)) - { - error( - node, - Diagnostics - .Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type - ); - } - } - } - - function checkImportBinding(node: ImportEqualsDeclaration - | ImportClause | NamespaceImport | ImportSpecifier) - { - checkCollisionWithRequireExportsInGeneratedCode(node, node.name!); - checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name!); - checkAliasSymbol(node); - } - - function checkImportDeclaration(node: ImportDeclaration) { - if (checkGrammarModuleElementContext( - node, - Diagnostics - .An_import_declaration_can_only_be_used_in_a_namespace_or_module - )) { - // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. - return; - } - if (!checkGrammarDecoratorsAndModifiers(node) - && hasModifiers(node)) - { - grammarErrorOnFirstToken( - node, - Diagnostics.An_import_declaration_cannot_have_modifiers - ); - } - if (checkExternalImportOrExportDeclaration(node)) { - const importClause = node.importClause; - if (importClause && !checkGrammarImportClause(importClause)) { - if (importClause.name) { - checkImportBinding(importClause); - } - if (importClause.namedBindings) { - if (importClause.namedBindings.kind - === SyntaxKind.NamespaceImport) - { - checkImportBinding(importClause.namedBindings); - } else { - const moduleExisted = resolveExternalModuleName( - node, - node.moduleSpecifier - ); - if (moduleExisted) { - forEach( - importClause.namedBindings.elements, - checkImportBinding - ); - } - } - } - } - } - } - - function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) { - if (checkGrammarModuleElementContext( - node, - Diagnostics - .An_import_declaration_can_only_be_used_in_a_namespace_or_module - )) { - // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. - return; - } - - checkGrammarDecoratorsAndModifiers(node); - if (isInternalModuleImportEqualsDeclaration(node) - || checkExternalImportOrExportDeclaration(node)) - { - checkImportBinding(node); - if (hasModifier(node, ModifierFlags.Export)) { - markExportAsReferenced(node); - } - if (node.moduleReference.kind - !== SyntaxKind.ExternalModuleReference) - { - const target = resolveAlias(getSymbolOfNode(node)); - if (target !== unknownSymbol) { - if (target.flags & SymbolFlags.Value) { - // Target is a value symbol, check that it is not hidden by a local declaration with the same name - const moduleName = - getFirstIdentifier(node.moduleReference); - if (!(resolveEntityName( - moduleName, - SymbolFlags.Value | SymbolFlags.Namespace - )!.flags & SymbolFlags.Namespace)) { - error( - moduleName, - Diagnostics - .Module_0_is_hidden_by_a_local_declaration_with_the_same_name, - declarationNameToString(moduleName) - ); - } - } - if (target.flags & SymbolFlags.Type) { - checkTypeNameIsReserved( - node.name, - Diagnostics.Import_name_cannot_be_0 - ); - } - } - } else { - if (moduleKind >= ModuleKind.ES2015 - && !(node.flags & NodeFlags.Ambient)) - { - // Import equals declaration is deprecated in es6 or above - grammarErrorOnNode( - node, - Diagnostics - .Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead - ); - } - } - } - } - - function checkExportDeclaration(node: ExportDeclaration) { - if (checkGrammarModuleElementContext( - node, - Diagnostics.An_export_declaration_can_only_be_used_in_a_module - )) { - // If we hit an export in an illegal context, just bail out to avoid cascading errors. - return; - } - - if (!checkGrammarDecoratorsAndModifiers(node) - && hasModifiers(node)) - { - grammarErrorOnFirstToken( - node, - Diagnostics.An_export_declaration_cannot_have_modifiers - ); - } - - if (!node.moduleSpecifier - || checkExternalImportOrExportDeclaration(node)) - { - if (node.exportClause) { - // export { x, y } - // export { x, y } from "foo" - if (isNamedExports(node.exportClause)) { - forEach( - node.exportClause.elements, - checkExportSpecifier - ); - } else if (!isNamespaceExport(node.exportClause)) { - checkImportBinding(node.exportClause); - } - - const inAmbientExternalModule = - node.parent.kind === SyntaxKind.ModuleBlock - && isAmbientModule(node.parent.parent); - const inAmbientNamespaceDeclaration = - !inAmbientExternalModule - && node.parent.kind === SyntaxKind.ModuleBlock - && !node.moduleSpecifier - && node.flags & NodeFlags.Ambient; - if (node.parent.kind !== SyntaxKind.SourceFile - && !inAmbientExternalModule - && !inAmbientNamespaceDeclaration) - { - error( - node, - Diagnostics - .Export_declarations_are_not_permitted_in_a_namespace - ); - } - } else { - // export * from "foo" - const moduleSymbol = resolveExternalModuleName( - node, - node.moduleSpecifier! - ); - if (moduleSymbol - && hasExportAssignmentSymbol(moduleSymbol)) - { - error( - node.moduleSpecifier, - Diagnostics - .Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, - symbolToString(moduleSymbol) - ); - } - - if (moduleKind !== ModuleKind.System - && moduleKind < ModuleKind.ES2015) - { - checkExternalEmitHelpers( - node, - ExternalEmitHelpers.ExportStar - ); - } - } - } - } - - function checkGrammarModuleElementContext( - node: Statement, - errorMessage: DiagnosticMessage - ): boolean { - const isInAppropriateContext = - node.parent.kind === SyntaxKind.SourceFile - || node.parent.kind === SyntaxKind.ModuleBlock - || node.parent.kind === SyntaxKind.ModuleDeclaration; - if (!isInAppropriateContext) { - grammarErrorOnFirstToken(node, errorMessage); - } - return !isInAppropriateContext; - } - - function importClauseContainsReferencedImport(importClause: - ImportClause) - { - return importClause.name && isReferenced(importClause) - || importClause.namedBindings - && namedBindingsContainsReferencedImport(importClause - .namedBindings); - - function isReferenced(declaration: Declaration) { - return !!getMergedSymbol(getSymbolOfNode(declaration)) - .isReferenced; - } - function namedBindingsContainsReferencedImport(namedBindings: - NamedImportBindings) - { - return isNamespaceImport(namedBindings) - ? isReferenced(namedBindings) - : some(namedBindings.elements, isReferenced); - } - } - - function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if ( - isImportDeclaration(statement) - && statement.importClause - && !statement.importClause.isTypeOnly - && importClauseContainsReferencedImport(statement - .importClause) - && !isReferencedAliasDeclaration( - statement.importClause, /*checkChildren*/ - true - ) - ) { - const isError = - compilerOptions.importsNotUsedAsValue - === ImportsNotUsedAsValue.Error; - errorOrSuggestion( - isError, - statement, - isError - ? Diagnostics - .This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValue_is_set_to_error - : Diagnostics - .This_import_may_be_converted_to_a_type_only_import - ); - } - } - } - - function checkExportSpecifier(node: ExportSpecifier) { - checkAliasSymbol(node); - if (getEmitDeclarations(compilerOptions)) { - collectLinkedAliases( - node.propertyName || node.name, /*setVisibility*/ - true - ); - } - if (!node.parent.parent.moduleSpecifier) { - const exportedName = node.propertyName || node.name; - // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) - const symbol = resolveName( - exportedName, - exportedName.escapedText, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace | SymbolFlags.Alias, - /*nameNotFoundMessage*/ undefined, /*nameArg*/ - undefined, /*isUse*/ - true - ); - if (symbol - && (symbol === undefinedSymbol - || symbol === globalThisSymbol - || isGlobalSourceFile(getDeclarationContainer(symbol - .declarations[0])))) - { - error( - exportedName, - Diagnostics - .Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, - idText(exportedName) - ); - } else { - markExportAsReferenced(node); - const target = symbol - && (symbol.flags & SymbolFlags.Alias - ? resolveAlias(symbol) - : symbol); - if (!target || target === unknownSymbol - || target.flags & SymbolFlags.Value) - { - checkExpressionCached(node.propertyName || node.name); - } - } - } - } - - function checkExportAssignment(node: ExportAssignment) { - if (checkGrammarModuleElementContext( - node, - Diagnostics.An_export_assignment_can_only_be_used_in_a_module - )) { - // If we hit an export assignment in an illegal context, just bail out to avoid cascading errors. - return; - } - - const container = node.parent.kind === SyntaxKind.SourceFile - ? node.parent - : node.parent.parent; - if (container.kind === SyntaxKind.ModuleDeclaration - && !isAmbientModule(container)) - { - if (node.isExportEquals) { - error( - node, - Diagnostics - .An_export_assignment_cannot_be_used_in_a_namespace - ); - } else { - error( - node, - Diagnostics - .A_default_export_can_only_be_used_in_an_ECMAScript_style_module - ); - } - - return; - } - // Grammar checking - if (!checkGrammarDecoratorsAndModifiers(node) - && hasModifiers(node)) - { - grammarErrorOnFirstToken( - node, - Diagnostics.An_export_assignment_cannot_have_modifiers - ); - } - if (node.expression.kind === SyntaxKind.Identifier) { - const id = node.expression as Identifier; - const sym = resolveEntityName( - id, - SymbolFlags.All, /*ignoreErrors*/ - true, /*dontResolveAlias*/ - true, - node - ); - if (sym) { - markAliasReferenced(sym, id); - // If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`) - const target = sym.flags & SymbolFlags.Alias - ? resolveAlias(sym) - : sym; - if (target === unknownSymbol - || target.flags & SymbolFlags.Value) - { - // However if it is a value, we need to check it's being used correctly - checkExpressionCached(node.expression); - } - } - - if (getEmitDeclarations(compilerOptions)) { - collectLinkedAliases( - node.expression as Identifier, /*setVisibility*/ - true - ); - } - } else { - checkExpressionCached(node.expression); - } - - checkExternalModuleExports(container); - - if ((node.flags & NodeFlags.Ambient) - && !isEntityNameExpression(node.expression)) - { - grammarErrorOnNode( - node.expression, - Diagnostics - .The_expression_of_an_export_assignment_must_be_an_identifier_or_qualified_name_in_an_ambient_context - ); - } - - if (node.isExportEquals && !(node.flags & NodeFlags.Ambient)) { - if (moduleKind >= ModuleKind.ES2015) { - // export assignment is not supported in es6 modules - grammarErrorOnNode( - node, - Diagnostics - .Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead - ); - } else if (moduleKind === ModuleKind.System) { - // system modules does not support export assignment - grammarErrorOnNode( - node, - Diagnostics - .Export_assignment_is_not_supported_when_module_flag_is_system - ); - } - } - } - - function hasExportedMembers(moduleSymbol: Symbol) { - return forEachEntry( - moduleSymbol.exports!, - (_, id) => id !== 'export=' - ); - } - - function checkExternalModuleExports(node: SourceFile - | ModuleDeclaration) - { - const moduleSymbol = getSymbolOfNode(node); - const links = getSymbolLinks(moduleSymbol); - if (!links.exportsChecked) { - const exportEqualsSymbol = moduleSymbol.exports! - .get('export=' as __String); - if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) { - const declaration = - getDeclarationOfAliasSymbol(exportEqualsSymbol) - || exportEqualsSymbol.valueDeclaration; - if (!isTopLevelInExternalModuleAugmentation(declaration) - && !isInJSFile(declaration)) - { - error( - declaration, - Diagnostics - .An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements - ); - } - } - // Checks for export * conflicts - const exports = getExportsOfModule(moduleSymbol); - if (exports) { - exports.forEach(({ declarations, flags }, id) => { - if (id === '__export') { - return; - } - // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. - // (TS Exceptions: namespaces, function overloads, enums, and interfaces) - if (flags - & (SymbolFlags.Namespace | SymbolFlags.Interface - | SymbolFlags.Enum)) - { - return; - } - const exportedDeclarationsCount = countWhere( - declarations, - isNotOverloadAndNotAccessor - ); - if (flags & SymbolFlags.TypeAlias - && exportedDeclarationsCount <= 2) - { - // it is legal to merge type alias with other values - // so count should be either 1 (just type alias) or 2 (type alias + merged value) - return; - } - if (exportedDeclarationsCount > 1) { - for (const declaration of declarations) { - if (isNotOverload(declaration)) { - diagnostics - .add(createDiagnosticForNode( - declaration, - Diagnostics - .Cannot_redeclare_exported_variable_0, - unescapeLeadingUnderscores(id) - )); - } - } - } - }); - } - links.exportsChecked = true; - } - } - - function checkSourceElement(node: Node | undefined): void { - if (node) { - const saveCurrentNode = currentNode; - currentNode = node; - instantiationCount = 0; - checkSourceElementWorker(node); - currentNode = saveCurrentNode; - } - } - - function checkSourceElementWorker(node: Node): void { - if (isInJSFile(node)) { - forEach( - (node as JSDocContainer).jsDoc, - ({ tags }) => forEach(tags, checkSourceElement) - ); - } - - const kind = node.kind; - if (cancellationToken) { - // Only bother checking on a few construct kinds. We don't want to be excessively - // hitting the cancellation token on every node we check. - switch (kind) { - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.FunctionDeclaration: - cancellationToken.throwIfCancellationRequested(); - } - } - if (kind >= SyntaxKind.FirstStatement - && kind <= SyntaxKind.LastStatement && node.flowNode - && !isReachableFlowNode(node.flowNode)) - { - errorOrSuggestion( - compilerOptions.allowUnreachableCode === false, - node, - Diagnostics.Unreachable_code_detected - ); - } - - switch (kind) { - case SyntaxKind.TypeParameter: - return checkTypeParameter( node); - case SyntaxKind.Parameter: - return checkParameter( node); - case SyntaxKind.PropertyDeclaration: - return checkPropertyDeclaration( node); - case SyntaxKind.PropertySignature: - return checkPropertySignature( node); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - return checkSignatureDeclaration( node); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - return checkMethodDeclaration( node); - case SyntaxKind.Constructor: - return checkConstructorDeclaration( node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return checkAccessorDeclaration( node); - case SyntaxKind.TypeReference: - return checkTypeReferenceNode( node); - case SyntaxKind.TypePredicate: - return checkTypePredicate( node); - case SyntaxKind.TypeQuery: - return checkTypeQuery( node); - case SyntaxKind.TypeLiteral: - return checkTypeLiteral( node); - case SyntaxKind.ArrayType: - return checkArrayType( node); - case SyntaxKind.TupleType: - return checkTupleType( node); - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - return checkUnionOrIntersectionType( node); - case SyntaxKind.ParenthesizedType: - case SyntaxKind.OptionalType: - case SyntaxKind.RestType: - return checkSourceElement(( node).type); - case SyntaxKind.ThisType: - return checkThisType( node); - case SyntaxKind.TypeOperator: - return checkTypeOperator( node); - case SyntaxKind.ConditionalType: - return checkConditionalType( node); - case SyntaxKind.InferType: - return checkInferType( node); - case SyntaxKind.ImportType: - return checkImportType( node); - case SyntaxKind.JSDocAugmentsTag: - return checkJSDocAugmentsTag(node as JSDocAugmentsTag); - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - return checkJSDocTypeAliasTag(node as JSDocTypedefTag); - case SyntaxKind.JSDocTemplateTag: - return checkJSDocTemplateTag(node as JSDocTemplateTag); - case SyntaxKind.JSDocTypeTag: - return checkJSDocTypeTag(node as JSDocTypeTag); - case SyntaxKind.JSDocParameterTag: - return checkJSDocParameterTag(node as JSDocParameterTag); - case SyntaxKind.JSDocFunctionType: - checkJSDocFunctionType(node as JSDocFunctionType); - // falls through - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocTypeLiteral: - checkJSDocTypeIsInJsFile(node); - forEachChild(node, checkSourceElement); - return; - case SyntaxKind.JSDocVariadicType: - checkJSDocVariadicType(node as JSDocVariadicType); - return; - case SyntaxKind.JSDocTypeExpression: - return checkSourceElement((node as JSDocTypeExpression) - .type); - case SyntaxKind.IndexedAccessType: - return checkIndexedAccessType( node); - case SyntaxKind.MappedType: - return checkMappedType( node); - case SyntaxKind.FunctionDeclaration: - return checkFunctionDeclaration( node); - case SyntaxKind.Block: - case SyntaxKind.ModuleBlock: - return checkBlock( node); - case SyntaxKind.VariableStatement: - return checkVariableStatement( node); - case SyntaxKind.ExpressionStatement: - return checkExpressionStatement( node); - case SyntaxKind.IfStatement: - return checkIfStatement( node); - case SyntaxKind.DoStatement: - return checkDoStatement( node); - case SyntaxKind.WhileStatement: - return checkWhileStatement( node); - case SyntaxKind.ForStatement: - return checkForStatement( node); - case SyntaxKind.ForInStatement: - return checkForInStatement( node); - case SyntaxKind.ForOfStatement: - return checkForOfStatement( node); - case SyntaxKind.ContinueStatement: - case SyntaxKind.BreakStatement: - return checkBreakOrContinueStatement( node); - case SyntaxKind.ReturnStatement: - return checkReturnStatement( node); - case SyntaxKind.WithStatement: - return checkWithStatement( node); - case SyntaxKind.SwitchStatement: - return checkSwitchStatement( node); - case SyntaxKind.LabeledStatement: - return checkLabeledStatement( node); - case SyntaxKind.ThrowStatement: - return checkThrowStatement( node); - case SyntaxKind.TryStatement: - return checkTryStatement( node); - case SyntaxKind.VariableDeclaration: - return checkVariableDeclaration( node); - case SyntaxKind.BindingElement: - return checkBindingElement( node); - case SyntaxKind.ClassDeclaration: - return checkClassDeclaration( node); - case SyntaxKind.InterfaceDeclaration: - return checkInterfaceDeclaration( node); - case SyntaxKind.TypeAliasDeclaration: - return checkTypeAliasDeclaration( node); - case SyntaxKind.EnumDeclaration: - return checkEnumDeclaration( node); - case SyntaxKind.ModuleDeclaration: - return checkModuleDeclaration( node); - case SyntaxKind.ImportDeclaration: - return checkImportDeclaration( node); - case SyntaxKind.ImportEqualsDeclaration: - return checkImportEqualsDeclaration( node); - case SyntaxKind.ExportDeclaration: - return checkExportDeclaration( node); - case SyntaxKind.ExportAssignment: - return checkExportAssignment( node); - case SyntaxKind.EmptyStatement: - case SyntaxKind.DebuggerStatement: - checkGrammarStatementInAmbientContext(node); - return; - case SyntaxKind.MissingDeclaration: - return checkMissingDeclaration(node); - } - } - - function checkJSDocTypeIsInJsFile(node: Node): void { - if (!isInJSFile(node)) { - grammarErrorOnNode( - node, - Diagnostics - .JSDoc_types_can_only_be_used_inside_documentation_comments - ); - } - } - - function checkJSDocVariadicType(node: JSDocVariadicType): void { - checkJSDocTypeIsInJsFile(node); - checkSourceElement(node.type); - - // Only legal location is in the *last* parameter tag or last parameter of a JSDoc function. - const { parent } = node; - if (isParameter(parent) && isJSDocFunctionType(parent.parent)) { - if (last(parent.parent.parameters) !== parent) { - error( - node, - Diagnostics - .A_rest_parameter_must_be_last_in_a_parameter_list - ); - } - return; - } - - if (!isJSDocTypeExpression(parent)) { - error( - node, - Diagnostics - .JSDoc_may_only_appear_in_the_last_parameter_of_a_signature - ); - } - - const paramTag = node.parent.parent; - if (!isJSDocParameterTag(paramTag)) { - error( - node, - Diagnostics - .JSDoc_may_only_appear_in_the_last_parameter_of_a_signature - ); - return; - } - - const param = getParameterSymbolFromJSDoc(paramTag); - if (!param) { - // We will error in `checkJSDocParameterTag`. - return; - } - - const host = getHostSignatureFromJSDoc(paramTag); - if (!host || last(host.parameters).symbol !== param) { - error( - node, - Diagnostics - .A_rest_parameter_must_be_last_in_a_parameter_list - ); - } - } - - function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { - const type = getTypeFromTypeNode(node.type); - const { parent } = node; - const paramTag = node.parent.parent; - if (isJSDocTypeExpression(node.parent) - && isJSDocParameterTag(paramTag)) - { - // Else we will add a diagnostic, see `checkJSDocVariadicType`. - const host = getHostSignatureFromJSDoc(paramTag); - if (host) { - /* - Only return an array type if the corresponding parameter is marked as a rest parameter, or if there are no parameters. - So in the following situation we will not create an array type: - /** @param {...number} a * / - function f(a) {} - Because `a` will just be of type `number | undefined`. A synthetic `...args` will also be added, which *will* get an array type. - */ - const lastParamDeclaration = - lastOrUndefined(host.parameters); - const symbol = getParameterSymbolFromJSDoc(paramTag); - if (!lastParamDeclaration - || symbol && lastParamDeclaration.symbol === symbol - && isRestParameter(lastParamDeclaration)) - { - return createArrayType(type); - } - } - } - if (isParameter(parent) && isJSDocFunctionType(parent.parent)) { - return createArrayType(type); - } - return addOptionality(type); - } - - // Function and class expression bodies are checked after all statements in the enclosing body. This is - // to ensure constructs like the following are permitted: - // const foo = function () { - // const s = foo(); - // return "hello"; - // } - // Here, performing a full type check of the body of the function expression whilst in the process of - // determining the type of foo would cause foo to be given type any because of the recursive reference. - // Delaying the type check of the body ensures foo has been assigned a type. - function checkNodeDeferred(node: Node) { - const enclosingFile = getSourceFileOfNode(node); - const links = getNodeLinks(enclosingFile); - if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes = links.deferredNodes || createMap(); - const id = '' + getNodeId(node); - links.deferredNodes.set(id, node); - } - } - - function checkDeferredNodes(context: SourceFile) { - const links = getNodeLinks(context); - if (links.deferredNodes) { - links.deferredNodes.forEach(checkDeferredNode); - } - } - - function checkDeferredNode(node: Node) { - const saveCurrentNode = currentNode; - currentNode = node; - instantiationCount = 0; - switch (node.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - checkFunctionExpressionOrObjectLiteralMethodDeferred( node); - break; - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - checkAccessorDeclaration( node); - break; - case SyntaxKind.ClassExpression: - checkClassExpressionDeferred( node); - break; - case SyntaxKind.JsxSelfClosingElement: - checkJsxSelfClosingElementDeferred( node); - break; - case SyntaxKind.JsxElement: - checkJsxElementDeferred( node); - break; - } - currentNode = saveCurrentNode; - } - - function checkSourceFile(node: SourceFile) { - performance.mark('beforeCheck'); - checkSourceFileWorker(node); - performance.mark('afterCheck'); - performance.measure('Check', 'beforeCheck', 'afterCheck'); - } - - function unusedIsError(kind: UnusedKind, isAmbient: boolean): boolean { - if (isAmbient) { - return false; - } - switch (kind) { - case UnusedKind.Local: - return !!compilerOptions.noUnusedLocals; - case UnusedKind.Parameter: - return !!compilerOptions.noUnusedParameters; - default: - return Debug.assertNever(kind); - } - } - - function getPotentiallyUnusedIdentifiers(sourceFile: - SourceFile): readonly PotentiallyUnusedIdentifier[] - { - return allPotentiallyUnusedIdentifiers.get(sourceFile.path) - || emptyArray; - } - - // Fully type check a source file and collect the relevant diagnostics. - function checkSourceFileWorker(node: SourceFile) { - const links = getNodeLinks(node); - if (!(links.flags & NodeCheckFlags.TypeChecked)) { - if (skipTypeChecking(node, compilerOptions, host)) { - return; - } - - // Grammar checking - checkGrammarSourceFile(node); - - clear(potentialThisCollisions); - clear(potentialNewTargetCollisions); - clear(potentialWeakMapCollisions); - - forEach(node.statements, checkSourceElement); - checkSourceElement(node.endOfFileToken); - - checkDeferredNodes(node); - - if (isExternalOrCommonJsModule(node)) { - registerForUnusedIdentifiersCheck(node); - } - - if (!node.isDeclarationFile - && (compilerOptions.noUnusedLocals - || compilerOptions.noUnusedParameters)) - { - checkUnusedIdentifiers( - getPotentiallyUnusedIdentifiers(node), - (containingNode, kind, diag) => { - if (!containsParseError(containingNode) - && unusedIsError( - kind, - !!(containingNode.flags - & NodeFlags.Ambient) - )) - { - diagnostics.add(diag); - } - } - ); - } - - if (!node.isDeclarationFile && isExternalModule(node)) { - checkImportsForTypeOnlyConversion(node); - } - - if (isExternalOrCommonJsModule(node)) { - checkExternalModuleExports(node); - } - - if (potentialThisCollisions.length) { - forEach( - potentialThisCollisions, - checkIfThisIsCapturedInEnclosingScope - ); - clear(potentialThisCollisions); - } - - if (potentialNewTargetCollisions.length) { - forEach( - potentialNewTargetCollisions, - checkIfNewTargetIsCapturedInEnclosingScope - ); - clear(potentialNewTargetCollisions); - } - - if (potentialWeakMapCollisions.length) { - forEach(potentialWeakMapCollisions, checkWeakMapCollision); - clear(potentialWeakMapCollisions); - } - - links.flags |= NodeCheckFlags.TypeChecked; - } - } - - function getDiagnostics( - sourceFile: SourceFile, - ct: CancellationToken - ): Diagnostic[] { - try { - // Record the cancellation token so it can be checked later on during checkSourceElement. - // Do this in a finally block so we can ensure that it gets reset back to nothing after - // this call is done. - cancellationToken = ct; - return getDiagnosticsWorker(sourceFile); - } finally { - cancellationToken = undefined; - } - } - - function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { - throwIfNonDiagnosticsProducing(); - if (sourceFile) { - // Some global diagnostics are deferred until they are needed and - // may not be reported in the first call to getGlobalDiagnostics. - // We should catch these changes and report them. - const previousGlobalDiagnostics = diagnostics - .getGlobalDiagnostics(); - const previousGlobalDiagnosticsSize = previousGlobalDiagnostics - .length; - - checkSourceFile(sourceFile); - - const semanticDiagnostics = diagnostics - .getDiagnostics(sourceFile.fileName); - const currentGlobalDiagnostics = diagnostics - .getGlobalDiagnostics(); - if (currentGlobalDiagnostics !== previousGlobalDiagnostics) { - // If the arrays are not the same reference, new diagnostics were added. - const deferredGlobalDiagnostics = relativeComplement( - previousGlobalDiagnostics, - currentGlobalDiagnostics, - compareDiagnostics - ); - return concatenate( - deferredGlobalDiagnostics, - semanticDiagnostics - ); - } else if (previousGlobalDiagnosticsSize === 0 - && currentGlobalDiagnostics.length > 0) - { - // If the arrays are the same reference, but the length has changed, a single - // new diagnostic was added as DiagnosticCollection attempts to reuse the - // same array. - return concatenate( - currentGlobalDiagnostics, - semanticDiagnostics - ); - } - - return semanticDiagnostics; - } - - // Global diagnostics are always added when a file is not provided to - // getDiagnostics - forEach(host.getSourceFiles(), checkSourceFile); - return diagnostics.getDiagnostics(); - } - - function getGlobalDiagnostics(): Diagnostic[] { - throwIfNonDiagnosticsProducing(); - return diagnostics.getGlobalDiagnostics(); - } - - function throwIfNonDiagnosticsProducing() { - if (!produceDiagnostics) { - throw new Error('Trying to get diagnostics from a type checker that does not produce them.'); - } - } - - // Language service support - - function getSymbolsInScope( - location: Node, - meaning: SymbolFlags - ): Symbol[] { - if (location.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return []; - } - - const symbols = createSymbolTable(); - let isStatic = false; - - populateSymbols(); - - symbols.delete(InternalSymbolName.This); // Not a symbol, a keyword - return symbolsToArray(symbols); - - function populateSymbols() { - while (location) { - if (location.locals && !isGlobalSourceFile(location)) { - copySymbols(location.locals, meaning); - } - - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule( location)) break; - // falls through - case SyntaxKind.ModuleDeclaration: - copySymbols( - getSymbolOfNode(location as ModuleDeclaration - | SourceFile).exports!, - meaning & SymbolFlags.ModuleMember - ); - break; - case SyntaxKind.EnumDeclaration: - copySymbols( - getSymbolOfNode(location as EnumDeclaration) - .exports!, - meaning & SymbolFlags.EnumMember - ); - break; - case SyntaxKind.ClassExpression: - const className = (location as ClassExpression) - .name; - if (className) { - copySymbol(location.symbol, meaning); - } - - // this fall-through is necessary because we would like to handle - // type parameter inside class expression similar to how we handle it in classDeclaration and interface Declaration. - // falls through - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - // If we didn't come from static member of class or interface, - // add the type parameters into the symbol table - // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. - // Note: that the memberFlags come from previous iteration. - if (!isStatic) { - copySymbols( - getMembersOfSymbol(getSymbolOfNode(location as ClassDeclaration - | InterfaceDeclaration)), - meaning & SymbolFlags.Type - ); - } - break; - case SyntaxKind.FunctionExpression: - const funcName = (location as FunctionExpression) - .name; - if (funcName) { - copySymbol(location.symbol, meaning); - } - break; - } - - if (introducesArgumentsExoticObject(location)) { - copySymbol(argumentsSymbol, meaning); - } - - isStatic = hasModifier(location, ModifierFlags.Static); - location = location.parent; - } - - copySymbols(globals, meaning); - } - - /** - * Copy the given symbol into symbol tables if the symbol has the given meaning - * and it doesn't already existed in the symbol table - * @param key a key for storing in symbol table; if undefined, use symbol.name - * @param symbol the symbol to be added into symbol table - * @param meaning meaning of symbol to filter by before adding to symbol table - */ - function copySymbol(symbol: Symbol, meaning: SymbolFlags): void { - if (getCombinedLocalAndExportSymbolFlags(symbol) & meaning) { - const id = symbol.escapedName; - // We will copy all symbol regardless of its reserved name because - // symbolsToArray will check whether the key is a reserved name and - // it will not copy symbol with reserved name to the array - if (!symbols.has(id)) { - symbols.set(id, symbol); - } - } - } - - function copySymbols( - source: SymbolTable, - meaning: SymbolFlags - ): void { - if (meaning) { - source.forEach(symbol => { - copySymbol(symbol, meaning); - }); - } - } - } - - function isTypeDeclarationName(name: Node): boolean { - return name.kind === SyntaxKind.Identifier - && isTypeDeclaration(name.parent) - && name.parent.name === name; - } - - function isTypeDeclaration(node: - Node - ): node is TypeParameterDeclaration | ClassDeclaration - | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration - | ImportClause | ImportSpecifier | ExportSpecifier - { - switch (node.kind) { - case SyntaxKind.TypeParameter: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.EnumDeclaration: - return true; - case SyntaxKind.ImportClause: - return (node as ImportClause).isTypeOnly; - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - return (node as ImportSpecifier | ExportSpecifier).parent - .parent.isTypeOnly; - default: - return false; - } - } - - // True if the given identifier is part of a type reference - function isTypeReferenceIdentifier(node: EntityName): boolean { - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent as QualifiedName; - } - - return node.parent.kind === SyntaxKind.TypeReference; - } - - function isHeritageClauseElementIdentifier(node: Node): boolean { - while (node.parent.kind === SyntaxKind.PropertyAccessExpression) { - node = node.parent; - } - - return node.parent.kind === SyntaxKind.ExpressionWithTypeArguments; - } - - function forEachEnclosingClass( - node: Node, - callback: (node: Node) => T | undefined - ): T | undefined { - let result: T | undefined; - - while (true) { - node = getContainingClass(node)!; - if (!node) break; - if (result = callback(node)) break; - } - - return result; - } - - function isNodeUsedDuringClassInitialization(node: Node) { - return !!findAncestor(node, element => { - if (isConstructorDeclaration(element) - && nodeIsPresent(element.body) - || isPropertyDeclaration(element)) - { - return true; - } else if (isClassLike(element) - || isFunctionLikeDeclaration(element)) - { - return 'quit'; - } - - return false; - }); - } - - function isNodeWithinClass( - node: Node, - classDeclaration: ClassLikeDeclaration - ) { - return !!forEachEnclosingClass(node, n => n === classDeclaration); - } - - function getLeftSideOfImportEqualsOrExportAssignment(nodeOnRightSide: - EntityName): ImportEqualsDeclaration | ExportAssignment | undefined - { - while (nodeOnRightSide.parent.kind === SyntaxKind.QualifiedName) { - nodeOnRightSide = nodeOnRightSide.parent; - } - - if (nodeOnRightSide.parent.kind - === SyntaxKind.ImportEqualsDeclaration) - { - return ( nodeOnRightSide.parent) - .moduleReference === nodeOnRightSide - ? nodeOnRightSide.parent - : undefined; - } - - if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) { - return ( nodeOnRightSide.parent).expression - === nodeOnRightSide - ? nodeOnRightSide.parent - : undefined; - } - - return undefined; - } - - function isInRightSideOfImportOrExportAssignment(node: EntityName) { - return getLeftSideOfImportEqualsOrExportAssignment(node) - !== undefined; - } - - function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: - EntityName | PropertyAccessExpression) - { - const specialPropertyAssignmentKind = - getAssignmentDeclarationKind(entityName.parent - .parent as BinaryExpression); - switch (specialPropertyAssignmentKind) { - case AssignmentDeclarationKind.ExportsProperty: - case AssignmentDeclarationKind.PrototypeProperty: - return getSymbolOfNode(entityName.parent); - case AssignmentDeclarationKind.ThisProperty: - case AssignmentDeclarationKind.ModuleExports: - case AssignmentDeclarationKind.Property: - return getSymbolOfNode(entityName.parent.parent); - } - } - - function isImportTypeQualifierPart(node: EntityName): ImportTypeNode - | undefined - { - let parent = node.parent; - while (isQualifiedName(parent)) { - node = parent; - parent = parent.parent; - } - if (parent && parent.kind === SyntaxKind.ImportType - && (parent as ImportTypeNode).qualifier === node) - { - return parent as ImportTypeNode; - } - return undefined; - } - - function getSymbolOfNameOrPropertyAccessExpression(name: EntityName - | PrivateIdentifier | PropertyAccessExpression): Symbol | undefined - { - if (isDeclarationName(name)) { - return getSymbolOfNode(name.parent); - } - - if (isInJSFile(name) - && name.parent.kind === SyntaxKind.PropertyAccessExpression - && name.parent - === (name.parent.parent as BinaryExpression).left) - { - // Check if this is a special property assignment - if (!isPrivateIdentifier(name)) { - const specialPropertyAssignmentSymbol = - getSpecialPropertyAssignmentSymbolFromEntityName(name); - if (specialPropertyAssignmentSymbol) { - return specialPropertyAssignmentSymbol; - } - } - } - - if (name.parent.kind === SyntaxKind.ExportAssignment - && isEntityNameExpression(name)) - { - // Even an entity name expression that doesn't resolve as an entityname may still typecheck as a property access expression - const success = resolveEntityName( - name, - /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace - | SymbolFlags.Alias, /*ignoreErrors*/ - true - ); - if (success && success !== unknownSymbol) { - return success; - } - } else if (!isPropertyAccessExpression(name) - && !isPrivateIdentifier(name) - && isInRightSideOfImportOrExportAssignment(name)) - { - // Since we already checked for ExportAssignment, this really could only be an Import - const importEqualsDeclaration = getAncestor( - name, - SyntaxKind.ImportEqualsDeclaration - ); - Debug.assert(importEqualsDeclaration !== undefined); - return getSymbolOfPartOfRightHandSideOfImportEquals( - name, /*dontResolveAlias*/ - true - ); - } - - if (!isPropertyAccessExpression(name) - && !isPrivateIdentifier(name)) - { - const possibleImportNode = isImportTypeQualifierPart(name); - if (possibleImportNode) { - getTypeFromTypeNode(possibleImportNode); - const sym = getNodeLinks(name).resolvedSymbol; - return sym === unknownSymbol ? undefined : sym; - } - } - - while (isRightSideOfQualifiedNameOrPropertyAccess(name)) { - name = name.parent; - } - - if (isHeritageClauseElementIdentifier(name)) { - let meaning = SymbolFlags.None; - // In an interface or class, we're definitely interested in a type. - if (name.parent.kind - === SyntaxKind.ExpressionWithTypeArguments) - { - meaning = SymbolFlags.Type; - - // In a class 'extends' clause we are also looking for a value. - if (isExpressionWithTypeArgumentsInClassExtendsClause(name - .parent)) - { - meaning |= SymbolFlags.Value; - } - } else { - meaning = SymbolFlags.Namespace; - } - - meaning |= SymbolFlags.Alias; - const entityNameSymbol = isEntityNameExpression(name) - ? resolveEntityName(name, meaning) - : undefined; - if (entityNameSymbol) { - return entityNameSymbol; - } - } - - if (name.parent.kind === SyntaxKind.JSDocParameterTag) { - return getParameterSymbolFromJSDoc(name - .parent as JSDocParameterTag); - } - - if (name.parent.kind === SyntaxKind.TypeParameter - && name.parent.parent.kind === SyntaxKind.JSDocTemplateTag) - { - Debug - .assert(!isInJSFile(name)); // Otherwise `isDeclarationName` would have been true. - const typeParameter = - getTypeParameterFromJsDoc(name - .parent as TypeParameterDeclaration - & { parent: JSDocTemplateTag; }); - return typeParameter && typeParameter.symbol; - } - - if (isExpressionNode(name)) { - if (nodeIsMissing(name)) { - // Missing entity name. - return undefined; - } - - if (name.kind === SyntaxKind.Identifier) { - if (isJSXTagName(name) && isJsxIntrinsicIdentifier(name)) { - const symbol = - getIntrinsicTagSymbol( name - .parent); - return symbol === unknownSymbol ? undefined : symbol; - } - - return resolveEntityName( - name, - SymbolFlags.Value, /*ignoreErrors*/ - false, /*dontResolveAlias*/ - true - ); - } else if (name.kind === SyntaxKind.PropertyAccessExpression - || name.kind === SyntaxKind.QualifiedName) - { - const links = getNodeLinks(name); - if (links.resolvedSymbol) { - return links.resolvedSymbol; - } - - if (name.kind === SyntaxKind.PropertyAccessExpression) { - checkPropertyAccessExpression(name); - } else { - checkQualifiedName(name); - } - return links.resolvedSymbol; - } - } else if (isTypeReferenceIdentifier( name)) { - const meaning = name.parent.kind === SyntaxKind.TypeReference - ? SymbolFlags.Type - : SymbolFlags.Namespace; - return resolveEntityName( - name, - meaning, /*ignoreErrors*/ - false, /*dontResolveAlias*/ - true - ); - } - - if (name.parent.kind === SyntaxKind.TypePredicate) { - return resolveEntityName( - name, /*meaning*/ - SymbolFlags.FunctionScopedVariable - ); - } - - // Do we want to return undefined here? - return undefined; - } - - function getSymbolAtLocation(node: Node): Symbol | undefined { - if (node.kind === SyntaxKind.SourceFile) { - return isExternalModule( node) - ? getMergedSymbol(node.symbol) - : undefined; - } - const { parent } = node; - const grandParent = parent.parent; - - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return undefined; - } - - if (isDeclarationNameOrImportPropertyName(node)) { - // This is a declaration, call getSymbolOfNode - const parentSymbol = getSymbolOfNode(parent)!; - return isImportOrExportSpecifier(node.parent) - && node.parent.propertyName === node - ? getImmediateAliasedSymbol(parentSymbol) - : parentSymbol; - } else if (isLiteralComputedPropertyDeclarationName(node)) { - return getSymbolOfNode(parent.parent); - } - - if (node.kind === SyntaxKind.Identifier) { - if (isInRightSideOfImportOrExportAssignment( node)) { - return getSymbolOfNameOrPropertyAccessExpression( node); - } else if (parent.kind === SyntaxKind.BindingElement - && grandParent.kind === SyntaxKind.ObjectBindingPattern - && node === ( parent).propertyName) - { - const typeOfPattern = getTypeOfNode(grandParent); - const propertyDeclaration = getPropertyOfType( - typeOfPattern, - ( node).escapedText - ); - - if (propertyDeclaration) { - return propertyDeclaration; - } - } - } - - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PrivateIdentifier: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.QualifiedName: - return getSymbolOfNameOrPropertyAccessExpression( node); - - case SyntaxKind.ThisKeyword: - const container = getThisContainer( - node, /*includeArrowFunctions*/ - false - ); - if (isFunctionLike(container)) { - const sig = getSignatureFromDeclaration(container); - if (sig.thisParameter) { - return sig.thisParameter; - } - } - if (isInExpressionContext(node)) { - return checkExpression(node as Expression).symbol; - } - // falls through - case SyntaxKind.ThisType: - return getTypeFromThisTypeNode(node as ThisExpression - | ThisTypeNode).symbol; - - case SyntaxKind.SuperKeyword: - return checkExpression(node as Expression).symbol; - - case SyntaxKind.ConstructorKeyword: - // constructor keyword for an overload, should take us to the definition if it exist - const constructorDeclaration = node.parent; - if (constructorDeclaration - && constructorDeclaration.kind - === SyntaxKind.Constructor) - { - return ( constructorDeclaration - .parent).symbol; - } - return undefined; - - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - // 1). import x = require("./mo/*gotToDefinitionHere*/d") - // 2). External module name in an import declaration - // 3). Dynamic import call or require in javascript - // 4). type A = import("./f/*gotToDefinitionHere*/oo") - if ((isExternalModuleImportEqualsDeclaration(node.parent - .parent) - && getExternalModuleImportEqualsDeclarationExpression(node - .parent.parent) === node) - || ((node.parent.kind === SyntaxKind.ImportDeclaration - || node.parent.kind - === SyntaxKind.ExportDeclaration) - && ( node.parent) - .moduleSpecifier === node) - || ((isInJSFile(node) - && isRequireCall( - node - .parent, /*checkArgumentIsStringLiteralLike*/ - false - )) || isImportCall(node.parent)) - || (isLiteralTypeNode(node.parent) - && isLiteralImportTypeNode(node.parent.parent) - && node.parent.parent.argument === node.parent)) - { - return resolveExternalModuleName( - node, - node - ); - } - if (isCallExpression(parent) - && isBindableObjectDefinePropertyCall(parent) - && parent.arguments[1] === node) - { - return getSymbolOfNode(parent); - } - // falls through - case SyntaxKind.NumericLiteral: - // index access - const objectType = isElementAccessExpression(parent) - ? parent.argumentExpression === node - ? getTypeOfExpression(parent.expression) - : undefined - : isLiteralTypeNode(parent) - && isIndexedAccessTypeNode(grandParent) - ? getTypeFromTypeNode(grandParent.objectType) - : undefined; - return objectType - && getPropertyOfType( - objectType, - escapeLeadingUnderscores((node as StringLiteral - | NumericLiteral).text) - ); - - case SyntaxKind.DefaultKeyword: - case SyntaxKind.FunctionKeyword: - case SyntaxKind.EqualsGreaterThanToken: - case SyntaxKind.ClassKeyword: - return getSymbolOfNode(node.parent); - case SyntaxKind.ImportType: - return isLiteralImportTypeNode(node) - ? getSymbolAtLocation(node.argument.literal) - : undefined; - - case SyntaxKind.ExportKeyword: - return isExportAssignment(node.parent) - ? Debug.assertDefined(node.parent.symbol) - : undefined; - - default: - return undefined; - } - } - - function getShorthandAssignmentValueSymbol(location: Node): Symbol - | undefined - { - if (location - && location.kind === SyntaxKind.ShorthandPropertyAssignment) - { - return resolveEntityName( - ( location).name, - SymbolFlags.Value | SymbolFlags.Alias - ); - } - return undefined; - } - - /** Returns the target of an export specifier without following aliases */ - function getExportSpecifierLocalTargetSymbol(node: - ExportSpecifier): Symbol | undefined - { - return node.parent.parent.moduleSpecifier - ? getExternalModuleMember(node.parent.parent, node) - : resolveEntityName( - node.propertyName || node.name, - SymbolFlags.Value | SymbolFlags.Type - | SymbolFlags.Namespace | SymbolFlags.Alias - ); - } - - function getTypeOfNode(node: Node): Type { - if (node.flags & NodeFlags.InWithStatement) { - // We cannot answer semantic questions within a with block, do not proceed any further - return errorType; - } - - const classDecl = - tryGetClassImplementingOrExtendingExpressionWithTypeArguments(node); - const classType = classDecl - && getDeclaredTypeOfClassOrInterface(getSymbolOfNode(classDecl - .class)); - if (isPartOfTypeNode(node)) { - const typeFromTypeNode = getTypeFromTypeNode( node); - return classType - ? getTypeWithThisArgument( - typeFromTypeNode, - classType.thisType - ) - : typeFromTypeNode; - } - - if (isExpressionNode(node)) { - return getRegularTypeOfExpression( node); - } - - if (classType && !classDecl!.isImplements) { - // A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the - // extends clause of a class. We handle that case here. - const baseType = firstOrUndefined(getBaseTypes(classType)); - return baseType - ? getTypeWithThisArgument(baseType, classType.thisType) - : errorType; - } - - if (isTypeDeclaration(node)) { - // In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration - const symbol = getSymbolOfNode(node); - return getDeclaredTypeOfSymbol(symbol); - } - - if (isTypeDeclarationName(node)) { - const symbol = getSymbolAtLocation(node); - return symbol ? getDeclaredTypeOfSymbol(symbol) : errorType; - } - - if (isDeclaration(node)) { - // In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration - const symbol = getSymbolOfNode(node); - return getTypeOfSymbol(symbol); - } - - if (isDeclarationNameOrImportPropertyName(node)) { - const symbol = getSymbolAtLocation(node); - if (symbol) { - return isTypeOnlyImportOrExportName(node) - ? getDeclaredTypeOfSymbol(symbol) - : getTypeOfSymbol(symbol); - } - return errorType; - } - - if (isBindingPattern(node)) { - return getTypeForVariableLikeDeclaration( - node.parent, /*includeOptionality*/ - true - ) || errorType; - } - - if (isInRightSideOfImportOrExportAssignment( node)) { - const symbol = getSymbolAtLocation(node); - if (symbol) { - const declaredType = getDeclaredTypeOfSymbol(symbol); - return declaredType !== errorType - ? declaredType - : getTypeOfSymbol(symbol); - } - } - - return errorType; - } - - // Gets the type of object literal or array literal of destructuring assignment. - // { a } from - // for ( { a } of elems) { - // } - // [ a ] from - // [a] = [ some array ...] - function getTypeOfAssignmentPattern(expr: AssignmentPattern): Type - | undefined - { - Debug - .assert(expr.kind === SyntaxKind.ObjectLiteralExpression - || expr.kind === SyntaxKind.ArrayLiteralExpression); - // If this is from "for of" - // for ( { a } of elems) { - // } - if (expr.parent.kind === SyntaxKind.ForOfStatement) { - const iteratedType = checkRightHandSideOfForOf( - ( expr.parent).expression, - ( expr.parent).awaitModifier - ); - return checkDestructuringAssignment( - expr, - iteratedType || errorType - ); - } - // If this is from "for" initializer - // for ({a } = elems[0];.....) { } - if (expr.parent.kind === SyntaxKind.BinaryExpression) { - const iteratedType = - getTypeOfExpression(( expr.parent) - .right); - return checkDestructuringAssignment( - expr, - iteratedType || errorType - ); - } - // If this is from nested object binding pattern - // for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) { - if (expr.parent.kind === SyntaxKind.PropertyAssignment) { - const node = cast( - expr.parent.parent, - isObjectLiteralExpression - ); - const typeOfParentObjectLiteral = - getTypeOfAssignmentPattern(node) || errorType; - const propertyIndex = indexOfNode( - node.properties, - expr.parent - ); - return checkObjectLiteralDestructuringPropertyAssignment( - node, - typeOfParentObjectLiteral, - propertyIndex - ); - } - // Array literal assignment - array destructuring pattern - const node = cast(expr.parent, isArrayLiteralExpression); - // [{ property1: p1, property2 }] = elems; - const typeOfArrayLiteral = getTypeOfAssignmentPattern(node) - || errorType; - const elementType = - checkIteratedTypeOrElementType( - IterationUse.Destructuring, - typeOfArrayLiteral, - undefinedType, - expr.parent - ) || errorType; - return checkArrayLiteralDestructuringElementAssignment( - node, - typeOfArrayLiteral, - node.elements.indexOf(expr), - elementType - ); - } - - // Gets the property symbol corresponding to the property in destructuring assignment - // 'property1' from - // for ( { property1: a } of elems) { - // } - // 'property1' at location 'a' from: - // [a] = [ property1, property2 ] - function getPropertySymbolOfDestructuringAssignment(location: - Identifier) - { - // Get the type of the object or array literal and then look for property of given name in the type - const typeOfObjectLiteral = - getTypeOfAssignmentPattern(cast( - location.parent.parent, - isAssignmentPattern - )); - return typeOfObjectLiteral - && getPropertyOfType( - typeOfObjectLiteral, - location.escapedText - ); - } - - function getRegularTypeOfExpression(expr: Expression): Type { - if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) { - expr = expr.parent; - } - return getRegularTypeOfLiteralType(getTypeOfExpression(expr)); - } - - /** - * Gets either the static or instance type of a class element, based on - * whether the element is declared as "static". - */ - function getParentTypeOfClassElement(node: ClassElement) { - const classSymbol = getSymbolOfNode(node.parent)!; - return hasModifier(node, ModifierFlags.Static) - ? getTypeOfSymbol(classSymbol) - : getDeclaredTypeOfSymbol(classSymbol); - } - - function getClassElementPropertyKeyType(element: ClassElement) { - const name = element.name!; - switch (name.kind) { - case SyntaxKind.Identifier: - return getLiteralType(idText(name)); - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - return getLiteralType(name.text); - case SyntaxKind.ComputedPropertyName: - const nameType = checkComputedPropertyName(name); - return isTypeAssignableToKind( - nameType, - TypeFlags.ESSymbolLike - ) - ? nameType - : stringType; - default: - return Debug.fail('Unsupported property name.'); - } - } - - // Return the list of properties of the given type, augmented with properties from Function - // if the type has call or construct signatures - function getAugmentedPropertiesOfType(type: Type): Symbol[] { - type = getApparentType(type); - const propsByName = createSymbolTable(getPropertiesOfType(type)); - const functionType = - getSignaturesOfType(type, SignatureKind.Call).length - ? globalCallableFunctionType - : getSignaturesOfType(type, SignatureKind.Construct).length - ? globalNewableFunctionType - : undefined; - if (functionType) { - forEach(getPropertiesOfType(functionType), p => { - if (!propsByName.has(p.escapedName)) { - propsByName.set(p.escapedName, p); - } - }); - } - return getNamedMembers(propsByName); - } - - function typeHasCallOrConstructSignatures(type: Type): boolean { - return ts.typeHasCallOrConstructSignatures(type, checker); - } - - function getRootSymbols(symbol: Symbol): readonly Symbol[] { - const roots = getImmediateRootSymbols(symbol); - return roots ? flatMap(roots, getRootSymbols) : [symbol]; - } - function getImmediateRootSymbols(symbol: Symbol): readonly Symbol[] - | undefined - { - if (getCheckFlags(symbol) & CheckFlags.Synthetic) { - return mapDefined( - getSymbolLinks(symbol).containingType!.types, - type => getPropertyOfType(type, symbol.escapedName) - ); - } else if (symbol.flags & SymbolFlags.Transient) { - const { leftSpread, rightSpread, syntheticOrigin } = - symbol as TransientSymbol; - return leftSpread - ? [leftSpread, rightSpread!] - : syntheticOrigin - ? [syntheticOrigin] - : singleElementArray(tryGetAliasTarget(symbol)); - } - return undefined; - } - function tryGetAliasTarget(symbol: Symbol): Symbol | undefined { - let target: Symbol | undefined; - let next: Symbol | undefined = symbol; - while (next = getSymbolLinks(next).target) { - target = next; - } - return target; - } - - // Emitter support - - function isArgumentsLocalBinding(nodeIn: Identifier): boolean { - if (!isGeneratedIdentifier(nodeIn)) { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - const isPropertyName = - node.parent.kind - === SyntaxKind.PropertyAccessExpression - && ( node.parent).name - === node; - return !isPropertyName - && getReferencedValueSymbol(node) === argumentsSymbol; - } - } - - return false; - } - - function moduleExportsSomeValue(moduleReferenceExpression: - Expression): boolean - { - let moduleSymbol = resolveExternalModuleName( - moduleReferenceExpression.parent, - moduleReferenceExpression - ); - if (!moduleSymbol - || isShorthandAmbientModuleSymbol(moduleSymbol)) - { - // If the module is not found or is shorthand, assume that it may export a value. - return true; - } - - const hasExportAssignment = - hasExportAssignmentSymbol(moduleSymbol); - // if module has export assignment then 'resolveExternalModuleSymbol' will return resolved symbol for export assignment - // otherwise it will return moduleSymbol itself - moduleSymbol = resolveExternalModuleSymbol(moduleSymbol); - - const symbolLinks = getSymbolLinks(moduleSymbol); - if (symbolLinks.exportsSomeValue === undefined) { - // for export assignments - check if resolved symbol for RHS is itself a value - // otherwise - check if at least one export is value - symbolLinks.exportsSomeValue = hasExportAssignment - ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachEntry(getExportsOfModule(moduleSymbol), isValue); - } - - return symbolLinks.exportsSomeValue!; - - function isValue(s: Symbol): boolean { - s = resolveSymbol(s); - return s && !!(s.flags & SymbolFlags.Value); - } - } - - function isNameOfModuleOrEnumDeclaration(node: Identifier) { - return isModuleOrEnumDeclaration(node.parent) - && node === node.parent.name; - } - - // When resolved as an expression identifier, if the given node references an exported entity, return the declaration - // node of the exported entity's container. Otherwise, return undefined. - function getReferencedExportContainer( - nodeIn: Identifier, - prefixLocals?: boolean - ): SourceFile | ModuleDeclaration | EnumDeclaration | undefined { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - // When resolving the export container for the name of a module or enum - // declaration, we need to start resolution at the declaration's container. - // Otherwise, we could incorrectly resolve the export container as the - // declaration if it contains an exported member with the same name. - let symbol = getReferencedValueSymbol( - node, /*startInDeclarationContainer*/ - isNameOfModuleOrEnumDeclaration(node) - ); - if (symbol) { - if (symbol.flags & SymbolFlags.ExportValue) { - // If we reference an exported entity within the same module declaration, then whether - // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the - // kinds that we do NOT prefix. - const exportSymbol = - getMergedSymbol(symbol.exportSymbol!); - if (!prefixLocals - && exportSymbol.flags & SymbolFlags.ExportHasLocal - && !(exportSymbol.flags & SymbolFlags.Variable)) - { - return undefined; - } - symbol = exportSymbol; - } - const parentSymbol = getParentOfSymbol(symbol); - if (parentSymbol) { - if (parentSymbol.flags & SymbolFlags.ValueModule - && parentSymbol.valueDeclaration.kind - === SyntaxKind.SourceFile) - { - const symbolFile = parentSymbol - .valueDeclaration; - const referenceFile = getSourceFileOfNode(node); - // If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return undefined. - const symbolIsUmdExport = - symbolFile !== referenceFile; - return symbolIsUmdExport ? undefined : symbolFile; - } - return findAncestor( - node.parent, - (n): n is ModuleDeclaration - | EnumDeclaration => - isModuleOrEnumDeclaration(n) - && getSymbolOfNode(n) === parentSymbol - ); - } - } - } - } - - // When resolved as an expression identifier, if the given node references an import, return the declaration of - // that import. Otherwise, return undefined. - function getReferencedImportDeclaration(nodeIn: - Identifier): Declaration | undefined - { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - const symbol = getReferencedValueSymbol(node); - // We should only get the declaration of an alias if there isn't a local value - // declaration for the symbol - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value)) { - return getDeclarationOfAliasSymbol(symbol); - } - } - - return undefined; - } - - function isSymbolOfDestructuredElementOfCatchBinding(symbol: Symbol) { - return isBindingElement(symbol.valueDeclaration) - && walkUpBindingElementsAndPatterns(symbol.valueDeclaration) - .parent.kind === SyntaxKind.CatchClause; - } - - function isSymbolOfDeclarationWithCollidingName(symbol: - Symbol): boolean - { - if (symbol.flags & SymbolFlags.BlockScoped - && !isSourceFile(symbol.valueDeclaration)) - { - const links = getSymbolLinks(symbol); - if (links.isDeclarationWithCollidingName === undefined) { - const container = - getEnclosingBlockScopeContainer(symbol - .valueDeclaration); - if (isStatementWithLocals(container) - || isSymbolOfDestructuredElementOfCatchBinding(symbol)) - { - const nodeLinks = - getNodeLinks(symbol.valueDeclaration); - if (resolveName( - container.parent, - symbol.escapedName, - SymbolFlags.Value, /*nameNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - false - )) { - // redeclaration - always should be renamed - links.isDeclarationWithCollidingName = true; - } else if (nodeLinks.flags - & NodeCheckFlags.CapturedBlockScopedBinding) - { - // binding is captured in the function - // should be renamed if: - // - binding is not top level - top level bindings never collide with anything - // AND - // - binding is not declared in loop, should be renamed to avoid name reuse across siblings - // let a, b - // { let x = 1; a = () => x; } - // { let x = 100; b = () => x; } - // console.log(a()); // should print '1' - // console.log(b()); // should print '100' - // OR - // - binding is declared inside loop but not in inside initializer of iteration statement or directly inside loop body - // * variables from initializer are passed to rewritten loop body as parameters so they are not captured directly - // * variables that are declared immediately in loop body will become top level variable after loop is rewritten and thus - // they will not collide with anything - const isDeclaredInLoop = - nodeLinks.flags - & NodeCheckFlags.BlockScopedBindingInLoop; - const inLoopInitializer = isIterationStatement( - container, /*lookInLabeledStatements*/ - false - ); - const inLoopBodyBlock = - container.kind === SyntaxKind.Block - && isIterationStatement( - container - .parent, /*lookInLabeledStatements*/ - false - ); - - links - .isDeclarationWithCollidingName = !isBlockScopedContainerTopLevel(container) - && (!isDeclaredInLoop - || (!inLoopInitializer - && !inLoopBodyBlock)); - } else { - links.isDeclarationWithCollidingName = false; - } - } - } - return links.isDeclarationWithCollidingName!; - } - return false; - } - - // When resolved as an expression identifier, if the given node references a nested block scoped entity with - // a name that either hides an existing name or might hide it when compiled downlevel, - // return the declaration of that entity. Otherwise, return undefined. - function getReferencedDeclarationWithCollidingName(nodeIn: - Identifier): Declaration | undefined - { - if (!isGeneratedIdentifier(nodeIn)) { - const node = getParseTreeNode(nodeIn, isIdentifier); - if (node) { - const symbol = getReferencedValueSymbol(node); - if (symbol - && isSymbolOfDeclarationWithCollidingName(symbol)) - { - return symbol.valueDeclaration; - } - } - } - - return undefined; - } - - // Return true if the given node is a declaration of a nested block scoped entity with a name that either hides an - // existing name or might hide a name when compiled downlevel - function isDeclarationWithCollidingName(nodeIn: Declaration): boolean { - const node = getParseTreeNode(nodeIn, isDeclaration); - if (node) { - const symbol = getSymbolOfNode(node); - if (symbol) { - return isSymbolOfDeclarationWithCollidingName(symbol); - } - } - - return false; - } - - function isValueAliasDeclaration(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - return isAliasResolvedToValue(getSymbolOfNode(node) - || unknownSymbol); - case SyntaxKind.ExportDeclaration: - const exportClause = ( node) - .exportClause; - return !!exportClause && ( - isNamespaceExport(exportClause) - || some(exportClause.elements, isValueAliasDeclaration) - ); - case SyntaxKind.ExportAssignment: - return ( node).expression - && ( node).expression.kind - === SyntaxKind.Identifier - ? isAliasResolvedToValue(getSymbolOfNode(node) - || unknownSymbol) - : true; - } - return false; - } - - function isTopLevelValueImportEqualsWithEntityName(nodeIn: - ImportEqualsDeclaration): boolean - { - const node = getParseTreeNode(nodeIn, isImportEqualsDeclaration); - if (node === undefined - || node.parent.kind !== SyntaxKind.SourceFile - || !isInternalModuleImportEqualsDeclaration(node)) - { - // parent is not source file or it is not reference to internal module - return false; - } - - const isValue = isAliasResolvedToValue(getSymbolOfNode(node)); - return isValue && node.moduleReference - && !nodeIsMissing(node.moduleReference); - } - - function isAliasResolvedToValue(symbol: Symbol): boolean { - const target = resolveAlias(symbol); - if (target === unknownSymbol) { - return true; - } - // const enums and modules that contain only const enums are not considered values from the emit perspective - // unless 'preserveConstEnums' option is set to true - return !!(target.flags & SymbolFlags.Value) - && (compilerOptions.preserveConstEnums - || !isConstEnumOrConstEnumOnlyModule(target)); - } - - function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean { - return isConstEnumSymbol(s) || !!s.constEnumOnlyModule; - } - - function isReferencedAliasDeclaration( - node: Node, - checkChildren?: boolean - ): boolean { - if (isAliasSymbolDeclaration(node)) { - const symbol = getSymbolOfNode(node); - if (symbol && getSymbolLinks(symbol).referenced) { - return true; - } - const target = getSymbolLinks(symbol!) - .target; // TODO: GH#18217 - if (target && getModifierFlags(node) & ModifierFlags.Export - && target.flags & SymbolFlags.Value - && (compilerOptions.preserveConstEnums - || !isConstEnumOrConstEnumOnlyModule(target))) - { - // An `export import ... =` of a value symbol is always considered referenced - return true; - } - } - - if (checkChildren) { - return !!forEachChild( - node, - node => isReferencedAliasDeclaration(node, checkChildren) - ); - } - return false; - } - - function isImplementationOfOverload(node: SignatureDeclaration) { - if (nodeIsPresent((node as FunctionLikeDeclaration).body)) { - if (isGetAccessor(node) - || isSetAccessor(node)) - return false; // Get or set accessors can never be overload implementations, but can have up to 2 signatures - const symbol = getSymbolOfNode(node); - const signaturesOfSymbol = getSignaturesOfSymbol(symbol); - // If this function body corresponds to function with multiple signature, it is implementation of overload - // e.g.: function foo(a: string): string; - // function foo(a: number): number; - // function foo(a: any) { // This is implementation of the overloads - // return a; - // } - return signaturesOfSymbol.length > 1 - // If there is single signature for the symbol, it is overload if that signature isn't coming from the node - // e.g.: function foo(a: string): string; - // function foo(a: any) { // This is implementation of the overloads - // return a; - // } - || (signaturesOfSymbol.length === 1 - && signaturesOfSymbol[0].declaration !== node); - } - return false; - } - - function isRequiredInitializedParameter(parameter: ParameterDeclaration - | JSDocParameterTag): boolean - { - return !!strictNullChecks - && !isOptionalParameter(parameter) - && !isJSDocParameterTag(parameter) - && !!parameter.initializer - && !hasModifier( - parameter, - ModifierFlags.ParameterPropertyModifier - ); - } - - function isOptionalUninitializedParameterProperty(parameter: - ParameterDeclaration) - { - return strictNullChecks - && isOptionalParameter(parameter) - && !parameter.initializer - && hasModifier( - parameter, - ModifierFlags.ParameterPropertyModifier - ); - } - - function isExpandoFunctionDeclaration(node: Declaration): boolean { - const declaration = getParseTreeNode(node, isFunctionDeclaration); - if (!declaration) { - return false; - } - const symbol = getSymbolOfNode(declaration); - if (!symbol || !(symbol.flags & SymbolFlags.Function)) { - return false; - } - return !!forEachEntry( - getExportsOfSymbol(symbol), - p => p.flags & SymbolFlags.Value && p.valueDeclaration - && isPropertyAccessExpression(p.valueDeclaration) - ); - } - - function getPropertiesOfContainerFunction(node: - Declaration): Symbol[] - { - const declaration = getParseTreeNode(node, isFunctionDeclaration); - if (!declaration) { - return emptyArray; - } - const symbol = getSymbolOfNode(declaration); - return symbol && getPropertiesOfType(getTypeOfSymbol(symbol)) - || emptyArray; - } - - function getNodeCheckFlags(node: Node): NodeCheckFlags { - return getNodeLinks(node).flags || 0; - } - - function getEnumMemberValue(node: EnumMember): string | number - | undefined - { - computeEnumMemberValues(node.parent); - return getNodeLinks(node).enumMemberValue; - } - - function canHaveConstantValue(node: Node): node is EnumMember - | AccessExpression - { - switch (node.kind) { - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return true; - } - return false; - } - - function getConstantValue(node: EnumMember | AccessExpression): string - | number | undefined - { - if (node.kind === SyntaxKind.EnumMember) { - return getEnumMemberValue(node); - } - - const symbol = getNodeLinks(node).resolvedSymbol; - if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { - // inline property\index accesses only for const enums - const member = symbol.valueDeclaration as EnumMember; - if (isEnumConst(member.parent)) { - return getEnumMemberValue(member); - } - } - - return undefined; - } - - function isFunctionType(type: Type): boolean { - return !!(type.flags & TypeFlags.Object) - && getSignaturesOfType(type, SignatureKind.Call).length > 0; - } - - function getTypeReferenceSerializationKind( - typeNameIn: EntityName, - location?: Node - ): TypeReferenceSerializationKind { - // ensure both `typeName` and `location` are parse tree nodes. - const typeName = getParseTreeNode(typeNameIn, isEntityName); - if (!typeName) return TypeReferenceSerializationKind.Unknown; - - if (location) { - location = getParseTreeNode(location); - if (!location) return TypeReferenceSerializationKind.Unknown; - } - - // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. - const valueSymbol = resolveEntityName( - typeName, - SymbolFlags.Value, /*ignoreErrors*/ - true, /*dontResolveAlias*/ - false, - location - ); - - // Resolve the symbol as a type so that we can provide a more useful hint for the type serializer. - const typeSymbol = resolveEntityName( - typeName, - SymbolFlags.Type, /*ignoreErrors*/ - true, /*dontResolveAlias*/ - false, - location - ); - if (valueSymbol && valueSymbol === typeSymbol) { - const globalPromiseSymbol = - getGlobalPromiseConstructorSymbol(/*reportErrors*/ false); - if (globalPromiseSymbol - && valueSymbol === globalPromiseSymbol) - { - return TypeReferenceSerializationKind.Promise; - } - - const constructorType = getTypeOfSymbol(valueSymbol); - if (constructorType && isConstructorType(constructorType)) { - return TypeReferenceSerializationKind - .TypeWithConstructSignatureAndValue; - } - } - - // We might not be able to resolve type symbol so use unknown type in that case (eg error case) - if (!typeSymbol) { - return TypeReferenceSerializationKind.Unknown; - } - const type = getDeclaredTypeOfSymbol(typeSymbol); - if (type === errorType) { - return TypeReferenceSerializationKind.Unknown; - } else if (type.flags & TypeFlags.AnyOrUnknown) { - return TypeReferenceSerializationKind.ObjectType; - } else if (isTypeAssignableToKind( - type, - TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never - )) { - return TypeReferenceSerializationKind.VoidNullableOrNeverType; - } else if (isTypeAssignableToKind(type, TypeFlags.BooleanLike)) { - return TypeReferenceSerializationKind.BooleanType; - } else if (isTypeAssignableToKind(type, TypeFlags.NumberLike)) { - return TypeReferenceSerializationKind.NumberLikeType; - } else if (isTypeAssignableToKind(type, TypeFlags.BigIntLike)) { - return TypeReferenceSerializationKind.BigIntLikeType; - } else if (isTypeAssignableToKind(type, TypeFlags.StringLike)) { - return TypeReferenceSerializationKind.StringLikeType; - } else if (isTupleType(type)) { - return TypeReferenceSerializationKind.ArrayLikeType; - } else if (isTypeAssignableToKind(type, TypeFlags.ESSymbolLike)) { - return TypeReferenceSerializationKind.ESSymbolType; - } else if (isFunctionType(type)) { - return TypeReferenceSerializationKind.TypeWithCallSignature; - } else if (isArrayType(type)) { - return TypeReferenceSerializationKind.ArrayLikeType; - } else { - return TypeReferenceSerializationKind.ObjectType; - } - } - - function createTypeOfDeclaration( - declarationIn: AccessorDeclaration | VariableLikeDeclaration - | PropertyAccessExpression, - enclosingDeclaration: Node, - flags: NodeBuilderFlags, - tracker: SymbolTracker, - addUndefined?: boolean - ) { - const declaration = getParseTreeNode( - declarationIn, - isVariableLikeOrAccessor - ); - if (!declaration) { - return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; - } - // Get type of the symbol if this is the valid symbol otherwise get type at location - const symbol = getSymbolOfNode(declaration); - let type = - symbol - && !(symbol.flags - & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) - ? getWidenedLiteralType(getTypeOfSymbol(symbol)) - : errorType; - if (type.flags & TypeFlags.UniqueESSymbol - && type.symbol === symbol) - { - flags |= NodeBuilderFlags.AllowUniqueESSymbolType; - } - if (addUndefined) { - type = getOptionalType(type); - } - return nodeBuilder.typeToTypeNode( - type, - enclosingDeclaration, - flags | NodeBuilderFlags.MultilineObjectLiterals, - tracker - ); - } - - function createReturnTypeOfSignatureDeclaration( - signatureDeclarationIn: SignatureDeclaration, - enclosingDeclaration: Node, - flags: NodeBuilderFlags, - tracker: SymbolTracker - ) { - const signatureDeclaration = getParseTreeNode( - signatureDeclarationIn, - isFunctionLike - ); - if (!signatureDeclaration) { - return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; - } - const signature = - getSignatureFromDeclaration(signatureDeclaration); - return nodeBuilder.typeToTypeNode( - getReturnTypeOfSignature(signature), - enclosingDeclaration, - flags | NodeBuilderFlags.MultilineObjectLiterals, - tracker - ); - } - - function createTypeOfExpression( - exprIn: Expression, - enclosingDeclaration: Node, - flags: NodeBuilderFlags, - tracker: SymbolTracker - ) { - const expr = getParseTreeNode(exprIn, isExpression); - if (!expr) { - return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; - } - const type = getWidenedType(getRegularTypeOfExpression(expr)); - return nodeBuilder.typeToTypeNode( - type, - enclosingDeclaration, - flags | NodeBuilderFlags.MultilineObjectLiterals, - tracker - ); - } - - function hasGlobalName(name: string): boolean { - return globals.has(escapeLeadingUnderscores(name)); - } - - function getReferencedValueSymbol( - reference: Identifier, - startInDeclarationContainer?: boolean - ): Symbol | undefined { - const resolvedSymbol = getNodeLinks(reference).resolvedSymbol; - if (resolvedSymbol) { - return resolvedSymbol; - } - - let location: Node = reference; - if (startInDeclarationContainer) { - // When resolving the name of a declaration as a value, we need to start resolution - // at a point outside of the declaration. - const parent = reference.parent; - if (isDeclaration(parent) && reference === parent.name) { - location = getDeclarationContainer(parent); - } - } - - return resolveName( - location, - reference.escapedText, - SymbolFlags.Value | SymbolFlags.ExportValue - | SymbolFlags.Alias, /*nodeNotFoundMessage*/ - undefined, /*nameArg*/ - undefined, /*isUse*/ - true - ); - } - - function getReferencedValueDeclaration(referenceIn: - Identifier): Declaration | undefined - { - if (!isGeneratedIdentifier(referenceIn)) { - const reference = getParseTreeNode(referenceIn, isIdentifier); - if (reference) { - const symbol = getReferencedValueSymbol(reference); - if (symbol) { - return getExportSymbolOfValueSymbolIfExported(symbol) - .valueDeclaration; - } - } - } - - return undefined; - } - - function isLiteralConstDeclaration(node: VariableDeclaration - | PropertyDeclaration | PropertySignature - | ParameterDeclaration): boolean - { - if (isDeclarationReadonly(node) || isVariableDeclaration(node) - && isVarConst(node)) - { - return isFreshLiteralType(getTypeOfSymbol(getSymbolOfNode(node))); - } - return false; - } - - function literalTypeToNode( - type: FreshableType, - enclosing: Node, - tracker: SymbolTracker - ): Expression { - const enumResult = type.flags & TypeFlags.EnumLiteral - ? nodeBuilder.symbolToExpression( - type.symbol, - SymbolFlags.Value, - enclosing, /*flags*/ - undefined, - tracker - ) - : type === trueType - ? createTrue() - : type === falseType && createFalse(); - return enumResult || createLiteral((type as LiteralType).value); - } - - function createLiteralConstValue( - node: VariableDeclaration | PropertyDeclaration | PropertySignature - | ParameterDeclaration, - tracker: SymbolTracker - ) { - const type = getTypeOfSymbol(getSymbolOfNode(node)); - return literalTypeToNode( type, node, tracker); - } - - function createResolver(): EmitResolver { - // this variable and functions that use it are deliberately moved here from the outer scope - // to avoid scope pollution - const resolvedTypeReferenceDirectives = host - .getResolvedTypeReferenceDirectives(); - let fileToDirective: Map; - if (resolvedTypeReferenceDirectives) { - // populate reverse mapping: file path -> type reference directive that was resolved to this file - fileToDirective = createMap(); - resolvedTypeReferenceDirectives - .forEach((resolvedDirective, key) => { - if (!resolvedDirective - || !resolvedDirective.resolvedFileName) - { - return; - } - const file = host - .getSourceFile(resolvedDirective - .resolvedFileName)!; - // Add the transitive closure of path references loaded by this file (as long as they are not) - // part of an existing type reference. - addReferencedFilesToTypeDirective(file, key); - }); - } - - return { - getReferencedExportContainer, - getReferencedImportDeclaration, - getReferencedDeclarationWithCollidingName, - isDeclarationWithCollidingName, - isValueAliasDeclaration: node => { - node = getParseTreeNode(node); - // Synthesized nodes are always treated like values. - return node ? isValueAliasDeclaration(node) : true; - }, - hasGlobalName, - isReferencedAliasDeclaration: (node, checkChildren?) => { - node = getParseTreeNode(node); - // Synthesized nodes are always treated as referenced. - return node - ? isReferencedAliasDeclaration(node, checkChildren) - : true; - }, - getNodeCheckFlags: node => { - node = getParseTreeNode(node); - return node ? getNodeCheckFlags(node) : 0; - }, - isTopLevelValueImportEqualsWithEntityName, - isDeclarationVisible, - isImplementationOfOverload, - isRequiredInitializedParameter, - isOptionalUninitializedParameterProperty, - isExpandoFunctionDeclaration, - getPropertiesOfContainerFunction, - createTypeOfDeclaration, - createReturnTypeOfSignatureDeclaration, - createTypeOfExpression, - createLiteralConstValue, - isSymbolAccessible, - isEntityNameVisible, - getConstantValue: nodeIn => { - const node = getParseTreeNode( - nodeIn, - canHaveConstantValue - ); - return node ? getConstantValue(node) : undefined; - }, - collectLinkedAliases, - getReferencedValueDeclaration, - getTypeReferenceSerializationKind, - isOptionalParameter, - moduleExportsSomeValue, - isArgumentsLocalBinding, - getExternalModuleFileFromDeclaration, - getTypeReferenceDirectivesForEntityName, - getTypeReferenceDirectivesForSymbol, - isLiteralConstDeclaration, - isLateBound: - (nodeIn: Declaration): nodeIn is LateBoundDeclaration => { - const node = getParseTreeNode(nodeIn, isDeclaration); - const symbol = node && getSymbolOfNode(node); - return !!(symbol - && getCheckFlags(symbol) & CheckFlags.Late); - }, - getJsxFactoryEntity: location => location - ? (getJsxNamespace(location), - (getSourceFileOfNode(location).localJsxFactory - || _jsxFactoryEntity)) - : _jsxFactoryEntity, - getAllAccessorDeclarations(accessor: - AccessorDeclaration): AllAccessorDeclarations - { - accessor = getParseTreeNode( - accessor, - isGetOrSetAccessorDeclaration - )!; // TODO: GH#18217 - const otherKind = accessor.kind === SyntaxKind.SetAccessor - ? SyntaxKind.GetAccessor - : SyntaxKind.SetAccessor; - const otherAccessor = - getDeclarationOfKind( - getSymbolOfNode(accessor), - otherKind - ); - const firstAccessor = - otherAccessor && (otherAccessor.pos < accessor.pos) - ? otherAccessor - : accessor; - const secondAccessor = - otherAccessor && (otherAccessor.pos < accessor.pos) - ? accessor - : otherAccessor; - const setAccessor = - accessor.kind === SyntaxKind.SetAccessor - ? accessor - : otherAccessor as SetAccessorDeclaration; - const getAccessor = - accessor.kind === SyntaxKind.GetAccessor - ? accessor - : otherAccessor as GetAccessorDeclaration; - return { - firstAccessor, - secondAccessor, - setAccessor, - getAccessor - }; - }, - getSymbolOfExternalModuleSpecifier: - moduleName => resolveExternalModuleNameWorker( - moduleName, - moduleName, /*moduleNotFoundError*/ - undefined - ), - isBindingCapturedByNode: (node, decl) => { - const parseNode = getParseTreeNode(node); - const parseDecl = getParseTreeNode(decl); - return !!parseNode && !!parseDecl - && (isVariableDeclaration(parseDecl) - || isBindingElement(parseDecl)) - && isBindingCapturedByNode(parseNode, parseDecl); - }, - getDeclarationStatementsForSourceFile: ( - node, - flags, - tracker, - bundled - ) => { - const n = getParseTreeNode(node) as SourceFile; - Debug.assert( - n && n.kind === SyntaxKind.SourceFile, - 'Non-sourcefile node passed into getDeclarationsForSourceFile' - ); - const sym = getSymbolOfNode(node); - if (!sym) { - return !node.locals - ? [] - : nodeBuilder.symbolTableToDeclarationStatements( - node.locals, - node, - flags, - tracker, - bundled - ); - } - return !sym.exports - ? [] - : nodeBuilder.symbolTableToDeclarationStatements( - sym.exports, - node, - flags, - tracker, - bundled - ); - } - }; - - function isInHeritageClause(node: - PropertyAccessEntityNameExpression) - { - return node.parent - && node.parent.kind - === SyntaxKind.ExpressionWithTypeArguments - && node.parent.parent - && node.parent.parent.kind === SyntaxKind.HeritageClause; - } - - // defined here to avoid outer scope pollution - function getTypeReferenceDirectivesForEntityName(node: - EntityNameOrEntityNameExpression): string[] | undefined - { - // program does not have any files with type reference directives - bail out - if (!fileToDirective) { - return undefined; - } - // property access can only be used as values, or types when within an expression with type arguments inside a heritage clause - // qualified names can only be used as types\namespaces - // identifiers are treated as values only if they appear in type queries - let meaning = SymbolFlags.Type | SymbolFlags.Namespace; - if ((node.kind === SyntaxKind.Identifier - && isInTypeQuery(node)) - || (node.kind === SyntaxKind.PropertyAccessExpression - && !isInHeritageClause(node))) - { - meaning = SymbolFlags.Value | SymbolFlags.ExportValue; - } - - const symbol = resolveEntityName( - node, - meaning, /*ignoreErrors*/ - true - ); - return symbol && symbol !== unknownSymbol - ? getTypeReferenceDirectivesForSymbol(symbol, meaning) - : undefined; - } - - // defined here to avoid outer scope pollution - function getTypeReferenceDirectivesForSymbol( - symbol: Symbol, - meaning?: SymbolFlags - ): string[] | undefined { - // program does not have any files with type reference directives - bail out - if (!fileToDirective) { - return undefined; - } - if (!isSymbolFromTypeDeclarationFile(symbol)) { - return undefined; - } - // check what declarations in the symbol can contribute to the target meaning - let typeReferenceDirectives: string[] | undefined; - for (const decl of symbol.declarations) { - // check meaning of the local symbol to see if declaration needs to be analyzed further - if (decl.symbol && decl.symbol.flags & meaning!) { - const file = getSourceFileOfNode(decl); - const typeReferenceDirective = fileToDirective - .get(file.path); - if (typeReferenceDirective) { - (typeReferenceDirectives - || (typeReferenceDirectives = [])) - .push(typeReferenceDirective); - } else { - // found at least one entry that does not originate from type reference directive - return undefined; - } - } - } - return typeReferenceDirectives; - } - - function isSymbolFromTypeDeclarationFile(symbol: Symbol): boolean { - // bail out if symbol does not have associated declarations (i.e. this is transient symbol created for property in binding pattern) - if (!symbol.declarations) { - return false; - } - - // walk the parent chain for symbols to make sure that top level parent symbol is in the global scope - // external modules cannot define or contribute to type declaration files - let current = symbol; - while (true) { - const parent = getParentOfSymbol(current); - if (parent) { - current = parent; - } else { - break; - } - } - - if (current.valueDeclaration - && current.valueDeclaration.kind === SyntaxKind.SourceFile - && current.flags & SymbolFlags.ValueModule) - { - return false; - } - - // check that at least one declaration of top level symbol originates from type declaration file - for (const decl of symbol.declarations) { - const file = getSourceFileOfNode(decl); - if (fileToDirective.has(file.path)) { - return true; - } - } - return false; - } - - function addReferencedFilesToTypeDirective( - file: SourceFile, - key: string - ) { - if (fileToDirective.has(file.path)) return; - fileToDirective.set(file.path, key); - for (const { fileName } of file.referencedFiles) { - const resolvedFile = resolveTripleslashReference( - fileName, - file.originalFileName - ); - const referencedFile = host.getSourceFile(resolvedFile); - if (referencedFile) { - addReferencedFilesToTypeDirective(referencedFile, key); - } - } - } - } - - function getExternalModuleFileFromDeclaration(declaration: - AnyImportOrReExport | ModuleDeclaration - | ImportTypeNode): SourceFile | undefined - { - const specifier = declaration.kind === SyntaxKind.ModuleDeclaration - ? tryCast(declaration.name, isStringLiteral) - : getExternalModuleName(declaration); - const moduleSymbol = resolveExternalModuleNameWorker( - specifier!, - specifier!, /*moduleNotFoundError*/ - undefined - ); // TODO: GH#18217 - if (!moduleSymbol) { - return undefined; - } - return getDeclarationOfKind(moduleSymbol, SyntaxKind.SourceFile); - } - - function initializeTypeChecker() { - // Bind all source files and propagate errors - for (const file of host.getSourceFiles()) { - bindSourceFile(file, compilerOptions); - } - - amalgamatedDuplicates = createMap(); - - // Initialize global symbol table - let augmentations: (readonly (StringLiteral | Identifier)[])[] - | undefined; - for (const file of host.getSourceFiles()) { - if (file.redirectInfo) { - continue; - } - if (!isExternalOrCommonJsModule(file)) { - // It is an error for a non-external-module (i.e. script) to declare its own `globalThis`. - // We can't use `builtinGlobals` for this due to synthetic expando-namespace generation in JS files. - const fileGlobalThisSymbol = file.locals! - .get('globalThis' as __String); - if (fileGlobalThisSymbol) { - for (const declaration of fileGlobalThisSymbol - .declarations) - { - diagnostics - .add(createDiagnosticForNode( - declaration, - Diagnostics - .Declaration_name_conflicts_with_built_in_global_identifier_0, - 'globalThis' - )); - } - } - mergeSymbolTable(globals, file.locals!); - } - if (file.jsGlobalAugmentations) { - mergeSymbolTable(globals, file.jsGlobalAugmentations); - } - if (file.patternAmbientModules - && file.patternAmbientModules.length) - { - patternAmbientModules = concatenate( - patternAmbientModules, - file.patternAmbientModules - ); - } - if (file.moduleAugmentations.length) { - (augmentations || (augmentations = [])) - .push(file.moduleAugmentations); - } - if (file.symbol && file.symbol.globalExports) { - // Merge in UMD exports with first-in-wins semantics (see #9771) - const source = file.symbol.globalExports; - source.forEach((sourceSymbol, id) => { - if (!globals.has(id)) { - globals.set(id, sourceSymbol); - } - }); - } - } - - // We do global augmentations separately from module augmentations (and before creating global types) because they - // 1. Affect global types. We won't have the correct global types until global augmentations are merged. Also, - // 2. Module augmentation instantiation requires creating the type of a module, which, in turn, can require - // checking for an export or property on the module (if export=) which, in turn, can fall back to the - // apparent type of the module - either globalObjectType or globalFunctionType - which wouldn't exist if we - // did module augmentations prior to finalizing the global types. - if (augmentations) { - // merge _global_ module augmentations. - // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed - for (const list of augmentations) { - for (const augmentation of list) { - if (!isGlobalScopeAugmentation(augmentation - .parent as ModuleDeclaration)) - { - continue; - } - mergeModuleAugmentation(augmentation); - } - } - } - - // Setup global builtins - addToSymbolTable( - globals, - builtinGlobals, - Diagnostics - .Declaration_name_conflicts_with_built_in_global_identifier_0 - ); - - getSymbolLinks(undefinedSymbol).type = undefinedWideningType; - getSymbolLinks(argumentsSymbol).type = getGlobalType( - 'IArguments' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - getSymbolLinks(unknownSymbol).type = errorType; - getSymbolLinks(globalThisSymbol).type = createObjectType( - ObjectFlags.Anonymous, - globalThisSymbol - ); - - // Initialize special types - globalArrayType = getGlobalType( - 'Array' as __String, /*arity*/ - 1, /*reportErrors*/ - true - ); - globalObjectType = getGlobalType( - 'Object' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - globalFunctionType = getGlobalType( - 'Function' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - globalCallableFunctionType = strictBindCallApply - && getGlobalType( - 'CallableFunction' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ) || globalFunctionType; - globalNewableFunctionType = strictBindCallApply - && getGlobalType( - 'NewableFunction' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ) || globalFunctionType; - globalStringType = getGlobalType( - 'String' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - globalNumberType = getGlobalType( - 'Number' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - globalBooleanType = getGlobalType( - 'Boolean' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - globalRegExpType = getGlobalType( - 'RegExp' as __String, /*arity*/ - 0, /*reportErrors*/ - true - ); - anyArrayType = createArrayType(anyType); - - autoArrayType = createArrayType(autoType); - if (autoArrayType === emptyObjectType) { - // autoArrayType is used as a marker, so even if global Array type is not defined, it needs to be a unique type - autoArrayType = createAnonymousType( - undefined, - emptySymbols, - emptyArray, - emptyArray, - undefined, - undefined - ); - } - - globalReadonlyArrayType = getGlobalTypeOrUndefined( - 'ReadonlyArray' as __String, /*arity*/ - 1 - ) || globalArrayType; - anyReadonlyArrayType = globalReadonlyArrayType - ? createTypeFromGenericGlobalType( - globalReadonlyArrayType, - [anyType] - ) - : anyArrayType; - globalThisType = getGlobalTypeOrUndefined( - 'ThisType' as __String, /*arity*/ - 1 - ); - - if (augmentations) { - // merge _nonglobal_ module augmentations. - // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed - for (const list of augmentations) { - for (const augmentation of list) { - if (isGlobalScopeAugmentation(augmentation - .parent as ModuleDeclaration)) - { - continue; - } - mergeModuleAugmentation(augmentation); - } - } - } - - amalgamatedDuplicates - .forEach(({ firstFile, secondFile, conflictingSymbols }) => { - // If not many things conflict, issue individual errors - if (conflictingSymbols.size < 8) { - conflictingSymbols - .forEach(( - { isBlockScoped, firstFileLocations, - secondFileLocations }, - symbolName - ) => { - const message = isBlockScoped - ? Diagnostics - .Cannot_redeclare_block_scoped_variable_0 - : Diagnostics.Duplicate_identifier_0; - for (const node of firstFileLocations) { - addDuplicateDeclarationError( - node, - message, - symbolName, - secondFileLocations - ); - } - for (const node of secondFileLocations) { - addDuplicateDeclarationError( - node, - message, - symbolName, - firstFileLocations - ); - } - }); - } else { - // Otherwise issue top-level error since the files appear very identical in terms of what they contain - const list = arrayFrom(conflictingSymbols.keys()) - .join(', '); - diagnostics.add(addRelatedInfo( - createDiagnosticForNode( - firstFile, - Diagnostics - .Definitions_of_the_following_identifiers_conflict_with_those_in_another_file_Colon_0, - list - ), - createDiagnosticForNode( - secondFile, - Diagnostics.Conflicts_are_in_this_file - ) - )); - diagnostics.add(addRelatedInfo( - createDiagnosticForNode( - secondFile, - Diagnostics - .Definitions_of_the_following_identifiers_conflict_with_those_in_another_file_Colon_0, - list - ), - createDiagnosticForNode( - firstFile, - Diagnostics.Conflicts_are_in_this_file - ) - )); - } - }); - amalgamatedDuplicates = undefined; - } - - function checkExternalEmitHelpers( - location: Node, - helpers: ExternalEmitHelpers - ) { - if ((requestedExternalEmitHelpers & helpers) !== helpers - && compilerOptions.importHelpers) - { - const sourceFile = getSourceFileOfNode(location); - if (isEffectiveExternalModule(sourceFile, compilerOptions) - && !(location.flags & NodeFlags.Ambient)) - { - const helpersModule = resolveHelpersModule( - sourceFile, - location - ); - if (helpersModule !== unknownSymbol) { - const uncheckedHelpers = - helpers & ~requestedExternalEmitHelpers; - for (let helper = ExternalEmitHelpers.FirstEmitHelper; - helper <= ExternalEmitHelpers.LastEmitHelper; - helper <<= 1) - { - if (uncheckedHelpers & helper) { - const name = getHelperName(helper); - const symbol = getSymbol( - helpersModule.exports!, - escapeLeadingUnderscores(name), - SymbolFlags.Value - ); - if (!symbol) { - error( - location, - Diagnostics - .This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0, - externalHelpersModuleNameText, - name - ); - } - } - } - } - requestedExternalEmitHelpers |= helpers; - } - } - } - - function getHelperName(helper: ExternalEmitHelpers) { - switch (helper) { - case ExternalEmitHelpers.Extends: - return '__extends'; - case ExternalEmitHelpers.Assign: - return '__assign'; - case ExternalEmitHelpers.Rest: - return '__rest'; - case ExternalEmitHelpers.Decorate: - return '__decorate'; - case ExternalEmitHelpers.Metadata: - return '__metadata'; - case ExternalEmitHelpers.Param: - return '__param'; - case ExternalEmitHelpers.Awaiter: - return '__awaiter'; - case ExternalEmitHelpers.Generator: - return '__generator'; - case ExternalEmitHelpers.Values: - return '__values'; - case ExternalEmitHelpers.Read: - return '__read'; - case ExternalEmitHelpers.Spread: - return '__spread'; - case ExternalEmitHelpers.SpreadArrays: - return '__spreadArrays'; - case ExternalEmitHelpers.Await: - return '__await'; - case ExternalEmitHelpers.AsyncGenerator: - return '__asyncGenerator'; - case ExternalEmitHelpers.AsyncDelegator: - return '__asyncDelegator'; - case ExternalEmitHelpers.AsyncValues: - return '__asyncValues'; - case ExternalEmitHelpers.ExportStar: - return '__exportStar'; - case ExternalEmitHelpers.MakeTemplateObject: - return '__makeTemplateObject'; - case ExternalEmitHelpers.ClassPrivateFieldGet: - return '__classPrivateFieldGet'; - case ExternalEmitHelpers.ClassPrivateFieldSet: - return '__classPrivateFieldSet'; - default: - return Debug.fail('Unrecognized helper'); - } - } - - function resolveHelpersModule(node: SourceFile, errorNode: Node) { - if (!externalHelpersModule) { - externalHelpersModule = resolveExternalModule( - node, - externalHelpersModuleNameText, - Diagnostics - .This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, - errorNode - ) || unknownSymbol; - } - return externalHelpersModule; - } - - // GRAMMAR CHECKING - function checkGrammarDecoratorsAndModifiers(node: Node): boolean { - return checkGrammarDecorators(node) || checkGrammarModifiers(node); - } - - function checkGrammarDecorators(node: Node): boolean { - if (!node.decorators) { - return false; - } - if (!nodeCanBeDecorated(node, node.parent, node.parent.parent)) { - if (node.kind === SyntaxKind.MethodDeclaration - && !nodeIsPresent(( node).body)) - { - return grammarErrorOnFirstToken( - node, - Diagnostics - .A_decorator_can_only_decorate_a_method_implementation_not_an_overload - ); - } else { - return grammarErrorOnFirstToken( - node, - Diagnostics.Decorators_are_not_valid_here - ); - } - } else if (node.kind === SyntaxKind.GetAccessor - || node.kind === SyntaxKind.SetAccessor) - { - const accessors = getAllAccessorDeclarations( - ( node.parent).members, - node - ); - if (accessors.firstAccessor.decorators - && node === accessors.secondAccessor) - { - return grammarErrorOnFirstToken( - node, - Diagnostics - .Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name - ); - } - } - return false; - } - - function checkGrammarModifiers(node: Node): boolean { - const quickResult = reportObviousModifierErrors(node); - if (quickResult !== undefined) { - return quickResult; - } - - let lastStatic: Node | undefined, lastDeclare: Node | undefined, - lastAsync: Node | undefined, lastReadonly: Node | undefined; - let flags = ModifierFlags.None; - for (const modifier of node.modifiers!) { - if (modifier.kind !== SyntaxKind.ReadonlyKeyword) { - if (node.kind === SyntaxKind.PropertySignature - || node.kind === SyntaxKind.MethodSignature) - { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_type_member, - tokenToString(modifier.kind) - ); - } - if (node.kind === SyntaxKind.IndexSignature) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_an_index_signature, - tokenToString(modifier.kind) - ); - } - } - switch (modifier.kind) { - case SyntaxKind.ConstKeyword: - if (node.kind !== SyntaxKind.EnumDeclaration) { - return grammarErrorOnNode( - node, - Diagnostics - .A_class_member_cannot_have_the_0_keyword, - tokenToString(SyntaxKind.ConstKeyword) - ); - } - break; - case SyntaxKind.PublicKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.PrivateKeyword: - const text = - visibilityToString(modifierToFlag(modifier.kind)); - - if (flags & ModifierFlags.AccessibilityModifier) { - return grammarErrorOnNode( - modifier, - Diagnostics.Accessibility_modifier_already_seen - ); - } else if (flags & ModifierFlags.Static) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - text, - 'static' - ); - } else if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - text, - 'readonly' - ); - } else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - text, - 'async' - ); - } else if (node.parent.kind === SyntaxKind.ModuleBlock - || node.parent.kind === SyntaxKind.SourceFile) - { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_module_or_namespace_element, - text - ); - } else if (flags & ModifierFlags.Abstract) { - if (modifier.kind === SyntaxKind.PrivateKeyword) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_with_1_modifier, - text, - 'abstract' - ); - } else { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - text, - 'abstract' - ); - } - } - flags |= modifierToFlag(modifier.kind); - break; - - case SyntaxKind.StaticKeyword: - if (flags & ModifierFlags.Static) { - return grammarErrorOnNode( - modifier, - Diagnostics._0_modifier_already_seen, - 'static' - ); - } else if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - 'static', - 'readonly' - ); - } else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - 'static', - 'async' - ); - } else if (node.parent.kind === SyntaxKind.ModuleBlock - || node.parent.kind === SyntaxKind.SourceFile) - { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_module_or_namespace_element, - 'static' - ); - } else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_parameter, - 'static' - ); - } else if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_with_1_modifier, - 'static', - 'abstract' - ); - } else if (isPrivateIdentifierPropertyDeclaration(node)) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_with_a_private_identifier, - 'static' - ); - } - flags |= ModifierFlags.Static; - lastStatic = modifier; - break; - - case SyntaxKind.ReadonlyKeyword: - if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode( - modifier, - Diagnostics._0_modifier_already_seen, - 'readonly' - ); - } else if (node.kind !== SyntaxKind.PropertyDeclaration - && node.kind !== SyntaxKind.PropertySignature - && node.kind !== SyntaxKind.IndexSignature - && node.kind !== SyntaxKind.Parameter) - { - // If node.kind === SyntaxKind.Parameter, checkParameter report an error if it's not a parameter property. - return grammarErrorOnNode( - modifier, - Diagnostics - .readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature - ); - } - flags |= ModifierFlags.Readonly; - lastReadonly = modifier; - break; - - case SyntaxKind.ExportKeyword: - if (flags & ModifierFlags.Export) { - return grammarErrorOnNode( - modifier, - Diagnostics._0_modifier_already_seen, - 'export' - ); - } else if (flags & ModifierFlags.Ambient) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - 'export', - 'declare' - ); - } else if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - 'export', - 'abstract' - ); - } else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_must_precede_1_modifier, - 'export', - 'async' - ); - } else if (isClassLike(node.parent)) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_class_element, - 'export' - ); - } else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_parameter, - 'export' - ); - } - flags |= ModifierFlags.Export; - break; - case SyntaxKind.DefaultKeyword: - const container = - node.parent.kind === SyntaxKind.SourceFile - ? node.parent - : node.parent.parent; - if (container.kind === SyntaxKind.ModuleDeclaration - && !isAmbientModule(container)) - { - return grammarErrorOnNode( - modifier, - Diagnostics - .A_default_export_can_only_be_used_in_an_ECMAScript_style_module - ); - } - - flags |= ModifierFlags.Default; - break; - case SyntaxKind.DeclareKeyword: - if (flags & ModifierFlags.Ambient) { - return grammarErrorOnNode( - modifier, - Diagnostics._0_modifier_already_seen, - 'declare' - ); - } else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_in_an_ambient_context, - 'async' - ); - } else if (isClassLike(node.parent) - && !isPropertyDeclaration(node)) - { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_class_element, - 'declare' - ); - } else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_parameter, - 'declare' - ); - } else if ((node.parent.flags & NodeFlags.Ambient) - && node.parent.kind === SyntaxKind.ModuleBlock) - { - return grammarErrorOnNode( - modifier, - Diagnostics - .A_declare_modifier_cannot_be_used_in_an_already_ambient_context - ); - } - flags |= ModifierFlags.Ambient; - lastDeclare = modifier; - break; - - case SyntaxKind.AbstractKeyword: - if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode( - modifier, - Diagnostics._0_modifier_already_seen, - 'abstract' - ); - } - if (node.kind !== SyntaxKind.ClassDeclaration) { - if (node.kind !== SyntaxKind.MethodDeclaration - && node.kind !== SyntaxKind.PropertyDeclaration - && node.kind !== SyntaxKind.GetAccessor - && node.kind !== SyntaxKind.SetAccessor) - { - return grammarErrorOnNode( - modifier, - Diagnostics - .abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration - ); - } - if (!(node.parent.kind - === SyntaxKind.ClassDeclaration - && hasModifier( - node.parent, - ModifierFlags.Abstract - ))) - { - return grammarErrorOnNode( - modifier, - Diagnostics - .Abstract_methods_can_only_appear_within_an_abstract_class - ); - } - if (flags & ModifierFlags.Static) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_with_1_modifier, - 'static', - 'abstract' - ); - } - if (flags & ModifierFlags.Private) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_with_1_modifier, - 'private', - 'abstract' - ); - } - } - if (isNamedDeclaration(node) - && node.name.kind === SyntaxKind.PrivateIdentifier) - { - return grammarErrorOnNode( - node, - Diagnostics - ._0_modifier_cannot_be_used_with_a_private_identifier, - 'abstract' - ); - } - - flags |= ModifierFlags.Abstract; - break; - - case SyntaxKind.AsyncKeyword: - if (flags & ModifierFlags.Async) { - return grammarErrorOnNode( - modifier, - Diagnostics._0_modifier_already_seen, - 'async' - ); - } else if (flags & ModifierFlags.Ambient - || node.parent.flags & NodeFlags.Ambient) - { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_be_used_in_an_ambient_context, - 'async' - ); - } else if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode( - modifier, - Diagnostics - ._0_modifier_cannot_appear_on_a_parameter, - 'async' - ); - } - flags |= ModifierFlags.Async; - lastAsync = modifier; - break; - } - } - - if (node.kind === SyntaxKind.Constructor) { - if (flags & ModifierFlags.Static) { - return grammarErrorOnNode( - lastStatic!, - Diagnostics - ._0_modifier_cannot_appear_on_a_constructor_declaration, - 'static' - ); - } - if (flags & ModifierFlags.Abstract) { - return grammarErrorOnNode( - lastStatic!, - Diagnostics - ._0_modifier_cannot_appear_on_a_constructor_declaration, - 'abstract' - ); // TODO: GH#18217 - } else if (flags & ModifierFlags.Async) { - return grammarErrorOnNode( - lastAsync!, - Diagnostics - ._0_modifier_cannot_appear_on_a_constructor_declaration, - 'async' - ); - } else if (flags & ModifierFlags.Readonly) { - return grammarErrorOnNode( - lastReadonly!, - Diagnostics - ._0_modifier_cannot_appear_on_a_constructor_declaration, - 'readonly' - ); - } - return false; - } else if ((node.kind === SyntaxKind.ImportDeclaration - || node.kind === SyntaxKind.ImportEqualsDeclaration) - && flags & ModifierFlags.Ambient) - { - return grammarErrorOnNode( - lastDeclare!, - Diagnostics - .A_0_modifier_cannot_be_used_with_an_import_declaration, - 'declare' - ); - } else if (node.kind === SyntaxKind.Parameter - && (flags & ModifierFlags.ParameterPropertyModifier) - && isBindingPattern(( node).name)) - { - return grammarErrorOnNode( - node, - Diagnostics - .A_parameter_property_may_not_be_declared_using_a_binding_pattern - ); - } else if (node.kind === SyntaxKind.Parameter - && (flags & ModifierFlags.ParameterPropertyModifier) - && ( node).dotDotDotToken) - { - return grammarErrorOnNode( - node, - Diagnostics - .A_parameter_property_cannot_be_declared_using_a_rest_parameter - ); - } else if (isNamedDeclaration(node) - && (flags & ModifierFlags.AccessibilityModifier) - && node.name.kind === SyntaxKind.PrivateIdentifier) - { - return grammarErrorOnNode( - node, - Diagnostics - .An_accessibility_modifier_cannot_be_used_with_a_private_identifier - ); - } - if (flags & ModifierFlags.Async) { - return checkGrammarAsyncModifier(node, lastAsync!); - } - return false; - } - - /** - * true | false: Early return this value from checkGrammarModifiers. - * undefined: Need to do full checking on the modifiers. - */ - function reportObviousModifierErrors(node: Node): boolean | undefined { - return !node.modifiers - ? false - : shouldReportBadModifier(node) - ? grammarErrorOnFirstToken( - node, - Diagnostics.Modifiers_cannot_appear_here - ) - : undefined; - } - function shouldReportBadModifier(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.Constructor: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportDeclaration: - case SyntaxKind.ExportAssignment: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.Parameter: - return false; - default: - if (node.parent.kind === SyntaxKind.ModuleBlock - || node.parent.kind === SyntaxKind.SourceFile) - { - return false; - } - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - return nodeHasAnyModifiersExcept( - node, - SyntaxKind.AsyncKeyword - ); - case SyntaxKind.ClassDeclaration: - return nodeHasAnyModifiersExcept( - node, - SyntaxKind.AbstractKeyword - ); - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.VariableStatement: - case SyntaxKind.TypeAliasDeclaration: - return true; - case SyntaxKind.EnumDeclaration: - return nodeHasAnyModifiersExcept( - node, - SyntaxKind.ConstKeyword - ); - default: - Debug.fail(); - return false; - } - } - } - function nodeHasAnyModifiersExcept( - node: Node, - allowedModifier: SyntaxKind - ): boolean { - return node.modifiers!.length > 1 - || node.modifiers![0].kind !== allowedModifier; - } - - function checkGrammarAsyncModifier( - node: Node, - asyncModifier: Node - ): boolean { - switch (node.kind) { - case SyntaxKind.MethodDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return false; - } - - return grammarErrorOnNode( - asyncModifier, - Diagnostics._0_modifier_cannot_be_used_here, - 'async' - ); - } - - function checkGrammarForDisallowedTrailingComma( - list: NodeArray | undefined, - diag = Diagnostics.Trailing_comma_not_allowed - ): boolean { - if (list && list.hasTrailingComma) { - return grammarErrorAtPos( - list[0], - list.end - ','.length, - ','.length, - diag - ); - } - return false; - } - - function checkGrammarTypeParameterList( - typeParameters: NodeArray | undefined, - file: SourceFile - ): boolean { - if (typeParameters && typeParameters.length === 0) { - const start = typeParameters.pos - '<'.length; - const end = skipTrivia(file.text, typeParameters.end) - + '>'.length; - return grammarErrorAtPos( - file, - start, - end - start, - Diagnostics.Type_parameter_list_cannot_be_empty - ); - } - return false; - } - - function checkGrammarParameterList(parameters: - NodeArray) - { - let seenOptionalParameter = false; - const parameterCount = parameters.length; - - for (let i = 0; i < parameterCount; i++) { - const parameter = parameters[i]; - if (parameter.dotDotDotToken) { - if (i !== (parameterCount - 1)) { - return grammarErrorOnNode( - parameter.dotDotDotToken, - Diagnostics - .A_rest_parameter_must_be_last_in_a_parameter_list - ); - } - if (!(parameter.flags - & NodeFlags - .Ambient)) - { // Allow `...foo,` in ambient declarations; see GH#23070 - checkGrammarForDisallowedTrailingComma( - parameters, - Diagnostics - .A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma - ); - } - - if (parameter.questionToken) { - return grammarErrorOnNode( - parameter.questionToken, - Diagnostics.A_rest_parameter_cannot_be_optional - ); - } - - if (parameter.initializer) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .A_rest_parameter_cannot_have_an_initializer - ); - } - } else if (parameter.questionToken) { - seenOptionalParameter = true; - - if (parameter.initializer) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .Parameter_cannot_have_question_mark_and_initializer - ); - } - } else if (seenOptionalParameter && !parameter.initializer) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .A_required_parameter_cannot_follow_an_optional_parameter - ); - } - } - } - - function getNonSimpleParameters(parameters: - readonly ParameterDeclaration[]): readonly ParameterDeclaration[] - { - return filter( - parameters, - parameter => !!parameter.initializer - || isBindingPattern(parameter.name) - || isRestParameter(parameter) - ); - } - - function checkGrammarForUseStrictSimpleParameterList(node: - FunctionLikeDeclaration): boolean - { - if (languageVersion >= ScriptTarget.ES2016) { - const useStrictDirective = node.body && isBlock(node.body) - && findUseStrictPrologue(node.body.statements); - if (useStrictDirective) { - const nonSimpleParameters = - getNonSimpleParameters(node.parameters); - if (length(nonSimpleParameters)) { - forEach(nonSimpleParameters, parameter => { - addRelatedInfo( - error( - parameter, - Diagnostics - .This_parameter_is_not_allowed_with_use_strict_directive - ), - createDiagnosticForNode( - useStrictDirective, - Diagnostics.use_strict_directive_used_here - ) - ); - }); - - const diagnostics = nonSimpleParameters - .map((parameter, index) => ( - index === 0 - ? createDiagnosticForNode( - parameter, - Diagnostics - .Non_simple_parameter_declared_here - ) - : createDiagnosticForNode( - parameter, - Diagnostics.and_here - ) - )) as [DiagnosticWithLocation, - ...DiagnosticWithLocation[]]; - addRelatedInfo( - error( - useStrictDirective, - Diagnostics - .use_strict_directive_cannot_be_used_with_non_simple_parameter_list - ), - ...diagnostics - ); - return true; - } - } - } - return false; - } - - function checkGrammarFunctionLikeDeclaration(node: - FunctionLikeDeclaration | MethodSignature): boolean - { - // Prevent cascading error by short-circuit - const file = getSourceFileOfNode(node); - return checkGrammarDecoratorsAndModifiers(node) - || checkGrammarTypeParameterList(node.typeParameters, file) - || checkGrammarParameterList(node.parameters) - || checkGrammarArrowFunction(node, file) - || (isFunctionLikeDeclaration(node) - && checkGrammarForUseStrictSimpleParameterList(node)); - } - - function checkGrammarClassLikeDeclaration(node: - ClassLikeDeclaration): boolean - { - const file = getSourceFileOfNode(node); - return checkGrammarClassDeclarationHeritageClauses(node) - || checkGrammarTypeParameterList(node.typeParameters, file); - } - - function checkGrammarArrowFunction( - node: Node, - file: SourceFile - ): boolean { - if (!isArrowFunction(node)) { - return false; - } - - const { equalsGreaterThanToken } = node; - const startLine = getLineAndCharacterOfPosition( - file, - equalsGreaterThanToken.pos - ).line; - const endLine = getLineAndCharacterOfPosition( - file, - equalsGreaterThanToken.end - ).line; - return startLine !== endLine - && grammarErrorOnNode( - equalsGreaterThanToken, - Diagnostics.Line_terminator_not_permitted_before_arrow - ); - } - - function checkGrammarIndexSignatureParameters(node: - SignatureDeclaration): boolean - { - const parameter = node.parameters[0]; - if (node.parameters.length !== 1) { - if (parameter) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_must_have_exactly_one_parameter - ); - } else { - return grammarErrorOnNode( - node, - Diagnostics - .An_index_signature_must_have_exactly_one_parameter - ); - } - } - if (parameter.dotDotDotToken) { - return grammarErrorOnNode( - parameter.dotDotDotToken, - Diagnostics.An_index_signature_cannot_have_a_rest_parameter - ); - } - if (hasModifiers(parameter)) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_parameter_cannot_have_an_accessibility_modifier - ); - } - if (parameter.questionToken) { - return grammarErrorOnNode( - parameter.questionToken, - Diagnostics - .An_index_signature_parameter_cannot_have_a_question_mark - ); - } - if (parameter.initializer) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_parameter_cannot_have_an_initializer - ); - } - if (!parameter.type) { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_parameter_must_have_a_type_annotation - ); - } - if (parameter.type.kind !== SyntaxKind.StringKeyword - && parameter.type.kind !== SyntaxKind.NumberKeyword) - { - const type = getTypeFromTypeNode(parameter.type); - - if (type.flags & TypeFlags.String - || type.flags & TypeFlags.Number) - { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_parameter_type_cannot_be_a_type_alias_Consider_writing_0_Colon_1_Colon_2_instead, - getTextOfNode(parameter.name), - typeToString(type), - typeToString(node.type - ? getTypeFromTypeNode(node.type) - : anyType) - ); - } - - if (type.flags & TypeFlags.Union - && allTypesAssignableToKind( - type, - TypeFlags.StringOrNumberLiteral, /*strict*/ - true - )) - { - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_parameter_type_cannot_be_a_union_type_Consider_using_a_mapped_object_type_instead - ); - } - - return grammarErrorOnNode( - parameter.name, - Diagnostics - .An_index_signature_parameter_type_must_be_either_string_or_number - ); - } - if (!node.type) { - return grammarErrorOnNode( - node, - Diagnostics.An_index_signature_must_have_a_type_annotation - ); - } - return false; - } - - function checkGrammarIndexSignature(node: SignatureDeclaration) { - // Prevent cascading error by short-circuit - return checkGrammarDecoratorsAndModifiers(node) - || checkGrammarIndexSignatureParameters(node); - } - - function checkGrammarForAtLeastOneTypeArgument( - node: Node, - typeArguments: NodeArray | undefined - ): boolean { - if (typeArguments && typeArguments.length === 0) { - const sourceFile = getSourceFileOfNode(node); - const start = typeArguments.pos - '<'.length; - const end = skipTrivia(sourceFile.text, typeArguments.end) - + '>'.length; - return grammarErrorAtPos( - sourceFile, - start, - end - start, - Diagnostics.Type_argument_list_cannot_be_empty - ); - } - return false; - } - - function checkGrammarTypeArguments( - node: Node, - typeArguments: NodeArray | undefined - ): boolean { - return checkGrammarForDisallowedTrailingComma(typeArguments) - || checkGrammarForAtLeastOneTypeArgument(node, typeArguments); - } - - function checkGrammarTaggedTemplateChain(node: - TaggedTemplateExpression): boolean - { - if (node.questionDotToken - || node.flags & NodeFlags.OptionalChain) - { - return grammarErrorOnNode( - node.template, - Diagnostics - .Tagged_template_expressions_are_not_permitted_in_an_optional_chain - ); - } - return false; - } - - function checkGrammarForOmittedArgument(args: NodeArray - | undefined): boolean - { - if (args) { - for (const arg of args) { - if (arg.kind === SyntaxKind.OmittedExpression) { - return grammarErrorAtPos( - arg, - arg.pos, - 0, - Diagnostics.Argument_expression_expected - ); - } - } - } - return false; - } - - function checkGrammarArguments(args: NodeArray - | undefined): boolean - { - return checkGrammarForOmittedArgument(args); - } - - function checkGrammarHeritageClause(node: HeritageClause): boolean { - const types = node.types; - if (checkGrammarForDisallowedTrailingComma(types)) { - return true; - } - if (types && types.length === 0) { - const listType = tokenToString(node.token); - return grammarErrorAtPos( - node, - types.pos, - 0, - Diagnostics._0_list_cannot_be_empty, - listType - ); - } - return some(types, checkGrammarExpressionWithTypeArguments); - } - - function checkGrammarExpressionWithTypeArguments(node: - ExpressionWithTypeArguments) - { - return checkGrammarTypeArguments(node, node.typeArguments); - } - - function checkGrammarClassDeclarationHeritageClauses(node: - ClassLikeDeclaration) - { - let seenExtendsClause = false; - let seenImplementsClause = false; - - if (!checkGrammarDecoratorsAndModifiers(node) - && node.heritageClauses) - { - for (const heritageClause of node.heritageClauses) { - if (heritageClause.token === SyntaxKind.ExtendsKeyword) { - if (seenExtendsClause) { - return grammarErrorOnFirstToken( - heritageClause, - Diagnostics.extends_clause_already_seen - ); - } - - if (seenImplementsClause) { - return grammarErrorOnFirstToken( - heritageClause, - Diagnostics - .extends_clause_must_precede_implements_clause - ); - } - - if (heritageClause.types.length > 1) { - return grammarErrorOnFirstToken( - heritageClause.types[1], - Diagnostics - .Classes_can_only_extend_a_single_class - ); - } - - seenExtendsClause = true; - } else { - Debug - .assert(heritageClause.token - === SyntaxKind.ImplementsKeyword); - if (seenImplementsClause) { - return grammarErrorOnFirstToken( - heritageClause, - Diagnostics.implements_clause_already_seen - ); - } - - seenImplementsClause = true; - } - - // Grammar checking heritageClause inside class declaration - checkGrammarHeritageClause(heritageClause); - } - } - } - - function checkGrammarInterfaceDeclaration(node: InterfaceDeclaration) { - let seenExtendsClause = false; - - if (node.heritageClauses) { - for (const heritageClause of node.heritageClauses) { - if (heritageClause.token === SyntaxKind.ExtendsKeyword) { - if (seenExtendsClause) { - return grammarErrorOnFirstToken( - heritageClause, - Diagnostics.extends_clause_already_seen - ); - } - - seenExtendsClause = true; - } else { - Debug - .assert(heritageClause.token - === SyntaxKind.ImplementsKeyword); - return grammarErrorOnFirstToken( - heritageClause, - Diagnostics - .Interface_declaration_cannot_have_implements_clause - ); - } - - // Grammar checking heritageClause inside class declaration - checkGrammarHeritageClause(heritageClause); - } - } - return false; - } - - function checkGrammarComputedPropertyName(node: Node): boolean { - // If node is not a computedPropertyName, just skip the grammar checking - if (node.kind !== SyntaxKind.ComputedPropertyName) { - return false; - } - - const computedPropertyName = node; - if (computedPropertyName.expression.kind - === SyntaxKind.BinaryExpression - && ( computedPropertyName.expression) - .operatorToken.kind === SyntaxKind.CommaToken) - { - return grammarErrorOnNode( - computedPropertyName.expression, - Diagnostics - .A_comma_expression_is_not_allowed_in_a_computed_property_name - ); - } - return false; - } - - function checkGrammarForGenerator(node: FunctionLikeDeclaration) { - if (node.asteriskToken) { - Debug.assert( - node.kind === SyntaxKind.FunctionDeclaration - || node.kind === SyntaxKind.FunctionExpression - || node.kind === SyntaxKind.MethodDeclaration - ); - if (node.flags & NodeFlags.Ambient) { - return grammarErrorOnNode( - node.asteriskToken!, - Diagnostics - .Generators_are_not_allowed_in_an_ambient_context - ); - } - if (!node.body) { - return grammarErrorOnNode( - node.asteriskToken!, - Diagnostics - .An_overload_signature_cannot_be_declared_as_a_generator - ); - } - } - } - - function checkGrammarForInvalidQuestionMark( - questionToken: QuestionToken | undefined, - message: DiagnosticMessage - ): boolean { - return !!questionToken - && grammarErrorOnNode(questionToken, message); - } - - function checkGrammarForInvalidExclamationToken( - exclamationToken: ExclamationToken | undefined, - message: DiagnosticMessage - ): boolean { - return !!exclamationToken - && grammarErrorOnNode(exclamationToken, message); - } - - function checkGrammarObjectLiteralExpression( - node: ObjectLiteralExpression, - inDestructuring: boolean - ) { - const seen = createUnderscoreEscapedMap(); - - for (const prop of node.properties) { - if (prop.kind === SyntaxKind.SpreadAssignment) { - if (inDestructuring) { - // a rest property cannot be destructured any further - const expression = skipParentheses(prop.expression); - if (isArrayLiteralExpression(expression) - || isObjectLiteralExpression(expression)) - { - return grammarErrorOnNode( - prop.expression, - Diagnostics - .A_rest_element_cannot_contain_a_binding_pattern - ); - } - } - continue; - } - const name = prop.name; - if (name.kind === SyntaxKind.ComputedPropertyName) { - // If the name is not a ComputedPropertyName, the grammar checking will skip it - checkGrammarComputedPropertyName(name); - } - - if (prop.kind === SyntaxKind.ShorthandPropertyAssignment - && !inDestructuring && prop.objectAssignmentInitializer) - { - // having objectAssignmentInitializer is only valid in ObjectAssignmentPattern - // outside of destructuring it is a syntax error - return grammarErrorOnNode( - prop.equalsToken!, - Diagnostics - .can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment - ); - } - - if (name.kind === SyntaxKind.PrivateIdentifier) { - return grammarErrorOnNode( - name, - Diagnostics - .Private_identifiers_are_not_allowed_outside_class_bodies - ); - } - - // Modifiers are never allowed on properties except for 'async' on a method declaration - if (prop.modifiers) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - for (const mod of prop.modifiers!) { // TODO: GH#19955 - if (mod.kind !== SyntaxKind.AsyncKeyword - || prop.kind !== SyntaxKind.MethodDeclaration) - { - grammarErrorOnNode( - mod, - Diagnostics._0_modifier_cannot_be_used_here, - getTextOfNode(mod) - ); - } - } - } - - // ECMA-262 11.1.5 Object Initializer - // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true - // a.This production is contained in strict code and IsDataDescriptor(previous) is true and - // IsDataDescriptor(propId.descriptor) is true. - // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true. - // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true. - // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true - // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields - let currentKind: DeclarationMeaning; - switch (prop.kind) { - case SyntaxKind.ShorthandPropertyAssignment: - checkGrammarForInvalidExclamationToken( - prop.exclamationToken, - Diagnostics - .A_definite_assignment_assertion_is_not_permitted_in_this_context - ); - // falls through - case SyntaxKind.PropertyAssignment: - // Grammar checking for computedPropertyName and shorthandPropertyAssignment - checkGrammarForInvalidQuestionMark( - prop.questionToken, - Diagnostics - .An_object_member_cannot_be_declared_optional - ); - if (name.kind === SyntaxKind.NumericLiteral) { - checkGrammarNumericLiteral(name); - } - currentKind = DeclarationMeaning.PropertyAssignment; - break; - case SyntaxKind.MethodDeclaration: - currentKind = DeclarationMeaning.Method; - break; - case SyntaxKind.GetAccessor: - currentKind = DeclarationMeaning.GetAccessor; - break; - case SyntaxKind.SetAccessor: - currentKind = DeclarationMeaning.SetAccessor; - break; - default: - throw Debug.assertNever( - prop, - 'Unexpected syntax kind:' + ( prop).kind - ); - } - - const effectiveName = getPropertyNameForPropertyNameNode(name); - if (effectiveName === undefined) { - continue; - } - - const existingKind = seen.get(effectiveName); - if (!existingKind) { - seen.set(effectiveName, currentKind); - } else { - if ((currentKind - & DeclarationMeaning.PropertyAssignmentOrMethod) - && (existingKind - & DeclarationMeaning.PropertyAssignmentOrMethod)) - { - grammarErrorOnNode( - name, - Diagnostics.Duplicate_identifier_0, - getTextOfNode(name) - ); - } else if ((currentKind - & DeclarationMeaning.GetOrSetAccessor) - && (existingKind - & DeclarationMeaning.GetOrSetAccessor)) - { - if (existingKind - !== DeclarationMeaning.GetOrSetAccessor - && currentKind !== existingKind) - { - seen.set( - effectiveName, - currentKind | existingKind - ); - } else { - return grammarErrorOnNode( - name, - Diagnostics - .An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name - ); - } - } else { - return grammarErrorOnNode( - name, - Diagnostics - .An_object_literal_cannot_have_property_and_accessor_with_the_same_name - ); - } - } - } - } - - function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - checkGrammarTypeArguments(node, node.typeArguments); - const seen = createUnderscoreEscapedMap(); - - for (const attr of node.attributes.properties) { - if (attr.kind === SyntaxKind.JsxSpreadAttribute) { - continue; - } - - const { name, initializer } = attr; - if (!seen.get(name.escapedText)) { - seen.set(name.escapedText, true); - } else { - return grammarErrorOnNode( - name, - Diagnostics - .JSX_elements_cannot_have_multiple_attributes_with_the_same_name - ); - } - - if (initializer - && initializer.kind === SyntaxKind.JsxExpression - && !initializer.expression) - { - return grammarErrorOnNode( - initializer, - Diagnostics - .JSX_attributes_must_only_be_assigned_a_non_empty_expression - ); - } - } - } - - function checkGrammarJsxExpression(node: JsxExpression) { - if (node.expression && isCommaSequence(node.expression)) { - return grammarErrorOnNode( - node.expression, - Diagnostics - .JSX_expressions_may_not_use_the_comma_operator_Did_you_mean_to_write_an_array - ); - } - } - - function checkGrammarForInOrForOfStatement(forInOrOfStatement: - ForInOrOfStatement): boolean - { - if (checkGrammarStatementInAmbientContext(forInOrOfStatement)) { - return true; - } - - if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement - && forInOrOfStatement.awaitModifier) - { - if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) - === NodeFlags.None) - { - // use of 'for-await-of' in non-async function - const sourceFile = getSourceFileOfNode(forInOrOfStatement); - if (!hasParseDiagnostics(sourceFile)) { - const diagnostic = createDiagnosticForNode( - forInOrOfStatement.awaitModifier, - Diagnostics - .A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator - ); - const func = getContainingFunction(forInOrOfStatement); - if (func && func.kind !== SyntaxKind.Constructor) { - Debug.assert( - (getFunctionFlags(func) & FunctionFlags.Async) - === 0, - 'Enclosing function should never be an async function.' - ); - const relatedInfo = createDiagnosticForNode( - func, - Diagnostics - .Did_you_mean_to_mark_this_function_as_async - ); - addRelatedInfo(diagnostic, relatedInfo); - } - diagnostics.add(diagnostic); - return true; - } - return false; - } - } - - if (forInOrOfStatement.initializer.kind - === SyntaxKind.VariableDeclarationList) - { - const variableList = - forInOrOfStatement.initializer; - if (!checkGrammarVariableDeclarationList(variableList)) { - const declarations = variableList.declarations; - - // declarations.length can be zero if there is an error in variable declaration in for-of or for-in - // See http://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements for details - // For example: - // var let = 10; - // for (let of [1,2,3]) {} // this is invalid ES6 syntax - // for (let in [1,2,3]) {} // this is invalid ES6 syntax - // We will then want to skip on grammar checking on variableList declaration - if (!declarations.length) { - return false; - } - - if (declarations.length > 1) { - const diagnostic = - forInOrOfStatement.kind - === SyntaxKind.ForInStatement - ? Diagnostics - .Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement - : Diagnostics - .Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement; - return grammarErrorOnFirstToken( - variableList.declarations[1], - diagnostic - ); - } - const firstDeclaration = declarations[0]; - - if (firstDeclaration.initializer) { - const diagnostic = - forInOrOfStatement.kind - === SyntaxKind.ForInStatement - ? Diagnostics - .The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer - : Diagnostics - .The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer; - return grammarErrorOnNode( - firstDeclaration.name, - diagnostic - ); - } - if (firstDeclaration.type) { - const diagnostic = - forInOrOfStatement.kind - === SyntaxKind.ForInStatement - ? Diagnostics - .The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation - : Diagnostics - .The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation; - return grammarErrorOnNode( - firstDeclaration, - diagnostic - ); - } - } - } - - return false; - } - - function checkGrammarAccessor(accessor: AccessorDeclaration): boolean { - if (!(accessor.flags & NodeFlags.Ambient)) { - if (languageVersion < ScriptTarget.ES5) { - return grammarErrorOnNode( - accessor.name, - Diagnostics - .Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher - ); - } - if (accessor.body === undefined - && !hasModifier(accessor, ModifierFlags.Abstract)) - { - return grammarErrorAtPos( - accessor, - accessor.end - 1, - ';'.length, - Diagnostics._0_expected, - '{' - ); - } - } - if (accessor.body - && hasModifier(accessor, ModifierFlags.Abstract)) - { - return grammarErrorOnNode( - accessor, - Diagnostics - .An_abstract_accessor_cannot_have_an_implementation - ); - } - if (accessor.typeParameters) { - return grammarErrorOnNode( - accessor.name, - Diagnostics.An_accessor_cannot_have_type_parameters - ); - } - if (!doesAccessorHaveCorrectParameterCount(accessor)) { - return grammarErrorOnNode( - accessor.name, - accessor.kind === SyntaxKind.GetAccessor - ? Diagnostics.A_get_accessor_cannot_have_parameters - : Diagnostics - .A_set_accessor_must_have_exactly_one_parameter - ); - } - if (accessor.kind === SyntaxKind.SetAccessor) { - if (accessor.type) { - return grammarErrorOnNode( - accessor.name, - Diagnostics - .A_set_accessor_cannot_have_a_return_type_annotation - ); - } - const parameter = Debug.assertDefined( - getSetAccessorValueParameter(accessor), - 'Return value does not match parameter count assertion.' - ); - if (parameter.dotDotDotToken) { - return grammarErrorOnNode( - parameter.dotDotDotToken, - Diagnostics.A_set_accessor_cannot_have_rest_parameter - ); - } - if (parameter.questionToken) { - return grammarErrorOnNode( - parameter.questionToken, - Diagnostics - .A_set_accessor_cannot_have_an_optional_parameter - ); - } - if (parameter.initializer) { - return grammarErrorOnNode( - accessor.name, - Diagnostics - .A_set_accessor_parameter_cannot_have_an_initializer - ); - } - } - return false; - } - - /** Does the accessor have the right number of parameters? - * A get accessor has no parameters or a single `this` parameter. - * A set accessor has one parameter or a `this` parameter and one more parameter. - */ - function doesAccessorHaveCorrectParameterCount(accessor: - AccessorDeclaration) - { - return getAccessorThisParameter(accessor) - || accessor.parameters.length - === (accessor.kind === SyntaxKind.GetAccessor ? 0 : 1); - } - - function getAccessorThisParameter(accessor: - AccessorDeclaration): ParameterDeclaration | undefined - { - if (accessor.parameters.length - === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2)) - { - return getThisParameter(accessor); - } - } - - function checkGrammarTypeOperatorNode(node: TypeOperatorNode) { - if (node.operator === SyntaxKind.UniqueKeyword) { - if (node.type.kind !== SyntaxKind.SymbolKeyword) { - return grammarErrorOnNode( - node.type, - Diagnostics._0_expected, - tokenToString(SyntaxKind.SymbolKeyword) - ); - } - - const parent = walkUpParenthesizedTypes(node.parent); - switch (parent.kind) { - case SyntaxKind.VariableDeclaration: - const decl = parent as VariableDeclaration; - if (decl.name.kind !== SyntaxKind.Identifier) { - return grammarErrorOnNode( - node, - Diagnostics - .unique_symbol_types_may_not_be_used_on_a_variable_declaration_with_a_binding_name - ); - } - if (!isVariableDeclarationInVariableStatement(decl)) { - return grammarErrorOnNode( - node, - Diagnostics - .unique_symbol_types_are_only_allowed_on_variables_in_a_variable_statement - ); - } - if (!(decl.parent.flags & NodeFlags.Const)) { - return grammarErrorOnNode( - ( parent).name, - Diagnostics - .A_variable_whose_type_is_a_unique_symbol_type_must_be_const - ); - } - break; - - case SyntaxKind.PropertyDeclaration: - if (!hasModifier(parent, ModifierFlags.Static) - || !hasModifier(parent, ModifierFlags.Readonly)) - { - return grammarErrorOnNode( - ( parent).name, - Diagnostics - .A_property_of_a_class_whose_type_is_a_unique_symbol_type_must_be_both_static_and_readonly - ); - } - break; - - case SyntaxKind.PropertySignature: - if (!hasModifier(parent, ModifierFlags.Readonly)) { - return grammarErrorOnNode( - ( parent).name, - Diagnostics - .A_property_of_an_interface_or_type_literal_whose_type_is_a_unique_symbol_type_must_be_readonly - ); - } - break; - - default: - return grammarErrorOnNode( - node, - Diagnostics - .unique_symbol_types_are_not_allowed_here - ); - } - } else if (node.operator === SyntaxKind.ReadonlyKeyword) { - if (node.type.kind !== SyntaxKind.ArrayType - && node.type.kind !== SyntaxKind.TupleType) - { - return grammarErrorOnFirstToken( - node, - Diagnostics - .readonly_type_modifier_is_only_permitted_on_array_and_tuple_literal_types, - tokenToString(SyntaxKind.SymbolKeyword) - ); - } - } - } - - function checkGrammarForInvalidDynamicName( - node: DeclarationName, - message: DiagnosticMessage - ) { - if (isNonBindableDynamicName(node)) { - return grammarErrorOnNode(node, message); - } - } - - function checkGrammarMethod(node: MethodDeclaration - | MethodSignature) - { - if (checkGrammarFunctionLikeDeclaration(node)) { - return true; - } - - if (node.kind === SyntaxKind.MethodDeclaration) { - if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) { - // We only disallow modifier on a method declaration if it is a property of object-literal-expression - if (node.modifiers - && !(node.modifiers.length === 1 - && first(node.modifiers).kind - === SyntaxKind.AsyncKeyword)) - { - return grammarErrorOnFirstToken( - node, - Diagnostics.Modifiers_cannot_appear_here - ); - } else if (checkGrammarForInvalidQuestionMark( - node.questionToken, - Diagnostics - .An_object_member_cannot_be_declared_optional - )) { - return true; - } else if (checkGrammarForInvalidExclamationToken( - node.exclamationToken, - Diagnostics - .A_definite_assignment_assertion_is_not_permitted_in_this_context - )) { - return true; - } else if (node.body === undefined) { - return grammarErrorAtPos( - node, - node.end - 1, - ';'.length, - Diagnostics._0_expected, - '{' - ); - } - } - if (checkGrammarForGenerator(node)) { - return true; - } - } - - if (isClassLike(node.parent)) { - // Technically, computed properties in ambient contexts is disallowed - // for property declarations and accessors too, not just methods. - // However, property declarations disallow computed names in general, - // and accessors are not allowed in ambient contexts in general, - // so this error only really matters for methods. - if (node.flags & NodeFlags.Ambient) { - return checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_an_ambient_context_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - ); - } else if (node.kind === SyntaxKind.MethodDeclaration - && !node.body) - { - return checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_a_method_overload_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - ); - } - } else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) { - return checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - ); - } else if (node.parent.kind === SyntaxKind.TypeLiteral) { - return checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - ); - } - } - - function checkGrammarBreakOrContinueStatement(node: - BreakOrContinueStatement): boolean - { - let current: Node = node; - while (current) { - if (isFunctionLike(current)) { - return grammarErrorOnNode( - node, - Diagnostics.Jump_target_cannot_cross_function_boundary - ); - } - - switch (current.kind) { - case SyntaxKind.LabeledStatement: - if (node.label - && ( current).label.escapedText - === node.label.escapedText) - { - // found matching label - verify that label usage is correct - // continue can only target labels that are on iteration statements - const isMisplacedContinueLabel = - node.kind === SyntaxKind.ContinueStatement - && !isIterationStatement( - ( current) - .statement, /*lookInLabeledStatement*/ - true - ); - - if (isMisplacedContinueLabel) { - return grammarErrorOnNode( - node, - Diagnostics - .A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement - ); - } - - return false; - } - break; - case SyntaxKind.SwitchStatement: - if (node.kind === SyntaxKind.BreakStatement - && !node.label) - { - // unlabeled break within switch statement - ok - return false; - } - break; - default: - if (isIterationStatement( - current, /*lookInLabeledStatement*/ - false - ) && !node.label) { - // unlabeled break or continue within iteration statement - ok - return false; - } - break; - } - - current = current.parent; - } - - if (node.label) { - const message = node.kind === SyntaxKind.BreakStatement - ? Diagnostics - .A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement - : Diagnostics - .A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement; - - return grammarErrorOnNode(node, message); - } else { - const message = node.kind === SyntaxKind.BreakStatement - ? Diagnostics - .A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement - : Diagnostics - .A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement; - return grammarErrorOnNode(node, message); - } - } - - function checkGrammarBindingElement(node: BindingElement) { - if (node.dotDotDotToken) { - const elements = node.parent.elements; - if (node !== last(elements)) { - return grammarErrorOnNode( - node, - Diagnostics - .A_rest_element_must_be_last_in_a_destructuring_pattern - ); - } - checkGrammarForDisallowedTrailingComma( - elements, - Diagnostics - .A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma - ); - - if (node.propertyName) { - return grammarErrorOnNode( - node.name, - Diagnostics.A_rest_element_cannot_have_a_property_name - ); - } - - if (node.initializer) { - // Error on equals token which immediately precedes the initializer - return grammarErrorAtPos( - node, - node.initializer.pos - 1, - 1, - Diagnostics.A_rest_element_cannot_have_an_initializer - ); - } - } - } - - function isStringOrNumberLiteralExpression(expr: Expression) { - return isStringOrNumericLiteralLike(expr) - || expr.kind === SyntaxKind.PrefixUnaryExpression - && ( expr).operator - === SyntaxKind.MinusToken - && ( expr).operand.kind - === SyntaxKind.NumericLiteral; - } - - function isBigIntLiteralExpression(expr: Expression) { - return expr.kind === SyntaxKind.BigIntLiteral - || expr.kind === SyntaxKind.PrefixUnaryExpression - && ( expr).operator - === SyntaxKind.MinusToken - && ( expr).operand.kind - === SyntaxKind.BigIntLiteral; - } - - function isSimpleLiteralEnumReference(expr: Expression) { - if ((isPropertyAccessExpression(expr) - || (isElementAccessExpression(expr) - && isStringOrNumberLiteralExpression(expr - .argumentExpression))) - && isEntityNameExpression(expr.expression)) - { - return !!(checkExpressionCached(expr).flags - & TypeFlags.EnumLiteral); - } - } - - function checkAmbientInitializer(node: VariableDeclaration - | PropertyDeclaration | PropertySignature) - { - const { initializer } = node; - if (initializer) { - const isInvalidInitializer = !( - isStringOrNumberLiteralExpression(initializer) - || isSimpleLiteralEnumReference(initializer) - || initializer.kind === SyntaxKind.TrueKeyword - || initializer.kind === SyntaxKind.FalseKeyword - || isBigIntLiteralExpression(initializer) - ); - const isConstOrReadonly = isDeclarationReadonly(node) - || isVariableDeclaration(node) && isVarConst(node); - if (isConstOrReadonly && !node.type) { - if (isInvalidInitializer) { - return grammarErrorOnNode( - initializer, - Diagnostics - .A_const_initializer_in_an_ambient_context_must_be_a_string_or_numeric_literal_or_literal_enum_reference - ); - } - } else { - return grammarErrorOnNode( - initializer, - Diagnostics - .Initializers_are_not_allowed_in_ambient_contexts - ); - } - if (!isConstOrReadonly || isInvalidInitializer) { - return grammarErrorOnNode( - initializer, - Diagnostics - .Initializers_are_not_allowed_in_ambient_contexts - ); - } - } - } - - function checkGrammarVariableDeclaration(node: VariableDeclaration) { - if (node.parent.parent.kind !== SyntaxKind.ForInStatement - && node.parent.parent.kind !== SyntaxKind.ForOfStatement) - { - if (node.flags & NodeFlags.Ambient) { - checkAmbientInitializer(node); - } else if (!node.initializer) { - if (isBindingPattern(node.name) - && !isBindingPattern(node.parent)) - { - return grammarErrorOnNode( - node, - Diagnostics - .A_destructuring_declaration_must_have_an_initializer - ); - } - if (isVarConst(node)) { - return grammarErrorOnNode( - node, - Diagnostics.const_declarations_must_be_initialized - ); - } - } - } - - if (node.exclamationToken - && (node.parent.parent.kind !== SyntaxKind.VariableStatement - || !node.type || node.initializer - || node.flags & NodeFlags.Ambient)) - { - return grammarErrorOnNode( - node.exclamationToken, - Diagnostics - .Definite_assignment_assertions_can_only_be_used_along_with_a_type_annotation - ); - } - - const moduleKind = getEmitModuleKind(compilerOptions); - - if (moduleKind < ModuleKind.ES2015 - && moduleKind !== ModuleKind.System && !compilerOptions.noEmit - && !(node.parent.parent.flags & NodeFlags.Ambient) - && hasModifier(node.parent.parent, ModifierFlags.Export)) - { - checkESModuleMarker(node.name); - } - - const checkLetConstNames = (isLet(node) || isVarConst(node)); - - // 1. LexicalDeclaration : LetOrConst BindingList ; - // It is a Syntax Error if the BoundNames of BindingList contains "let". - // 2. ForDeclaration: ForDeclaration : LetOrConst ForBinding - // It is a Syntax Error if the BoundNames of ForDeclaration contains "let". - - // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code - // and its Identifier is eval or arguments - return checkLetConstNames - && checkGrammarNameInLetOrConstDeclarations(node.name); - } - - function checkESModuleMarker(name: Identifier - | BindingPattern): boolean - { - if (name.kind === SyntaxKind.Identifier) { - if (idText(name) === '__esModule') { - return grammarErrorOnNode( - name, - Diagnostics - .Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules - ); - } - } else { - const elements = name.elements; - for (const element of elements) { - if (!isOmittedExpression(element)) { - return checkESModuleMarker(element.name); - } - } - } - return false; - } - - function checkGrammarNameInLetOrConstDeclarations(name: Identifier - | BindingPattern): boolean - { - if (name.kind === SyntaxKind.Identifier) { - if (name.originalKeywordKind === SyntaxKind.LetKeyword) { - return grammarErrorOnNode( - name, - Diagnostics - .let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations - ); - } - } else { - const elements = name.elements; - for (const element of elements) { - if (!isOmittedExpression(element)) { - checkGrammarNameInLetOrConstDeclarations(element.name); - } - } - } - return false; - } - - function checkGrammarVariableDeclarationList(declarationList: - VariableDeclarationList): boolean - { - const declarations = declarationList.declarations; - if (checkGrammarForDisallowedTrailingComma(declarationList - .declarations)) - { - return true; - } - - if (!declarationList.declarations.length) { - return grammarErrorAtPos( - declarationList, - declarations.pos, - declarations.end - declarations.pos, - Diagnostics.Variable_declaration_list_cannot_be_empty - ); - } - return false; - } - - function allowLetAndConstDeclarations(parent: Node): boolean { - switch (parent.kind) { - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - return false; - case SyntaxKind.LabeledStatement: - return allowLetAndConstDeclarations(parent.parent); - } - - return true; - } - - function checkGrammarForDisallowedLetOrConstStatement(node: - VariableStatement) - { - if (!allowLetAndConstDeclarations(node.parent)) { - if (isLet(node.declarationList)) { - return grammarErrorOnNode( - node, - Diagnostics - .let_declarations_can_only_be_declared_inside_a_block - ); - } else if (isVarConst(node.declarationList)) { - return grammarErrorOnNode( - node, - Diagnostics - .const_declarations_can_only_be_declared_inside_a_block - ); - } - } - } - - function checkGrammarMetaProperty(node: MetaProperty) { - const escapedText = node.name.escapedText; - switch (node.keywordToken) { - case SyntaxKind.NewKeyword: - if (escapedText !== 'target') { - return grammarErrorOnNode( - node.name, - Diagnostics - ._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, - node.name.escapedText, - tokenToString(node.keywordToken), - 'target' - ); - } - break; - case SyntaxKind.ImportKeyword: - if (escapedText !== 'meta') { - return grammarErrorOnNode( - node.name, - Diagnostics - ._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, - node.name.escapedText, - tokenToString(node.keywordToken), - 'meta' - ); - } - break; - } - } - - function hasParseDiagnostics(sourceFile: SourceFile): boolean { - return sourceFile.parseDiagnostics.length > 0; - } - - function grammarErrorOnFirstToken( - node: Node, - message: DiagnosticMessage, - arg0?: any, - arg1?: any, - arg2?: any - ): boolean { - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition(sourceFile, node.pos); - diagnostics - .add(createFileDiagnostic( - sourceFile, - span.start, - span.length, - message, - arg0, - arg1, - arg2 - )); - return true; - } - return false; - } - - function grammarErrorAtPos( - nodeForSourceFile: Node, - start: number, - length: number, - message: DiagnosticMessage, - arg0?: any, - arg1?: any, - arg2?: any - ): boolean { - const sourceFile = getSourceFileOfNode(nodeForSourceFile); - if (!hasParseDiagnostics(sourceFile)) { - diagnostics - .add(createFileDiagnostic( - sourceFile, - start, - length, - message, - arg0, - arg1, - arg2 - )); - return true; - } - return false; - } - - function grammarErrorOnNode( - node: Node, - message: DiagnosticMessage, - arg0?: any, - arg1?: any, - arg2?: any - ): boolean { - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - diagnostics - .add(createDiagnosticForNode( - node, - message, - arg0, - arg1, - arg2 - )); - return true; - } - return false; - } - - function checkGrammarConstructorTypeParameters(node: - ConstructorDeclaration) - { - const jsdocTypeParameters = isInJSFile(node) - ? getJSDocTypeParameterDeclarations(node) - : undefined; - const range = node.typeParameters || jsdocTypeParameters - && firstOrUndefined(jsdocTypeParameters); - if (range) { - const pos = range.pos === range.end - ? range.pos - : skipTrivia(getSourceFileOfNode(node).text, range.pos); - return grammarErrorAtPos( - node, - pos, - range.end - pos, - Diagnostics - .Type_parameters_cannot_appear_on_a_constructor_declaration - ); - } - } - - function checkGrammarConstructorTypeAnnotation(node: - ConstructorDeclaration) - { - const type = getEffectiveReturnTypeNode(node); - if (type) { - return grammarErrorOnNode( - type, - Diagnostics - .Type_annotation_cannot_appear_on_a_constructor_declaration - ); - } - } - - function checkGrammarProperty(node: PropertyDeclaration - | PropertySignature) - { - if (isClassLike(node.parent)) { - if (isStringLiteral(node.name) - && node.name.text === 'constructor') - { - return grammarErrorOnNode( - node.name, - Diagnostics - .Classes_may_not_have_a_field_named_constructor - ); - } - if (checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_a_class_property_declaration_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - )) { - return true; - } - if (languageVersion < ScriptTarget.ES2015 - && isPrivateIdentifier(node.name)) - { - return grammarErrorOnNode( - node.name, - Diagnostics - .Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher - ); - } - } else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) { - if (checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - )) { - return true; - } - if (node.initializer) { - return grammarErrorOnNode( - node.initializer, - Diagnostics - .An_interface_property_cannot_have_an_initializer - ); - } - } else if (node.parent.kind === SyntaxKind.TypeLiteral) { - if (checkGrammarForInvalidDynamicName( - node.name, - Diagnostics - .A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type - )) { - return true; - } - if (node.initializer) { - return grammarErrorOnNode( - node.initializer, - Diagnostics - .A_type_literal_property_cannot_have_an_initializer - ); - } - } - - if (node.flags & NodeFlags.Ambient) { - checkAmbientInitializer(node); - } - - if (isPropertyDeclaration(node) && node.exclamationToken - && (!isClassLike(node.parent) || !node.type || node.initializer - || node.flags & NodeFlags.Ambient - || hasModifier( - node, - ModifierFlags.Static | ModifierFlags.Abstract - ))) - { - return grammarErrorOnNode( - node.exclamationToken, - Diagnostics - .A_definite_assignment_assertion_is_not_permitted_in_this_context - ); - } - } - - function checkGrammarTopLevelElementForRequiredDeclareModifier(node: - Node): boolean - { - // A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace - // interfaces and imports categories: - // - // DeclarationElement: - // ExportAssignment - // export_opt InterfaceDeclaration - // export_opt TypeAliasDeclaration - // export_opt ImportDeclaration - // export_opt ExternalImportDeclaration - // export_opt AmbientDeclaration - // - // TODO: The spec needs to be amended to reflect this grammar. - if (node.kind === SyntaxKind.InterfaceDeclaration - || node.kind === SyntaxKind.TypeAliasDeclaration - || node.kind === SyntaxKind.ImportDeclaration - || node.kind === SyntaxKind.ImportEqualsDeclaration - || node.kind === SyntaxKind.ExportDeclaration - || node.kind === SyntaxKind.ExportAssignment - || node.kind === SyntaxKind.NamespaceExportDeclaration - || hasModifier( - node, - ModifierFlags.Ambient | ModifierFlags.Export - | ModifierFlags.Default - )) - { - return false; - } - - return grammarErrorOnFirstToken( - node, - Diagnostics - .Top_level_declarations_in_d_ts_files_must_start_with_either_a_declare_or_export_modifier - ); - } - - function checkGrammarTopLevelElementsForRequiredDeclareModifier(file: - SourceFile): boolean - { - for (const decl of file.statements) { - if (isDeclaration(decl) - || decl.kind === SyntaxKind.VariableStatement) - { - if (checkGrammarTopLevelElementForRequiredDeclareModifier(decl)) { - return true; - } - } - } - return false; - } - - function checkGrammarSourceFile(node: SourceFile): boolean { - return !!(node.flags & NodeFlags.Ambient) - && checkGrammarTopLevelElementsForRequiredDeclareModifier(node); - } - - function checkGrammarStatementInAmbientContext(node: Node): boolean { - if (node.flags & NodeFlags.Ambient) { - // Find containing block which is either Block, ModuleBlock, SourceFile - const links = getNodeLinks(node); - if (!links.hasReportedStatementInAmbientContext - && (isFunctionLike(node.parent) - || isAccessor(node.parent))) - { - return getNodeLinks(node) - .hasReportedStatementInAmbientContext = grammarErrorOnFirstToken( - node, - Diagnostics - .An_implementation_cannot_be_declared_in_ambient_contexts - ); - } - - // We are either parented by another statement, or some sort of block. - // If we're in a block, we only want to really report an error once - // to prevent noisiness. So use a bit on the block to indicate if - // this has already been reported, and don't report if it has. - // - if (node.parent.kind === SyntaxKind.Block - || node.parent.kind === SyntaxKind.ModuleBlock - || node.parent.kind === SyntaxKind.SourceFile) - { - const links = getNodeLinks(node.parent); - // Check if the containing block ever report this error - if (!links.hasReportedStatementInAmbientContext) { - return links - .hasReportedStatementInAmbientContext = grammarErrorOnFirstToken( - node, - Diagnostics - .Statements_are_not_allowed_in_ambient_contexts - ); - } - } else { - // We must be parented by a statement. If so, there's no need - // to report the error as our parent will have already done it. - // Debug.assert(isStatement(node.parent)); - } - } - return false; - } - - function checkGrammarNumericLiteral(node: NumericLiteral): boolean { - // Grammar checking - if (node.numericLiteralFlags & TokenFlags.Octal) { - let diagnosticMessage: DiagnosticMessage | undefined; - if (languageVersion >= ScriptTarget.ES5) { - diagnosticMessage = Diagnostics - .Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0; - } else if (isChildOfNodeWithKind( - node, - SyntaxKind.LiteralType - )) { - diagnosticMessage = Diagnostics - .Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0; - } else if (isChildOfNodeWithKind( - node, - SyntaxKind.EnumMember - )) { - diagnosticMessage = Diagnostics - .Octal_literals_are_not_allowed_in_enums_members_initializer_Use_the_syntax_0; - } - if (diagnosticMessage) { - const withMinus = isPrefixUnaryExpression(node.parent) - && node.parent.operator === SyntaxKind.MinusToken; - const literal = (withMinus ? '-' : '') + '0o' + node.text; - return grammarErrorOnNode( - withMinus ? node.parent : node, - diagnosticMessage, - literal - ); - } - } - - // Realism (size) checking - checkNumericLiteralValueSize(node); - - return false; - } - - function checkNumericLiteralValueSize(node: NumericLiteral) { - // Scientific notation (e.g. 2e54 and 1e00000000010) can't be converted to bigint - // Literals with 15 or fewer characters aren't long enough to reach past 2^53 - 1 - // Fractional numbers (e.g. 9000000000000000.001) are inherently imprecise anyway - if (node.numericLiteralFlags & TokenFlags.Scientific - || node.text.length <= 15 || node.text.indexOf('.') !== -1) - { - return; - } - - // We can't rely on the runtime to accurately store and compare extremely large numeric values - // Even for internal use, we use getTextOfNode: https://github.com/microsoft/TypeScript/issues/33298 - // Thus, if the runtime claims a too-large number is lower than Number.MAX_SAFE_INTEGER, - // it's likely addition operations on it will fail too - const apparentValue = +getTextOfNode(node); - if (apparentValue <= 2 ** 53 - 1 - && apparentValue + 1 > apparentValue) - { - return; - } - - addErrorOrSuggestion( - /*isError*/ false, - createDiagnosticForNode( - node, - Diagnostics - .Numeric_literals_with_absolute_values_equal_to_2_53_or_greater_are_too_large_to_be_represented_accurately_as_integers - ) - ); - } - - function checkGrammarBigIntLiteral(node: BigIntLiteral): boolean { - const literalType = isLiteralTypeNode(node.parent) - || isPrefixUnaryExpression(node.parent) - && isLiteralTypeNode(node.parent.parent); - if (!literalType) { - if (languageVersion < ScriptTarget.ES2020) { - if (grammarErrorOnNode( - node, - Diagnostics - .BigInt_literals_are_not_available_when_targeting_lower_than_ES2020 - )) { - return true; - } - } - } - return false; - } - - function grammarErrorAfterFirstToken( - node: Node, - message: DiagnosticMessage, - arg0?: any, - arg1?: any, - arg2?: any - ): boolean { - const sourceFile = getSourceFileOfNode(node); - if (!hasParseDiagnostics(sourceFile)) { - const span = getSpanOfTokenAtPosition(sourceFile, node.pos); - diagnostics - .add(createFileDiagnostic( - sourceFile, - textSpanEnd(span), /*length*/ - 0, - message, - arg0, - arg1, - arg2 - )); - return true; - } - return false; - } - - function getAmbientModules(): Symbol[] { - if (!ambientModulesCache) { - ambientModulesCache = []; - globals.forEach((global, sym) => { - // No need to `unescapeLeadingUnderscores`, an escaped symbol is never an ambient module. - if (ambientModuleSymbolRegex.test(sym as string)) { - ambientModulesCache!.push(global); - } - }); - } - return ambientModulesCache; - } - - function checkGrammarImportClause(node: ImportClause): boolean { - if (node.isTypeOnly && node.name && node.namedBindings) { - return grammarErrorOnNode( - node, - Diagnostics - .A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both - ); - } - return false; - } - - function checkGrammarImportCallExpression(node: ImportCall): boolean { - if (moduleKind === ModuleKind.ES2015) { - return grammarErrorOnNode( - node, - Diagnostics - .Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_esnext_commonjs_amd_system_or_umd - ); - } - - if (node.typeArguments) { - return grammarErrorOnNode( - node, - Diagnostics.Dynamic_import_cannot_have_type_arguments - ); - } - - const nodeArguments = node.arguments; - if (nodeArguments.length !== 1) { - return grammarErrorOnNode( - node, - Diagnostics - .Dynamic_import_must_have_one_specifier_as_an_argument - ); - } - checkGrammarForDisallowedTrailingComma(nodeArguments); - // see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import. - // parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed as specifier in dynamic import. - if (isSpreadElement(nodeArguments[0])) { - return grammarErrorOnNode( - nodeArguments[0], - Diagnostics - .Specifier_of_dynamic_import_cannot_be_spread_element - ); - } - return false; - } - - function findMatchingTypeReferenceOrTypeAliasReference( - source: Type, - unionTarget: UnionOrIntersectionType - ) { - const sourceObjectFlags = getObjectFlags(source); - if (sourceObjectFlags - & (ObjectFlags.Reference | ObjectFlags.Anonymous) - && unionTarget.flags & TypeFlags.Union) - { - return find(unionTarget.types, target => { - if (target.flags & TypeFlags.Object) { - const overlapObjFlags = - sourceObjectFlags & getObjectFlags(target); - if (overlapObjFlags & ObjectFlags.Reference) { - return (source as TypeReference).target - === (target as TypeReference).target; - } - if (overlapObjFlags & ObjectFlags.Anonymous) { - return !!(source as AnonymousType).aliasSymbol - && (source as AnonymousType).aliasSymbol - === (target as AnonymousType).aliasSymbol; - } - } - return false; - }); - } - } - - function findBestTypeForObjectLiteral( - source: Type, - unionTarget: UnionOrIntersectionType - ) { - if (getObjectFlags(source) & ObjectFlags.ObjectLiteral - && forEachType(unionTarget, isArrayLikeType)) - { - return find(unionTarget.types, t => !isArrayLikeType(t)); - } - } - - function findBestTypeForInvokable( - source: Type, - unionTarget: UnionOrIntersectionType - ) { - let signatureKind = SignatureKind.Call; - const hasSignatures = - getSignaturesOfType(source, signatureKind).length > 0 - || (signatureKind = SignatureKind.Construct, - getSignaturesOfType(source, signatureKind).length > 0); - if (hasSignatures) { - return find( - unionTarget.types, - t => getSignaturesOfType(t, signatureKind).length > 0 - ); - } - } - - function findMostOverlappyType( - source: Type, - unionTarget: UnionOrIntersectionType - ) { - let bestMatch: Type | undefined; - let matchingCount = 0; - for (const target of unionTarget.types) { - const overlap = - getIntersectionType([getIndexType(source), - getIndexType(target)]); - if (overlap.flags & TypeFlags.Index) { - // perfect overlap of keys - bestMatch = target; - matchingCount = Infinity; - } else if (overlap.flags & TypeFlags.Union) { - // We only want to account for literal types otherwise. - // If we have a union of index types, it seems likely that we - // needed to elaborate between two generic mapped types anyway. - const len = - length(filter( - (overlap as UnionType).types, - isUnitType - )); - if (len >= matchingCount) { - bestMatch = target; - matchingCount = len; - } - } else if (isUnitType(overlap) && 1 >= matchingCount) { - bestMatch = target; - matchingCount = 1; - } - } - return bestMatch; - } - - function filterPrimitivesIfContainsNonPrimitive(type: UnionType) { - if (maybeTypeOfKind(type, TypeFlags.NonPrimitive)) { - const result = filterType( - type, - t => !(t.flags & TypeFlags.Primitive) - ); - if (!(result.flags & TypeFlags.Never)) { - return result; - } - } - return type; - } - - // Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly - function findMatchingDiscriminantType( - source: Type, - target: Type, - isRelatedTo: (source: Type, target: Type) => Ternary - ) { - if (target.flags & TypeFlags.Union - && source.flags & (TypeFlags.Intersection | TypeFlags.Object)) - { - const sourceProperties = getPropertiesOfType(source); - if (sourceProperties) { - const sourcePropertiesFiltered = - findDiscriminantProperties(sourceProperties, target); - if (sourcePropertiesFiltered) { - return discriminateTypeByDiscriminableItems( - target, - map( - sourcePropertiesFiltered, - p => ([() => getTypeOfSymbol(p), - p.escapedName] as [() => Type, __String]) - ), - isRelatedTo - ); - } - } - } - return undefined; - } - } - - function isNotAccessor(declaration: Declaration): boolean { - // Accessors check for their own matching duplicates, and in contexts where they are valid, there are already duplicate identifier checks - return !isAccessor(declaration); - } - - function isNotOverload(declaration: Declaration): boolean { - return (declaration.kind !== SyntaxKind.FunctionDeclaration - && declaration.kind !== SyntaxKind.MethodDeclaration) - || !!(declaration as FunctionDeclaration).body; - } - - /** Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. */ - function isDeclarationNameOrImportPropertyName(name: Node): boolean { - switch (name.parent.kind) { - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - return isIdentifier(name); - default: - return isDeclarationName(name); - } - } - - function isSomeImportDeclaration(decl: Node): boolean { - switch (decl.kind) { - case SyntaxKind.ImportClause: // For default import - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.NamespaceImport: - case SyntaxKind.ImportSpecifier: // For rename import `x as y` - return true; - case SyntaxKind.Identifier: - // For regular import, `decl` is an Identifier under the ImportSpecifier. - return decl.parent.kind === SyntaxKind.ImportSpecifier; - default: - return false; - } - } - - namespace JsxNames { - export const JSX = 'JSX' as __String; - export const IntrinsicElements = 'IntrinsicElements' as __String; - export const ElementClass = 'ElementClass' as __String; - export const ElementAttributesPropertyNameContainer = - 'ElementAttributesProperty' as __String; // TODO: Deprecate and remove support - export const ElementChildrenAttributeNameContainer = - 'ElementChildrenAttribute' as __String; - export const Element = 'Element' as __String; - export const IntrinsicAttributes = 'IntrinsicAttributes' as __String; - export const IntrinsicClassAttributes = - 'IntrinsicClassAttributes' as __String; - export const LibraryManagedAttributes = - 'LibraryManagedAttributes' as __String; - } - - function getIterationTypesKeyFromIterationTypeKind(typeKind: - IterationTypeKind) - { - switch (typeKind) { - case IterationTypeKind.Yield: - return 'yieldType'; - case IterationTypeKind.Return: - return 'returnType'; - case IterationTypeKind.Next: - return 'nextType'; - } - } - - export function signatureHasRestParameter(s: Signature) { - return !!(s.flags & SignatureFlags.HasRestParameter); - } - - export function signatureHasLiteralTypes(s: Signature) { - return !!(s.flags & SignatureFlags.HasLiteralTypes); - } -} diff --git a/tests/test.rs b/tests/spec_test.rs similarity index 55% rename from tests/test.rs rename to tests/spec_test.rs index 178b9fc2..361925c5 100644 --- a/tests/test.rs +++ b/tests/spec_test.rs @@ -1,43 +1,12 @@ -extern crate dprint_development; -extern crate dprint_plugin_typescript; - -//#[macro_use] extern crate debug_here; - -use std::fs; use std::path::PathBuf; -use std::time::Instant; +use std::sync::Arc; use dprint_core::configuration::*; use dprint_development::*; use dprint_plugin_typescript::configuration::*; use dprint_plugin_typescript::*; -#[allow(dead_code)] -fn test_performance() { - // run this with `cargo test --release -- --nocapture` - - // This file was not written with an 80 line width in mind so overall - // it's not too bad, but there are a few small issues to fix here and there. - let config = ConfigurationBuilder::new().line_width(80).quote_style(QuoteStyle::PreferSingle).build(); - let file_text = fs::read_to_string("tests/performance/checker.txt").expect("Expected to read."); - - //debug_here!(); - - for i in 0..10 { - let start = Instant::now(); - let result = format_text(&PathBuf::from("checker.ts"), &file_text, &config).expect("Could not parse..."); - - println!("{}ms", start.elapsed().as_millis()); - println!("---"); - - if i == 0 { - fs::write("tests/performance/checker_output.txt", result.as_deref().unwrap_or(&file_text)).expect("Expected to write to the file."); - } - } -} - -#[test] -fn test_specs() { +fn main() { //debug_here!(); let global_config = GlobalConfiguration { // for the tests only because a higher indent width increases the likelihood of problems @@ -54,15 +23,15 @@ fn test_specs() { }, { let global_config = global_config.clone(); - move |file_name, file_text, spec_config| { + Arc::new(move |file_name, file_text, spec_config| { let spec_config: ConfigKeyMap = serde_json::from_value(spec_config.clone().into()).unwrap(); let config_result = resolve_config(spec_config, &global_config); ensure_no_diagnostics(&config_result.diagnostics); format_text(file_name, file_text, &config_result.config) - } + }) }, - move |_file_name, _file_text, _spec_config| { + Arc::new(move |_file_name, _file_text, _spec_config| { #[cfg(feature = "tracing")] { let spec_config: ConfigKeyMap = serde_json::from_value(_spec_config.clone().into()).unwrap(); @@ -73,6 +42,6 @@ fn test_specs() { #[cfg(not(feature = "tracing"))] panic!("\n====\nPlease run with `cargo test --features tracing` to get trace output\n====\n") - }, + }), ) } diff --git a/tests/specs/expressions/ObjectExpression/ObjectExpression_TrailingCommas_Always copy.txt b/tests/specs/expressions/ObjectExpression/ObjectExpression_TrailingCommas_Always.txt similarity index 100% rename from tests/specs/expressions/ObjectExpression/ObjectExpression_TrailingCommas_Always copy.txt rename to tests/specs/expressions/ObjectExpression/ObjectExpression_TrailingCommas_Always.txt diff --git a/tests/specs/issues/old-repo/issue022.txt b/tests/specs/issues/old_repo/issue022.txt similarity index 100% rename from tests/specs/issues/old-repo/issue022.txt rename to tests/specs/issues/old_repo/issue022.txt diff --git a/tests/specs/issues/old-repo/issue028.txt b/tests/specs/issues/old_repo/issue028.txt similarity index 100% rename from tests/specs/issues/old-repo/issue028.txt rename to tests/specs/issues/old_repo/issue028.txt diff --git a/tests/specs/issues/old-repo/issue035.txt b/tests/specs/issues/old_repo/issue035.txt similarity index 100% rename from tests/specs/issues/old-repo/issue035.txt rename to tests/specs/issues/old_repo/issue035.txt diff --git a/tests/specs/issues/old-repo/issue035_PreferSingleLine.txt b/tests/specs/issues/old_repo/issue035_PreferSingleLine.txt similarity index 100% rename from tests/specs/issues/old-repo/issue035_PreferSingleLine.txt rename to tests/specs/issues/old_repo/issue035_PreferSingleLine.txt diff --git a/tests/specs/issues/old-repo/issue036.txt b/tests/specs/issues/old_repo/issue036.txt similarity index 100% rename from tests/specs/issues/old-repo/issue036.txt rename to tests/specs/issues/old_repo/issue036.txt diff --git a/tests/specs/issues/old-repo/issue046.txt b/tests/specs/issues/old_repo/issue046.txt similarity index 100% rename from tests/specs/issues/old-repo/issue046.txt rename to tests/specs/issues/old_repo/issue046.txt diff --git a/tests/specs/issues/old-repo/issue051.txt b/tests/specs/issues/old_repo/issue051.txt similarity index 100% rename from tests/specs/issues/old-repo/issue051.txt rename to tests/specs/issues/old_repo/issue051.txt diff --git a/tests/specs/issues/old-repo/issue062.txt b/tests/specs/issues/old_repo/issue062.txt similarity index 100% rename from tests/specs/issues/old-repo/issue062.txt rename to tests/specs/issues/old_repo/issue062.txt diff --git a/tests/specs/issues/old-repo/issue066.txt b/tests/specs/issues/old_repo/issue066.txt similarity index 100% rename from tests/specs/issues/old-repo/issue066.txt rename to tests/specs/issues/old_repo/issue066.txt diff --git a/tests/specs/issues/old-repo/issue067.txt b/tests/specs/issues/old_repo/issue067.txt similarity index 100% rename from tests/specs/issues/old-repo/issue067.txt rename to tests/specs/issues/old_repo/issue067.txt diff --git a/tests/specs/issues/old-repo/issue083.txt b/tests/specs/issues/old_repo/issue083.txt similarity index 100% rename from tests/specs/issues/old-repo/issue083.txt rename to tests/specs/issues/old_repo/issue083.txt diff --git a/tests/specs/issues/old-repo/issue084.txt b/tests/specs/issues/old_repo/issue084.txt similarity index 100% rename from tests/specs/issues/old-repo/issue084.txt rename to tests/specs/issues/old_repo/issue084.txt diff --git a/tests/specs/issues/old-repo/issue105.txt b/tests/specs/issues/old_repo/issue105.txt similarity index 100% rename from tests/specs/issues/old-repo/issue105.txt rename to tests/specs/issues/old_repo/issue105.txt diff --git a/tests/specs/issues/old-repo/issue107.txt b/tests/specs/issues/old_repo/issue107.txt similarity index 100% rename from tests/specs/issues/old-repo/issue107.txt rename to tests/specs/issues/old_repo/issue107.txt diff --git a/tests/specs/issues/old-repo/issue116.txt b/tests/specs/issues/old_repo/issue116.txt similarity index 100% rename from tests/specs/issues/old-repo/issue116.txt rename to tests/specs/issues/old_repo/issue116.txt diff --git a/tests/specs/issues/old-repo/issue117.txt b/tests/specs/issues/old_repo/issue117.txt similarity index 100% rename from tests/specs/issues/old-repo/issue117.txt rename to tests/specs/issues/old_repo/issue117.txt diff --git a/tests/specs/issues/old-repo/issue122.txt b/tests/specs/issues/old_repo/issue122.txt similarity index 100% rename from tests/specs/issues/old-repo/issue122.txt rename to tests/specs/issues/old_repo/issue122.txt diff --git a/tests/specs/issues/old-repo/issue123.txt b/tests/specs/issues/old_repo/issue123.txt similarity index 100% rename from tests/specs/issues/old-repo/issue123.txt rename to tests/specs/issues/old_repo/issue123.txt diff --git a/tests/specs/issues/old-repo/issue124.txt b/tests/specs/issues/old_repo/issue124.txt similarity index 100% rename from tests/specs/issues/old-repo/issue124.txt rename to tests/specs/issues/old_repo/issue124.txt diff --git a/tests/specs/issues/old-repo/issue128.txt b/tests/specs/issues/old_repo/issue128.txt similarity index 100% rename from tests/specs/issues/old-repo/issue128.txt rename to tests/specs/issues/old_repo/issue128.txt diff --git a/tests/specs/issues/old-repo/issue131.txt b/tests/specs/issues/old_repo/issue131.txt similarity index 100% rename from tests/specs/issues/old-repo/issue131.txt rename to tests/specs/issues/old_repo/issue131.txt diff --git a/tests/specs/issues/old-repo/issue132.txt b/tests/specs/issues/old_repo/issue132.txt similarity index 100% rename from tests/specs/issues/old-repo/issue132.txt rename to tests/specs/issues/old_repo/issue132.txt diff --git a/tests/specs/issues/old-repo/issue133.txt b/tests/specs/issues/old_repo/issue133.txt similarity index 100% rename from tests/specs/issues/old-repo/issue133.txt rename to tests/specs/issues/old_repo/issue133.txt diff --git a/tests/specs/issues/old-repo/issue134.txt b/tests/specs/issues/old_repo/issue134.txt similarity index 100% rename from tests/specs/issues/old-repo/issue134.txt rename to tests/specs/issues/old_repo/issue134.txt diff --git a/tests/specs/issues/old-repo/issue139.txt b/tests/specs/issues/old_repo/issue139.txt similarity index 100% rename from tests/specs/issues/old-repo/issue139.txt rename to tests/specs/issues/old_repo/issue139.txt diff --git a/tests/specs/issues/old-repo/issue140.txt b/tests/specs/issues/old_repo/issue140.txt similarity index 100% rename from tests/specs/issues/old-repo/issue140.txt rename to tests/specs/issues/old_repo/issue140.txt diff --git a/tests/specs/issues/old-repo/issue142.txt b/tests/specs/issues/old_repo/issue142.txt similarity index 100% rename from tests/specs/issues/old-repo/issue142.txt rename to tests/specs/issues/old_repo/issue142.txt diff --git a/tests/specs/issues/old-repo/issue150.txt b/tests/specs/issues/old_repo/issue150.txt similarity index 100% rename from tests/specs/issues/old-repo/issue150.txt rename to tests/specs/issues/old_repo/issue150.txt diff --git a/tests/specs/issues/old-repo/issue158.txt b/tests/specs/issues/old_repo/issue158.txt similarity index 100% rename from tests/specs/issues/old-repo/issue158.txt rename to tests/specs/issues/old_repo/issue158.txt diff --git a/tests/specs/issues/old-repo/issue165.txt b/tests/specs/issues/old_repo/issue165.txt similarity index 100% rename from tests/specs/issues/old-repo/issue165.txt rename to tests/specs/issues/old_repo/issue165.txt diff --git a/tests/specs/issues/old-repo/issue170.txt b/tests/specs/issues/old_repo/issue170.txt similarity index 100% rename from tests/specs/issues/old-repo/issue170.txt rename to tests/specs/issues/old_repo/issue170.txt diff --git a/tests/specs/issues/old-repo/issue174.txt b/tests/specs/issues/old_repo/issue174.txt similarity index 100% rename from tests/specs/issues/old-repo/issue174.txt rename to tests/specs/issues/old_repo/issue174.txt diff --git a/tests/specs/issues/old-repo/issue174_PreferHanging.txt b/tests/specs/issues/old_repo/issue174_PreferHanging.txt similarity index 100% rename from tests/specs/issues/old-repo/issue174_PreferHanging.txt rename to tests/specs/issues/old_repo/issue174_PreferHanging.txt diff --git a/tests/specs/issues/old-repo/issue175.txt b/tests/specs/issues/old_repo/issue175.txt similarity index 100% rename from tests/specs/issues/old-repo/issue175.txt rename to tests/specs/issues/old_repo/issue175.txt diff --git a/tests/specs/issues/old-repo/issue179.txt b/tests/specs/issues/old_repo/issue179.txt similarity index 100% rename from tests/specs/issues/old-repo/issue179.txt rename to tests/specs/issues/old_repo/issue179.txt diff --git a/tests/specs/issues/old-repo/issue180.txt b/tests/specs/issues/old_repo/issue180.txt similarity index 100% rename from tests/specs/issues/old-repo/issue180.txt rename to tests/specs/issues/old_repo/issue180.txt diff --git a/tests/specs/issues/old-repo/issue183.txt b/tests/specs/issues/old_repo/issue183.txt similarity index 100% rename from tests/specs/issues/old-repo/issue183.txt rename to tests/specs/issues/old_repo/issue183.txt diff --git a/tests/specs/issues/old-repo/issue197.txt b/tests/specs/issues/old_repo/issue197.txt similarity index 100% rename from tests/specs/issues/old-repo/issue197.txt rename to tests/specs/issues/old_repo/issue197.txt diff --git a/tests/specs/issues/old-repo/issue199.txt b/tests/specs/issues/old_repo/issue199.txt similarity index 100% rename from tests/specs/issues/old-repo/issue199.txt rename to tests/specs/issues/old_repo/issue199.txt diff --git a/tests/specs/issues/old-repo/issue201.txt b/tests/specs/issues/old_repo/issue201.txt similarity index 100% rename from tests/specs/issues/old-repo/issue201.txt rename to tests/specs/issues/old_repo/issue201.txt diff --git a/tests/specs/issues/old-repo/issue212.txt b/tests/specs/issues/old_repo/issue212.txt similarity index 100% rename from tests/specs/issues/old-repo/issue212.txt rename to tests/specs/issues/old_repo/issue212.txt diff --git a/tests/specs/issues/old-repo/issue217.txt b/tests/specs/issues/old_repo/issue217.txt similarity index 100% rename from tests/specs/issues/old-repo/issue217.txt rename to tests/specs/issues/old_repo/issue217.txt diff --git a/tests/specs/issues/old-repo/issue219.txt b/tests/specs/issues/old_repo/issue219.txt similarity index 100% rename from tests/specs/issues/old-repo/issue219.txt rename to tests/specs/issues/old_repo/issue219.txt diff --git a/tests/specs/issues/old-repo/issue220.txt b/tests/specs/issues/old_repo/issue220.txt similarity index 100% rename from tests/specs/issues/old-repo/issue220.txt rename to tests/specs/issues/old_repo/issue220.txt