From a292da593bb4d059c84b0510b8b234dbe448119c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 23 Sep 2015 14:45:09 -0700 Subject: [PATCH 1/4] Check if binding element already has been assigned a contextual type --- src/compiler/checker.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2aee597092d..b0a695e1e7d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2293,10 +2293,17 @@ namespace ts { 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: VariableLikeDeclaration) { + let symbol = getSymbolOfNode(node); + return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node); + } + // Return the inferred type for a binding element function getTypeForBindingElement(declaration: BindingElement): Type { let pattern = declaration.parent; - let parentType = getTypeForVariableLikeDeclaration(pattern.parent); + let parentType = getTypeForBindingElementParent(pattern.parent); // If parent has the unknown (error) type, then so does this binding element if (parentType === unknownType) { return unknownType; From 1a2678961570d4fcc968056e75221105d59c77f0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Sep 2015 13:35:12 -0700 Subject: [PATCH 2/4] Properly propagate contextual types into parameter destructuring elements --- src/compiler/checker.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b0a695e1e7d..e8996a0b1ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9169,10 +9169,24 @@ namespace ts { } } + // 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(node: VariableLikeDeclaration) { + if (isBindingPattern(node.name)) { + for (let element of (node.name).elements) { + if (element.kind !== SyntaxKind.OmittedExpression) { + getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element); + assignBindingElementTypes(element); + } + } + } + } + function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper) { let links = getSymbolLinks(parameter); if (!links.type) { links.type = instantiateType(contextualType, mapper); + assignBindingElementTypes(parameter.valueDeclaration); } else if (isInferentialContext(mapper)) { // Even if the parameter already has a type, it might be because it was given a type while From eb73565429d6dfd8267688dbe60e5d851b19d2ae Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Sep 2015 13:52:19 -0700 Subject: [PATCH 3/4] Adding tests --- .../destructuringWithGenericParameter.js | 30 +++++++++++ .../destructuringWithGenericParameter.symbols | 45 +++++++++++++++++ .../destructuringWithGenericParameter.types | 50 +++++++++++++++++++ .../destructuringWithGenericParameter.ts | 13 +++++ .../fourslash/parameterWithDestructuring.ts | 11 ++++ 5 files changed, 149 insertions(+) create mode 100644 tests/baselines/reference/destructuringWithGenericParameter.js create mode 100644 tests/baselines/reference/destructuringWithGenericParameter.symbols create mode 100644 tests/baselines/reference/destructuringWithGenericParameter.types create mode 100644 tests/cases/compiler/destructuringWithGenericParameter.ts create mode 100644 tests/cases/fourslash/parameterWithDestructuring.ts diff --git a/tests/baselines/reference/destructuringWithGenericParameter.js b/tests/baselines/reference/destructuringWithGenericParameter.js new file mode 100644 index 00000000000..61d523f81fa --- /dev/null +++ b/tests/baselines/reference/destructuringWithGenericParameter.js @@ -0,0 +1,30 @@ +//// [destructuringWithGenericParameter.ts] +class GenericClass { + payload: T; +} + +var genericObject = new GenericClass<{ greeting: string }>(); + +function genericFunction(object: GenericClass, callback: (payload: T) => void) { + callback(object.payload); +} + +genericFunction(genericObject, ({greeting}) => { + var s = greeting.toLocaleLowerCase(); // Greeting should be of type string +}); + + +//// [destructuringWithGenericParameter.js] +var GenericClass = (function () { + function GenericClass() { + } + return GenericClass; +})(); +var genericObject = new GenericClass(); +function genericFunction(object, callback) { + callback(object.payload); +} +genericFunction(genericObject, function (_a) { + var greeting = _a.greeting; + var s = greeting.toLocaleLowerCase(); // Greeting should be of type string +}); diff --git a/tests/baselines/reference/destructuringWithGenericParameter.symbols b/tests/baselines/reference/destructuringWithGenericParameter.symbols new file mode 100644 index 00000000000..941cb0bf237 --- /dev/null +++ b/tests/baselines/reference/destructuringWithGenericParameter.symbols @@ -0,0 +1,45 @@ +=== tests/cases/compiler/destructuringWithGenericParameter.ts === +class GenericClass { +>GenericClass : Symbol(GenericClass, Decl(destructuringWithGenericParameter.ts, 0, 0)) +>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 0, 19)) + + payload: T; +>payload : Symbol(payload, Decl(destructuringWithGenericParameter.ts, 0, 23)) +>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 0, 19)) +} + +var genericObject = new GenericClass<{ greeting: string }>(); +>genericObject : Symbol(genericObject, Decl(destructuringWithGenericParameter.ts, 4, 3)) +>GenericClass : Symbol(GenericClass, Decl(destructuringWithGenericParameter.ts, 0, 0)) +>greeting : Symbol(greeting, Decl(destructuringWithGenericParameter.ts, 4, 38)) + +function genericFunction(object: GenericClass, callback: (payload: T) => void) { +>genericFunction : Symbol(genericFunction, Decl(destructuringWithGenericParameter.ts, 4, 61)) +>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 6, 25)) +>object : Symbol(object, Decl(destructuringWithGenericParameter.ts, 6, 28)) +>GenericClass : Symbol(GenericClass, Decl(destructuringWithGenericParameter.ts, 0, 0)) +>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 6, 25)) +>callback : Symbol(callback, Decl(destructuringWithGenericParameter.ts, 6, 52)) +>payload : Symbol(payload, Decl(destructuringWithGenericParameter.ts, 6, 64)) +>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 6, 25)) + + callback(object.payload); +>callback : Symbol(callback, Decl(destructuringWithGenericParameter.ts, 6, 52)) +>object.payload : Symbol(GenericClass.payload, Decl(destructuringWithGenericParameter.ts, 0, 23)) +>object : Symbol(object, Decl(destructuringWithGenericParameter.ts, 6, 28)) +>payload : Symbol(GenericClass.payload, Decl(destructuringWithGenericParameter.ts, 0, 23)) +} + +genericFunction(genericObject, ({greeting}) => { +>genericFunction : Symbol(genericFunction, Decl(destructuringWithGenericParameter.ts, 4, 61)) +>genericObject : Symbol(genericObject, Decl(destructuringWithGenericParameter.ts, 4, 3)) +>greeting : Symbol(greeting, Decl(destructuringWithGenericParameter.ts, 10, 33)) + + var s = greeting.toLocaleLowerCase(); // Greeting should be of type string +>s : Symbol(s, Decl(destructuringWithGenericParameter.ts, 11, 7)) +>greeting.toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.d.ts, 402, 26)) +>greeting : Symbol(greeting, Decl(destructuringWithGenericParameter.ts, 10, 33)) +>toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.d.ts, 402, 26)) + +}); + diff --git a/tests/baselines/reference/destructuringWithGenericParameter.types b/tests/baselines/reference/destructuringWithGenericParameter.types new file mode 100644 index 00000000000..07a86e679a9 --- /dev/null +++ b/tests/baselines/reference/destructuringWithGenericParameter.types @@ -0,0 +1,50 @@ +=== tests/cases/compiler/destructuringWithGenericParameter.ts === +class GenericClass { +>GenericClass : GenericClass +>T : T + + payload: T; +>payload : T +>T : T +} + +var genericObject = new GenericClass<{ greeting: string }>(); +>genericObject : GenericClass<{ greeting: string; }> +>new GenericClass<{ greeting: string }>() : GenericClass<{ greeting: string; }> +>GenericClass : typeof GenericClass +>greeting : string + +function genericFunction(object: GenericClass, callback: (payload: T) => void) { +>genericFunction : (object: GenericClass, callback: (payload: T) => void) => void +>T : T +>object : GenericClass +>GenericClass : GenericClass +>T : T +>callback : (payload: T) => void +>payload : T +>T : T + + callback(object.payload); +>callback(object.payload) : void +>callback : (payload: T) => void +>object.payload : T +>object : GenericClass +>payload : T +} + +genericFunction(genericObject, ({greeting}) => { +>genericFunction(genericObject, ({greeting}) => { var s = greeting.toLocaleLowerCase(); // Greeting should be of type string}) : void +>genericFunction : (object: GenericClass, callback: (payload: T) => void) => void +>genericObject : GenericClass<{ greeting: string; }> +>({greeting}) => { var s = greeting.toLocaleLowerCase(); // Greeting should be of type string} : ({greeting}: { greeting: string; }) => void +>greeting : string + + var s = greeting.toLocaleLowerCase(); // Greeting should be of type string +>s : string +>greeting.toLocaleLowerCase() : string +>greeting.toLocaleLowerCase : () => string +>greeting : string +>toLocaleLowerCase : () => string + +}); + diff --git a/tests/cases/compiler/destructuringWithGenericParameter.ts b/tests/cases/compiler/destructuringWithGenericParameter.ts new file mode 100644 index 00000000000..99b5bfade36 --- /dev/null +++ b/tests/cases/compiler/destructuringWithGenericParameter.ts @@ -0,0 +1,13 @@ +class GenericClass { + payload: T; +} + +var genericObject = new GenericClass<{ greeting: string }>(); + +function genericFunction(object: GenericClass, callback: (payload: T) => void) { + callback(object.payload); +} + +genericFunction(genericObject, ({greeting}) => { + var s = greeting.toLocaleLowerCase(); // Greeting should be of type string +}); diff --git a/tests/cases/fourslash/parameterWithDestructuring.ts b/tests/cases/fourslash/parameterWithDestructuring.ts new file mode 100644 index 00000000000..e8026125e2b --- /dev/null +++ b/tests/cases/fourslash/parameterWithDestructuring.ts @@ -0,0 +1,11 @@ +/// + +////const result = [{ foo: 'hello' }] +//// .map(({ /*1*/foo }) => /*2*/foo) +//// .map(foo => foo); + +goTo.marker('1'); +verify.quickInfoIs('var foo: string'); + +goTo.marker('2'); +verify.quickInfoIs('var foo: string'); From 80e3b728167efc1ceb141560bf239cb295eeacbc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Sep 2015 15:46:00 -0700 Subject: [PATCH 4/4] Adding repro from #4818 to test --- tests/cases/fourslash/parameterWithDestructuring.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/cases/fourslash/parameterWithDestructuring.ts b/tests/cases/fourslash/parameterWithDestructuring.ts index e8026125e2b..72dfefd553a 100644 --- a/tests/cases/fourslash/parameterWithDestructuring.ts +++ b/tests/cases/fourslash/parameterWithDestructuring.ts @@ -1,11 +1,22 @@ /// +// Repros from issues #4949 and #4818 + ////const result = [{ foo: 'hello' }] //// .map(({ /*1*/foo }) => /*2*/foo) //// .map(foo => foo); +//// +////const f = (foo: (bar: string[]) => void) => { }; +//// +////f(([a, b]) => { +//// /*3*/a.charAt(0); // Not okay: inferred as `any` +////}); goTo.marker('1'); verify.quickInfoIs('var foo: string'); goTo.marker('2'); verify.quickInfoIs('var foo: string'); + +goTo.marker('3'); +verify.quickInfoIs('var a: string');