Skip to content

Commit

Permalink
Merge pull request microsoft#4956 from Microsoft/bindingElementContex…
Browse files Browse the repository at this point in the history
…tualTyping

Fix parameter destructuring issues
  • Loading branch information
ahejlsberg committed Sep 24, 2015
2 parents dd660dc + 80e3b72 commit bcea359
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 1 deletion.
23 changes: 22 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2294,10 +2294,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 @@ -9163,10 +9170,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');

0 comments on commit bcea359

Please sign in to comment.