Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <BindingPattern>declaration.parent;
let parentType = getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>pattern.parent);
let parentType = getTypeForBindingElementParent(<VariableLikeDeclaration>pattern.parent);
// If parent has the unknown (error) type, then so does this binding element
if (parentType === unknownType) {
return unknownType;
Expand Down Expand Up @@ -9162,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 (<BindingPattern>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(<ParameterDeclaration>parameter.valueDeclaration);
}
else if (isInferentialContext(mapper)) {
// Even if the parameter already has a type, it might be because it was given a type while
Expand Down
30 changes: 30 additions & 0 deletions tests/baselines/reference/destructuringWithGenericParameter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [destructuringWithGenericParameter.ts]
class GenericClass<T> {
payload: T;
}

var genericObject = new GenericClass<{ greeting: string }>();

function genericFunction<T>(object: GenericClass<T>, 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
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
=== tests/cases/compiler/destructuringWithGenericParameter.ts ===
class GenericClass<T> {
>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<T>(object: GenericClass<T>, 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))

});

50 changes: 50 additions & 0 deletions tests/baselines/reference/destructuringWithGenericParameter.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
=== tests/cases/compiler/destructuringWithGenericParameter.ts ===
class GenericClass<T> {
>GenericClass : GenericClass<T>
>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<T>(object: GenericClass<T>, callback: (payload: T) => void) {
>genericFunction : <T>(object: GenericClass<T>, callback: (payload: T) => void) => void
>T : T
>object : GenericClass<T>
>GenericClass : GenericClass<T>
>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<T>
>payload : T
}

genericFunction(genericObject, ({greeting}) => {
>genericFunction(genericObject, ({greeting}) => { var s = greeting.toLocaleLowerCase(); // Greeting should be of type string}) : void
>genericFunction : <T>(object: GenericClass<T>, 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

});

13 changes: 13 additions & 0 deletions tests/cases/compiler/destructuringWithGenericParameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class GenericClass<T> {
payload: T;
}

var genericObject = new GenericClass<{ greeting: string }>();

function genericFunction<T>(object: GenericClass<T>, callback: (payload: T) => void) {
callback(object.payload);
}

genericFunction(genericObject, ({greeting}) => {
var s = greeting.toLocaleLowerCase(); // Greeting should be of type string
});
22 changes: 22 additions & 0 deletions tests/cases/fourslash/parameterWithDestructuring.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts'/>

// 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');