From b2e0c4bea60ea521df47fef415822cf4b66a663f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 10 Apr 2018 18:21:24 -0700 Subject: [PATCH] Add support for destructuring well-known and late-bound names (#23297) * Add support for destructuring well-known and late-bound names * Add test of not present late bound prop --- src/compiler/checker.ts | 9 +++-- ...redLateBoundNameHasCorrectTypes.errors.txt | 18 +++++++++ ...estructuredLateBoundNameHasCorrectTypes.js | 22 +++++++++++ ...cturedLateBoundNameHasCorrectTypes.symbols | 29 ++++++++++++++ ...ructuredLateBoundNameHasCorrectTypes.types | 38 +++++++++++++++++++ ...estructuredLateBoundNameHasCorrectTypes.ts | 12 ++++++ 6 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.errors.txt create mode 100644 tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.js create mode 100644 tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.symbols create mode 100644 tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.types create mode 100644 tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cc42cc43294..7e3357d9cdf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4174,14 +4174,17 @@ namespace ts { else { // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) const name = declaration.propertyName || declaration.name; - if (isComputedNonLiteralName(name)) { - // computed properties with non-literal names are treated as 'any' + const isLate = isLateBindableName(name); + const isWellKnown = isComputedPropertyName(name) && isWellKnownSymbolSyntactically(name.expression); + if (!isLate && !isWellKnown && isComputedNonLiteralName(name)) { return anyType; } // Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature, // or otherwise the type of the string index signature. - const text = getTextOfPropertyName(name); + const text = isLate ? getLateBoundNameFromType(checkComputedPropertyName(name as ComputedPropertyName) as LiteralType | UniqueESSymbolType) : + isWellKnown ? getPropertyNameForKnownSymbolName(idText(((name as ComputedPropertyName).expression as PropertyAccessExpression).name)) : + getTextOfPropertyName(name); // 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)) { diff --git a/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.errors.txt b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.errors.txt new file mode 100644 index 00000000000..6a5c50021db --- /dev/null +++ b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.errors.txt @@ -0,0 +1,18 @@ +tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts(11,7): error TS2459: Type '{ prop: string; }' has no property '[notPresent]' and no string index signature. + + +==== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts (1 errors) ==== + let { [Symbol.iterator]: destructured } = []; + void destructured; + + const named = "prop"; + + let { [named]: computed } = { prop: "b" }; + void computed; + + const notPresent = "prop2"; + + let { [notPresent]: computed2 } = { prop: "b" }; + ~~~~~~~~~~~~ +!!! error TS2459: Type '{ prop: string; }' has no property '[notPresent]' and no string index signature. + \ No newline at end of file diff --git a/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.js b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.js new file mode 100644 index 00000000000..b1bee839e90 --- /dev/null +++ b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.js @@ -0,0 +1,22 @@ +//// [destructuredLateBoundNameHasCorrectTypes.ts] +let { [Symbol.iterator]: destructured } = []; +void destructured; + +const named = "prop"; + +let { [named]: computed } = { prop: "b" }; +void computed; + +const notPresent = "prop2"; + +let { [notPresent]: computed2 } = { prop: "b" }; + + +//// [destructuredLateBoundNameHasCorrectTypes.js] +let { [Symbol.iterator]: destructured } = []; +void destructured; +const named = "prop"; +let { [named]: computed } = { prop: "b" }; +void computed; +const notPresent = "prop2"; +let { [notPresent]: computed2 } = { prop: "b" }; diff --git a/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.symbols b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.symbols new file mode 100644 index 00000000000..dbf0aac121d --- /dev/null +++ b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts === +let { [Symbol.iterator]: destructured } = []; +>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --)) +>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>destructured : Symbol(destructured, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 0, 5)) + +void destructured; +>destructured : Symbol(destructured, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 0, 5)) + +const named = "prop"; +>named : Symbol(named, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 3, 5)) + +let { [named]: computed } = { prop: "b" }; +>named : Symbol(named, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 3, 5)) +>computed : Symbol(computed, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 5, 5)) +>prop : Symbol(prop, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 5, 29)) + +void computed; +>computed : Symbol(computed, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 5, 5)) + +const notPresent = "prop2"; +>notPresent : Symbol(notPresent, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 8, 5)) + +let { [notPresent]: computed2 } = { prop: "b" }; +>notPresent : Symbol(notPresent, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 8, 5)) +>computed2 : Symbol(computed2, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 10, 5)) +>prop : Symbol(prop, Decl(destructuredLateBoundNameHasCorrectTypes.ts, 10, 35)) + diff --git a/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.types b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.types new file mode 100644 index 00000000000..8d24b45e0ec --- /dev/null +++ b/tests/baselines/reference/destructuredLateBoundNameHasCorrectTypes.types @@ -0,0 +1,38 @@ +=== tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts === +let { [Symbol.iterator]: destructured } = []; +>Symbol.iterator : symbol +>Symbol : SymbolConstructor +>iterator : symbol +>destructured : () => IterableIterator +>[] : undefined[] + +void destructured; +>void destructured : undefined +>destructured : () => IterableIterator + +const named = "prop"; +>named : "prop" +>"prop" : "prop" + +let { [named]: computed } = { prop: "b" }; +>named : "prop" +>computed : string +>{ prop: "b" } : { prop: string; } +>prop : string +>"b" : "b" + +void computed; +>void computed : undefined +>computed : string + +const notPresent = "prop2"; +>notPresent : "prop2" +>"prop2" : "prop2" + +let { [notPresent]: computed2 } = { prop: "b" }; +>notPresent : "prop2" +>computed2 : any +>{ prop: "b" } : { prop: string; } +>prop : string +>"b" : "b" + diff --git a/tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts b/tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts new file mode 100644 index 00000000000..b2e6538e49e --- /dev/null +++ b/tests/cases/compiler/destructuredLateBoundNameHasCorrectTypes.ts @@ -0,0 +1,12 @@ +// @target: es6 +let { [Symbol.iterator]: destructured } = []; +void destructured; + +const named = "prop"; + +let { [named]: computed } = { prop: "b" }; +void computed; + +const notPresent = "prop2"; + +let { [notPresent]: computed2 } = { prop: "b" };