Skip to content

Commit bcea359

Browse files
committed
Merge pull request microsoft#4956 from Microsoft/bindingElementContextualTyping
Fix parameter destructuring issues
2 parents dd660dc + 80e3b72 commit bcea359

6 files changed

+182
-1
lines changed

src/compiler/checker.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -2294,10 +2294,17 @@ namespace ts {
22942294
return type && (type.flags & TypeFlags.Any) !== 0;
22952295
}
22962296

2297+
// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
2298+
// assigned by contextual typing.
2299+
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
2300+
let symbol = getSymbolOfNode(node);
2301+
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node);
2302+
}
2303+
22972304
// Return the inferred type for a binding element
22982305
function getTypeForBindingElement(declaration: BindingElement): Type {
22992306
let pattern = <BindingPattern>declaration.parent;
2300-
let parentType = getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>pattern.parent);
2307+
let parentType = getTypeForBindingElementParent(<VariableLikeDeclaration>pattern.parent);
23012308
// If parent has the unknown (error) type, then so does this binding element
23022309
if (parentType === unknownType) {
23032310
return unknownType;
@@ -9163,10 +9170,24 @@ namespace ts {
91639170
}
91649171
}
91659172

9173+
// When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push
9174+
// the destructured type into the contained binding elements.
9175+
function assignBindingElementTypes(node: VariableLikeDeclaration) {
9176+
if (isBindingPattern(node.name)) {
9177+
for (let element of (<BindingPattern>node.name).elements) {
9178+
if (element.kind !== SyntaxKind.OmittedExpression) {
9179+
getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element);
9180+
assignBindingElementTypes(element);
9181+
}
9182+
}
9183+
}
9184+
}
9185+
91669186
function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper) {
91679187
let links = getSymbolLinks(parameter);
91689188
if (!links.type) {
91699189
links.type = instantiateType(contextualType, mapper);
9190+
assignBindingElementTypes(<ParameterDeclaration>parameter.valueDeclaration);
91709191
}
91719192
else if (isInferentialContext(mapper)) {
91729193
// Even if the parameter already has a type, it might be because it was given a type while
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [destructuringWithGenericParameter.ts]
2+
class GenericClass<T> {
3+
payload: T;
4+
}
5+
6+
var genericObject = new GenericClass<{ greeting: string }>();
7+
8+
function genericFunction<T>(object: GenericClass<T>, callback: (payload: T) => void) {
9+
callback(object.payload);
10+
}
11+
12+
genericFunction(genericObject, ({greeting}) => {
13+
var s = greeting.toLocaleLowerCase(); // Greeting should be of type string
14+
});
15+
16+
17+
//// [destructuringWithGenericParameter.js]
18+
var GenericClass = (function () {
19+
function GenericClass() {
20+
}
21+
return GenericClass;
22+
})();
23+
var genericObject = new GenericClass();
24+
function genericFunction(object, callback) {
25+
callback(object.payload);
26+
}
27+
genericFunction(genericObject, function (_a) {
28+
var greeting = _a.greeting;
29+
var s = greeting.toLocaleLowerCase(); // Greeting should be of type string
30+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/destructuringWithGenericParameter.ts ===
2+
class GenericClass<T> {
3+
>GenericClass : Symbol(GenericClass, Decl(destructuringWithGenericParameter.ts, 0, 0))
4+
>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 0, 19))
5+
6+
payload: T;
7+
>payload : Symbol(payload, Decl(destructuringWithGenericParameter.ts, 0, 23))
8+
>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 0, 19))
9+
}
10+
11+
var genericObject = new GenericClass<{ greeting: string }>();
12+
>genericObject : Symbol(genericObject, Decl(destructuringWithGenericParameter.ts, 4, 3))
13+
>GenericClass : Symbol(GenericClass, Decl(destructuringWithGenericParameter.ts, 0, 0))
14+
>greeting : Symbol(greeting, Decl(destructuringWithGenericParameter.ts, 4, 38))
15+
16+
function genericFunction<T>(object: GenericClass<T>, callback: (payload: T) => void) {
17+
>genericFunction : Symbol(genericFunction, Decl(destructuringWithGenericParameter.ts, 4, 61))
18+
>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 6, 25))
19+
>object : Symbol(object, Decl(destructuringWithGenericParameter.ts, 6, 28))
20+
>GenericClass : Symbol(GenericClass, Decl(destructuringWithGenericParameter.ts, 0, 0))
21+
>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 6, 25))
22+
>callback : Symbol(callback, Decl(destructuringWithGenericParameter.ts, 6, 52))
23+
>payload : Symbol(payload, Decl(destructuringWithGenericParameter.ts, 6, 64))
24+
>T : Symbol(T, Decl(destructuringWithGenericParameter.ts, 6, 25))
25+
26+
callback(object.payload);
27+
>callback : Symbol(callback, Decl(destructuringWithGenericParameter.ts, 6, 52))
28+
>object.payload : Symbol(GenericClass.payload, Decl(destructuringWithGenericParameter.ts, 0, 23))
29+
>object : Symbol(object, Decl(destructuringWithGenericParameter.ts, 6, 28))
30+
>payload : Symbol(GenericClass.payload, Decl(destructuringWithGenericParameter.ts, 0, 23))
31+
}
32+
33+
genericFunction(genericObject, ({greeting}) => {
34+
>genericFunction : Symbol(genericFunction, Decl(destructuringWithGenericParameter.ts, 4, 61))
35+
>genericObject : Symbol(genericObject, Decl(destructuringWithGenericParameter.ts, 4, 3))
36+
>greeting : Symbol(greeting, Decl(destructuringWithGenericParameter.ts, 10, 33))
37+
38+
var s = greeting.toLocaleLowerCase(); // Greeting should be of type string
39+
>s : Symbol(s, Decl(destructuringWithGenericParameter.ts, 11, 7))
40+
>greeting.toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.d.ts, 402, 26))
41+
>greeting : Symbol(greeting, Decl(destructuringWithGenericParameter.ts, 10, 33))
42+
>toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.d.ts, 402, 26))
43+
44+
});
45+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
=== tests/cases/compiler/destructuringWithGenericParameter.ts ===
2+
class GenericClass<T> {
3+
>GenericClass : GenericClass<T>
4+
>T : T
5+
6+
payload: T;
7+
>payload : T
8+
>T : T
9+
}
10+
11+
var genericObject = new GenericClass<{ greeting: string }>();
12+
>genericObject : GenericClass<{ greeting: string; }>
13+
>new GenericClass<{ greeting: string }>() : GenericClass<{ greeting: string; }>
14+
>GenericClass : typeof GenericClass
15+
>greeting : string
16+
17+
function genericFunction<T>(object: GenericClass<T>, callback: (payload: T) => void) {
18+
>genericFunction : <T>(object: GenericClass<T>, callback: (payload: T) => void) => void
19+
>T : T
20+
>object : GenericClass<T>
21+
>GenericClass : GenericClass<T>
22+
>T : T
23+
>callback : (payload: T) => void
24+
>payload : T
25+
>T : T
26+
27+
callback(object.payload);
28+
>callback(object.payload) : void
29+
>callback : (payload: T) => void
30+
>object.payload : T
31+
>object : GenericClass<T>
32+
>payload : T
33+
}
34+
35+
genericFunction(genericObject, ({greeting}) => {
36+
>genericFunction(genericObject, ({greeting}) => { var s = greeting.toLocaleLowerCase(); // Greeting should be of type string}) : void
37+
>genericFunction : <T>(object: GenericClass<T>, callback: (payload: T) => void) => void
38+
>genericObject : GenericClass<{ greeting: string; }>
39+
>({greeting}) => { var s = greeting.toLocaleLowerCase(); // Greeting should be of type string} : ({greeting}: { greeting: string; }) => void
40+
>greeting : string
41+
42+
var s = greeting.toLocaleLowerCase(); // Greeting should be of type string
43+
>s : string
44+
>greeting.toLocaleLowerCase() : string
45+
>greeting.toLocaleLowerCase : () => string
46+
>greeting : string
47+
>toLocaleLowerCase : () => string
48+
49+
});
50+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class GenericClass<T> {
2+
payload: T;
3+
}
4+
5+
var genericObject = new GenericClass<{ greeting: string }>();
6+
7+
function genericFunction<T>(object: GenericClass<T>, callback: (payload: T) => void) {
8+
callback(object.payload);
9+
}
10+
11+
genericFunction(genericObject, ({greeting}) => {
12+
var s = greeting.toLocaleLowerCase(); // Greeting should be of type string
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// Repros from issues #4949 and #4818
4+
5+
////const result = [{ foo: 'hello' }]
6+
//// .map(({ /*1*/foo }) => /*2*/foo)
7+
//// .map(foo => foo);
8+
////
9+
////const f = (foo: (bar: string[]) => void) => { };
10+
////
11+
////f(([a, b]) => {
12+
//// /*3*/a.charAt(0); // Not okay: inferred as `any`
13+
////});
14+
15+
goTo.marker('1');
16+
verify.quickInfoIs('var foo: string');
17+
18+
goTo.marker('2');
19+
verify.quickInfoIs('var foo: string');
20+
21+
goTo.marker('3');
22+
verify.quickInfoIs('var a: string');

0 commit comments

Comments
 (0)