diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2a5a357822..007edacadc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4249,15 +4249,18 @@ namespace ts { } function createInferenceMapper(context: InferenceContext): TypeMapper { - return t => { + let mapper: TypeMapper = t => { for (let i = 0; i < context.typeParameters.length; i++) { if (t === context.typeParameters[i]) { context.inferences[i].isFixed = true; return getInferredType(context, i); } } - return t; + return t; }; + + mapper.context = context; + return mapper; } function identityMapper(type: Type): Type { @@ -5468,7 +5471,9 @@ namespace ts { function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext { let inferences: TypeInferences[] = []; for (let unused of typeParameters) { - inferences.push({ primary: undefined, secondary: undefined, isFixed: false }); + inferences.push({ + primary: undefined, secondary: undefined, isFixed: false + }); } return { typeParameters, @@ -6769,10 +6774,23 @@ namespace ts { return result; } - // Presence of a contextual type mapper indicates inferential typing, except the identityMapper object is - // used as a special marker for other purposes. + /** + * Detect if the mapper implies an inference context. Specifically, there are 4 possible values + * for a mapper. Let's go through each one of them: + * + * 1. undefined - this means we are not doing inferential typing, but we may do contextual typing, + * which could cause us to assign a parameter a type + * 2. identityMapper - means we want to avoid assigning a parameter a type, whether or not we are in + * inferential typing (context is undefined for the identityMapper) + * 3. a mapper created by createInferenceMapper - we are doing inferential typing, we want to assign + * types to parameters and fix type parameters (context is defined) + * 4. an instantiation mapper created by createTypeMapper or createTypeEraser - this should never be + * passed as the contextual mapper when checking an expression (context is undefined for these) + * + * isInferentialContext is detecting if we are in case 3 + */ function isInferentialContext(mapper: TypeMapper) { - return mapper && mapper !== identityMapper; + return mapper && mapper.context; } // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property @@ -8861,13 +8879,52 @@ namespace ts { let len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); for (let i = 0; i < len; i++) { let parameter = signature.parameters[i]; - let links = getSymbolLinks(parameter); - links.type = instantiateType(getTypeAtPosition(context, i), mapper); + let contextualParameterType = getTypeAtPosition(context, i); + assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper); } if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) { let parameter = lastOrUndefined(signature.parameters); - let links = getSymbolLinks(parameter); - links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper); + let contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters)); + assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper); + } + } + + function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper) { + let links = getSymbolLinks(parameter); + if (!links.type) { + links.type = instantiateType(contextualType, mapper); + } + else if (isInferentialContext(mapper)) { + // Even if the parameter already has a type, it might be because it was given a type while + // processing the function as an argument to a prior signature during overload resolution. + // If this was the case, it may have caused some type parameters to be fixed. So here, + // we need to ensure that type parameters at the same positions get fixed again. This is + // done by calling instantiateType to attach the mapper to the contextualType, and then + // calling inferTypes to force a walk of contextualType so that all the correct fixing + // happens. The choice to pass in links.type may seem kind of arbitrary, but it serves + // to make sure that all the correct positions in contextualType are reached by the walk. + // Here is an example: + // + // interface Base { + // baseProp; + // } + // interface Derived extends Base { + // toBase(): Base; + // } + // + // var derived: Derived; + // + // declare function foo(x: T, func: (p: T) => T): T; + // declare function foo(x: T, func: (p: T) => T): T; + // + // var result = foo(derived, d => d.toBase()); + // + // We are typing d while checking the second overload. But we've already given d + // a type (Derived) from the first overload. However, we still want to fix the + // T in the second overload so that we do not infer Base as a candidate for T + // (inferring Base would make type argument inference inconsistent between the two + // overloads). + inferTypes(mapper.context, links.type, instantiateType(contextualType, mapper)); } } @@ -9087,27 +9144,36 @@ namespace ts { let links = getNodeLinks(node); let type = getTypeOfSymbol(node.symbol); - // Check if function expression is contextually typed and assign parameter types if so - if (!(links.flags & NodeCheckFlags.ContextChecked)) { + let contextSensitive = isContextSensitive(node); + let mightFixTypeParameters = contextSensitive && isInferentialContext(contextualMapper); + + // Check if function expression is contextually typed and assign parameter types if so. + // See the comment in assignTypeToParameterAndFixTypeParameters to understand why we need to + // check mightFixTypeParameters. + if (mightFixTypeParameters || !(links.flags & NodeCheckFlags.ContextChecked)) { let contextualSignature = getContextualSignature(node); // If a type check is started at a function expression that is an argument of a function call, obtaining the // contextual type may recursively get back to here during overload resolution of the call. If so, we will have // already assigned contextual types. - if (!(links.flags & NodeCheckFlags.ContextChecked)) { + let contextChecked = !!(links.flags & NodeCheckFlags.ContextChecked); + if (mightFixTypeParameters || !contextChecked) { links.flags |= NodeCheckFlags.ContextChecked; if (contextualSignature) { let signature = getSignaturesOfType(type, SignatureKind.Call)[0]; - if (isContextSensitive(node)) { + if (contextSensitive) { assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper); } - if (!node.type && !signature.resolvedReturnType) { + if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) { let returnType = getReturnTypeFromBody(node, contextualMapper); if (!signature.resolvedReturnType) { signature.resolvedReturnType = returnType; } } } - checkSignatureDeclaration(node); + + if (!contextChecked) { + checkSignatureDeclaration(node); + } } } @@ -9797,7 +9863,7 @@ namespace ts { } function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, contextualMapper?: TypeMapper) { - if (contextualMapper && contextualMapper !== identityMapper) { + if (isInferentialContext(contextualMapper)) { let signature = getSingleCallSignature(type); if (signature && signature.typeParameters) { let contextualType = getContextualType(node); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e0f16a00c2..f1b476d11b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1912,6 +1912,9 @@ namespace ts { /* @internal */ export interface TypeMapper { (t: TypeParameter): Type; + context?: InferenceContext; // The inference context this mapper was created from. + // Only inference mappers have this set (in createInferenceMapper). + // The identity mapper and regular instantiation mappers do not need it. } /* @internal */ diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly1.js b/tests/baselines/reference/fixingTypeParametersRepeatedly1.js new file mode 100644 index 0000000000..93ab8ba988 --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly1.js @@ -0,0 +1,12 @@ +//// [fixingTypeParametersRepeatedly1.ts] +declare function f(x: T, y: (p: T) => T, z: (p: T) => T): T; +f("", x => null, x => x.toLowerCase()); + +// First overload of g should type check just like f +declare function g(x: T, y: (p: T) => T, z: (p: T) => T): T; +declare function g(); +g("", x => null, x => x.toLowerCase()); + +//// [fixingTypeParametersRepeatedly1.js] +f("", function (x) { return null; }, function (x) { return x.toLowerCase(); }); +g("", function (x) { return null; }, function (x) { return x.toLowerCase(); }); diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly1.symbols b/tests/baselines/reference/fixingTypeParametersRepeatedly1.symbols new file mode 100644 index 0000000000..998e59c0b8 --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly1.symbols @@ -0,0 +1,51 @@ +=== tests/cases/compiler/fixingTypeParametersRepeatedly1.ts === +declare function f(x: T, y: (p: T) => T, z: (p: T) => T): T; +>f : Symbol(f, Decl(fixingTypeParametersRepeatedly1.ts, 0, 0)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 0, 22)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) +>y : Symbol(y, Decl(fixingTypeParametersRepeatedly1.ts, 0, 27)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly1.ts, 0, 32)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) +>z : Symbol(z, Decl(fixingTypeParametersRepeatedly1.ts, 0, 43)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly1.ts, 0, 48)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 0, 19)) + +f("", x => null, x => x.toLowerCase()); +>f : Symbol(f, Decl(fixingTypeParametersRepeatedly1.ts, 0, 0)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 1, 5)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 1, 16)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, 399, 51)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 1, 16)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, 399, 51)) + +// First overload of g should type check just like f +declare function g(x: T, y: (p: T) => T, z: (p: T) => T): T; +>g : Symbol(g, Decl(fixingTypeParametersRepeatedly1.ts, 1, 39), Decl(fixingTypeParametersRepeatedly1.ts, 4, 63)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 4, 22)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) +>y : Symbol(y, Decl(fixingTypeParametersRepeatedly1.ts, 4, 27)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly1.ts, 4, 32)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) +>z : Symbol(z, Decl(fixingTypeParametersRepeatedly1.ts, 4, 43)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly1.ts, 4, 48)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly1.ts, 4, 19)) + +declare function g(); +>g : Symbol(g, Decl(fixingTypeParametersRepeatedly1.ts, 1, 39), Decl(fixingTypeParametersRepeatedly1.ts, 4, 63)) + +g("", x => null, x => x.toLowerCase()); +>g : Symbol(g, Decl(fixingTypeParametersRepeatedly1.ts, 1, 39), Decl(fixingTypeParametersRepeatedly1.ts, 4, 63)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 6, 5)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 6, 16)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, 399, 51)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 6, 16)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, 399, 51)) + diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly1.types b/tests/baselines/reference/fixingTypeParametersRepeatedly1.types new file mode 100644 index 0000000000..273c66b342 --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly1.types @@ -0,0 +1,63 @@ +=== tests/cases/compiler/fixingTypeParametersRepeatedly1.ts === +declare function f(x: T, y: (p: T) => T, z: (p: T) => T): T; +>f : (x: T, y: (p: T) => T, z: (p: T) => T) => T +>T : T +>x : T +>T : T +>y : (p: T) => T +>p : T +>T : T +>T : T +>z : (p: T) => T +>p : T +>T : T +>T : T +>T : T + +f("", x => null, x => x.toLowerCase()); +>f("", x => null, x => x.toLowerCase()) : string +>f : (x: T, y: (p: T) => T, z: (p: T) => T) => T +>"" : string +>x => null : (x: string) => any +>x : string +>null : null +>x => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + +// First overload of g should type check just like f +declare function g(x: T, y: (p: T) => T, z: (p: T) => T): T; +>g : { (x: T, y: (p: T) => T, z: (p: T) => T): T; (): any; } +>T : T +>x : T +>T : T +>y : (p: T) => T +>p : T +>T : T +>T : T +>z : (p: T) => T +>p : T +>T : T +>T : T +>T : T + +declare function g(); +>g : { (x: T, y: (p: T) => T, z: (p: T) => T): T; (): any; } + +g("", x => null, x => x.toLowerCase()); +>g("", x => null, x => x.toLowerCase()) : string +>g : { (x: T, y: (p: T) => T, z: (p: T) => T): T; (): any; } +>"" : string +>x => null : (x: string) => any +>x : string +>null : null +>x => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly2.errors.txt b/tests/baselines/reference/fixingTypeParametersRepeatedly2.errors.txt new file mode 100644 index 0000000000..e56b58a268 --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly2.errors.txt @@ -0,0 +1,32 @@ +tests/cases/compiler/fixingTypeParametersRepeatedly2.ts(11,27): error TS2345: Argument of type '(d: Derived) => Base' is not assignable to parameter of type '(p: Derived) => Derived'. + Type 'Base' is not assignable to type 'Derived'. + Property 'toBase' is missing in type 'Base'. +tests/cases/compiler/fixingTypeParametersRepeatedly2.ts(17,27): error TS2345: Argument of type '(d: Derived) => Base' is not assignable to parameter of type '(p: Derived) => Derived'. + Type 'Base' is not assignable to type 'Derived'. + + +==== tests/cases/compiler/fixingTypeParametersRepeatedly2.ts (2 errors) ==== + interface Base { + baseProp; + } + interface Derived extends Base { + toBase(): Base; + } + + var derived: Derived; + + declare function foo(x: T, func: (p: T) => T): T; + var result = foo(derived, d => d.toBase()); + ~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '(d: Derived) => Base' is not assignable to parameter of type '(p: Derived) => Derived'. +!!! error TS2345: Type 'Base' is not assignable to type 'Derived'. +!!! error TS2345: Property 'toBase' is missing in type 'Base'. + + // bar should type check just like foo. + // The same error should be observed in both cases. + declare function bar(x: T, func: (p: T) => T): T; + declare function bar(x: T, func: (p: T) => T): T; + var result = bar(derived, d => d.toBase()); + ~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '(d: Derived) => Base' is not assignable to parameter of type '(p: Derived) => Derived'. +!!! error TS2345: Type 'Base' is not assignable to type 'Derived'. \ No newline at end of file diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly2.js b/tests/baselines/reference/fixingTypeParametersRepeatedly2.js new file mode 100644 index 0000000000..9ebf426bbd --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly2.js @@ -0,0 +1,23 @@ +//// [fixingTypeParametersRepeatedly2.ts] +interface Base { + baseProp; +} +interface Derived extends Base { + toBase(): Base; +} + +var derived: Derived; + +declare function foo(x: T, func: (p: T) => T): T; +var result = foo(derived, d => d.toBase()); + +// bar should type check just like foo. +// The same error should be observed in both cases. +declare function bar(x: T, func: (p: T) => T): T; +declare function bar(x: T, func: (p: T) => T): T; +var result = bar(derived, d => d.toBase()); + +//// [fixingTypeParametersRepeatedly2.js] +var derived; +var result = foo(derived, function (d) { return d.toBase(); }); +var result = bar(derived, function (d) { return d.toBase(); }); diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly3.js b/tests/baselines/reference/fixingTypeParametersRepeatedly3.js new file mode 100644 index 0000000000..83a47c92c3 --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly3.js @@ -0,0 +1,23 @@ +//// [fixingTypeParametersRepeatedly3.ts] +interface Base { + baseProp; +} +interface Derived extends Base { + toBase?(): Base; +} + +var derived: Derived; + +declare function foo(x: T, func: (p: T) => T): T; +var result = foo(derived, d => d.toBase()); + +// bar should type check just like foo. +// result2 should have the same type as result +declare function bar(x: T, func: (p: T) => T): T; +declare function bar(x: T, func: (p: T) => T): T; +var result2 = bar(derived, d => d.toBase()); + +//// [fixingTypeParametersRepeatedly3.js] +var derived; +var result = foo(derived, function (d) { return d.toBase(); }); +var result2 = bar(derived, function (d) { return d.toBase(); }); diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly3.symbols b/tests/baselines/reference/fixingTypeParametersRepeatedly3.symbols new file mode 100644 index 0000000000..a849adb623 --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly3.symbols @@ -0,0 +1,73 @@ +=== tests/cases/compiler/fixingTypeParametersRepeatedly3.ts === +interface Base { +>Base : Symbol(Base, Decl(fixingTypeParametersRepeatedly3.ts, 0, 0)) + + baseProp; +>baseProp : Symbol(baseProp, Decl(fixingTypeParametersRepeatedly3.ts, 0, 16)) +} +interface Derived extends Base { +>Derived : Symbol(Derived, Decl(fixingTypeParametersRepeatedly3.ts, 2, 1)) +>Base : Symbol(Base, Decl(fixingTypeParametersRepeatedly3.ts, 0, 0)) + + toBase?(): Base; +>toBase : Symbol(toBase, Decl(fixingTypeParametersRepeatedly3.ts, 3, 32)) +>Base : Symbol(Base, Decl(fixingTypeParametersRepeatedly3.ts, 0, 0)) +} + +var derived: Derived; +>derived : Symbol(derived, Decl(fixingTypeParametersRepeatedly3.ts, 7, 3)) +>Derived : Symbol(Derived, Decl(fixingTypeParametersRepeatedly3.ts, 2, 1)) + +declare function foo(x: T, func: (p: T) => T): T; +>foo : Symbol(foo, Decl(fixingTypeParametersRepeatedly3.ts, 7, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 9, 21)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly3.ts, 9, 24)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 9, 21)) +>func : Symbol(func, Decl(fixingTypeParametersRepeatedly3.ts, 9, 29)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly3.ts, 9, 37)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 9, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 9, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 9, 21)) + +var result = foo(derived, d => d.toBase()); +>result : Symbol(result, Decl(fixingTypeParametersRepeatedly3.ts, 10, 3)) +>foo : Symbol(foo, Decl(fixingTypeParametersRepeatedly3.ts, 7, 21)) +>derived : Symbol(derived, Decl(fixingTypeParametersRepeatedly3.ts, 7, 3)) +>d : Symbol(d, Decl(fixingTypeParametersRepeatedly3.ts, 10, 25)) +>d.toBase : Symbol(Derived.toBase, Decl(fixingTypeParametersRepeatedly3.ts, 3, 32)) +>d : Symbol(d, Decl(fixingTypeParametersRepeatedly3.ts, 10, 25)) +>toBase : Symbol(Derived.toBase, Decl(fixingTypeParametersRepeatedly3.ts, 3, 32)) + +// bar should type check just like foo. +// result2 should have the same type as result +declare function bar(x: T, func: (p: T) => T): T; +>bar : Symbol(bar, Decl(fixingTypeParametersRepeatedly3.ts, 10, 43), Decl(fixingTypeParametersRepeatedly3.ts, 14, 52)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 14, 21)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly3.ts, 14, 24)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 14, 21)) +>func : Symbol(func, Decl(fixingTypeParametersRepeatedly3.ts, 14, 29)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly3.ts, 14, 37)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 14, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 14, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 14, 21)) + +declare function bar(x: T, func: (p: T) => T): T; +>bar : Symbol(bar, Decl(fixingTypeParametersRepeatedly3.ts, 10, 43), Decl(fixingTypeParametersRepeatedly3.ts, 14, 52)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 15, 21)) +>x : Symbol(x, Decl(fixingTypeParametersRepeatedly3.ts, 15, 24)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 15, 21)) +>func : Symbol(func, Decl(fixingTypeParametersRepeatedly3.ts, 15, 29)) +>p : Symbol(p, Decl(fixingTypeParametersRepeatedly3.ts, 15, 37)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 15, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 15, 21)) +>T : Symbol(T, Decl(fixingTypeParametersRepeatedly3.ts, 15, 21)) + +var result2 = bar(derived, d => d.toBase()); +>result2 : Symbol(result2, Decl(fixingTypeParametersRepeatedly3.ts, 16, 3)) +>bar : Symbol(bar, Decl(fixingTypeParametersRepeatedly3.ts, 10, 43), Decl(fixingTypeParametersRepeatedly3.ts, 14, 52)) +>derived : Symbol(derived, Decl(fixingTypeParametersRepeatedly3.ts, 7, 3)) +>d : Symbol(d, Decl(fixingTypeParametersRepeatedly3.ts, 16, 26)) +>d.toBase : Symbol(Derived.toBase, Decl(fixingTypeParametersRepeatedly3.ts, 3, 32)) +>d : Symbol(d, Decl(fixingTypeParametersRepeatedly3.ts, 16, 26)) +>toBase : Symbol(Derived.toBase, Decl(fixingTypeParametersRepeatedly3.ts, 3, 32)) + diff --git a/tests/baselines/reference/fixingTypeParametersRepeatedly3.types b/tests/baselines/reference/fixingTypeParametersRepeatedly3.types new file mode 100644 index 0000000000..3bf926276d --- /dev/null +++ b/tests/baselines/reference/fixingTypeParametersRepeatedly3.types @@ -0,0 +1,79 @@ +=== tests/cases/compiler/fixingTypeParametersRepeatedly3.ts === +interface Base { +>Base : Base + + baseProp; +>baseProp : any +} +interface Derived extends Base { +>Derived : Derived +>Base : Base + + toBase?(): Base; +>toBase : () => Base +>Base : Base +} + +var derived: Derived; +>derived : Derived +>Derived : Derived + +declare function foo(x: T, func: (p: T) => T): T; +>foo : (x: T, func: (p: T) => T) => T +>T : T +>x : T +>T : T +>func : (p: T) => T +>p : T +>T : T +>T : T +>T : T + +var result = foo(derived, d => d.toBase()); +>result : Derived +>foo(derived, d => d.toBase()) : Derived +>foo : (x: T, func: (p: T) => T) => T +>derived : Derived +>d => d.toBase() : (d: Derived) => Base +>d : Derived +>d.toBase() : Base +>d.toBase : () => Base +>d : Derived +>toBase : () => Base + +// bar should type check just like foo. +// result2 should have the same type as result +declare function bar(x: T, func: (p: T) => T): T; +>bar : { (x: T, func: (p: T) => T): T; (x: T, func: (p: T) => T): T; } +>T : T +>x : T +>T : T +>func : (p: T) => T +>p : T +>T : T +>T : T +>T : T + +declare function bar(x: T, func: (p: T) => T): T; +>bar : { (x: T, func: (p: T) => T): T; (x: T, func: (p: T) => T): T; } +>T : T +>x : T +>T : T +>func : (p: T) => T +>p : T +>T : T +>T : T +>T : T + +var result2 = bar(derived, d => d.toBase()); +>result2 : Derived +>bar(derived, d => d.toBase()) : Derived +>bar : { (x: T, func: (p: T) => T): T; (x: T, func: (p: T) => T): T; } +>derived : Derived +>d => d.toBase() : (d: Derived) => Base +>d : Derived +>d.toBase() : Base +>d.toBase : () => Base +>d : Derived +>toBase : () => Base + diff --git a/tests/baselines/reference/parenthesizedContexualTyping1.types b/tests/baselines/reference/parenthesizedContexualTyping1.types index b7307eaf3e..61ec1a24ec 100644 --- a/tests/baselines/reference/parenthesizedContexualTyping1.types +++ b/tests/baselines/reference/parenthesizedContexualTyping1.types @@ -146,8 +146,8 @@ var h = fun((((x => x))), ((x => x)), 10); // Ternaries in parens var i = fun((Math.random() < 0.5 ? x => x : x => undefined), 10); ->i : any ->fun((Math.random() < 0.5 ? x => x : x => undefined), 10) : any +>i : number +>fun((Math.random() < 0.5 ? x => x : x => undefined), 10) : number >fun : { (g: (x: T) => T, x: T): T; (g: (x: T) => T, h: (y: T) => T, x: T): T; } >(Math.random() < 0.5 ? x => x : x => undefined) : (x: number) => any >Math.random() < 0.5 ? x => x : x => undefined : (x: number) => any @@ -166,8 +166,8 @@ var i = fun((Math.random() < 0.5 ? x => x : x => undefined), 10); >10 : number var j = fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), 10); ->j : any ->fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), 10) : any +>j : number +>fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), 10) : number >fun : { (g: (x: T) => T, x: T): T; (g: (x: T) => T, h: (y: T) => T, x: T): T; } >(Math.random() < 0.5 ? (x => x) : (x => undefined)) : (x: number) => any >Math.random() < 0.5 ? (x => x) : (x => undefined) : (x: number) => any @@ -188,8 +188,8 @@ var j = fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), 10); >10 : number var k = fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), x => x, 10); ->k : any ->fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), x => x, 10) : any +>k : number +>fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), x => x, 10) : number >fun : { (g: (x: T) => T, x: T): T; (g: (x: T) => T, h: (y: T) => T, x: T): T; } >(Math.random() < 0.5 ? (x => x) : (x => undefined)) : (x: number) => any >Math.random() < 0.5 ? (x => x) : (x => undefined) : (x: number) => any @@ -207,14 +207,14 @@ var k = fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), x => x, 10); >x => undefined : (x: number) => any >x : number >undefined : undefined ->x => x : (x: any) => any ->x : any ->x : any +>x => x : (x: number) => number +>x : number +>x : number >10 : number var l = fun(((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))), ((x => x)), 10); ->l : any ->fun(((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))), ((x => x)), 10) : any +>l : number +>fun(((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))), ((x => x)), 10) : number >fun : { (g: (x: T) => T, x: T): T; (g: (x: T) => T, h: (y: T) => T, x: T): T; } >((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))) : (x: number) => any >(Math.random() < 0.5 ? ((x => x)) : ((x => undefined))) : (x: number) => any @@ -235,11 +235,11 @@ var l = fun(((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))), ((x => x) >x => undefined : (x: number) => any >x : number >undefined : undefined ->((x => x)) : (x: any) => any ->(x => x) : (x: any) => any ->x => x : (x: any) => any ->x : any ->x : any +>((x => x)) : (x: number) => number +>(x => x) : (x: number) => number +>x => x : (x: number) => number +>x : number +>x : number >10 : number var lambda1: (x: number) => number = x => x; diff --git a/tests/cases/compiler/fixingTypeParametersRepeatedly1.ts b/tests/cases/compiler/fixingTypeParametersRepeatedly1.ts new file mode 100644 index 0000000000..d02e434c61 --- /dev/null +++ b/tests/cases/compiler/fixingTypeParametersRepeatedly1.ts @@ -0,0 +1,7 @@ +declare function f(x: T, y: (p: T) => T, z: (p: T) => T): T; +f("", x => null, x => x.toLowerCase()); + +// First overload of g should type check just like f +declare function g(x: T, y: (p: T) => T, z: (p: T) => T): T; +declare function g(); +g("", x => null, x => x.toLowerCase()); \ No newline at end of file diff --git a/tests/cases/compiler/fixingTypeParametersRepeatedly2.ts b/tests/cases/compiler/fixingTypeParametersRepeatedly2.ts new file mode 100644 index 0000000000..b439838862 --- /dev/null +++ b/tests/cases/compiler/fixingTypeParametersRepeatedly2.ts @@ -0,0 +1,17 @@ +interface Base { + baseProp; +} +interface Derived extends Base { + toBase(): Base; +} + +var derived: Derived; + +declare function foo(x: T, func: (p: T) => T): T; +var result = foo(derived, d => d.toBase()); + +// bar should type check just like foo. +// The same error should be observed in both cases. +declare function bar(x: T, func: (p: T) => T): T; +declare function bar(x: T, func: (p: T) => T): T; +var result = bar(derived, d => d.toBase()); \ No newline at end of file diff --git a/tests/cases/compiler/fixingTypeParametersRepeatedly3.ts b/tests/cases/compiler/fixingTypeParametersRepeatedly3.ts new file mode 100644 index 0000000000..1ffba2e50c --- /dev/null +++ b/tests/cases/compiler/fixingTypeParametersRepeatedly3.ts @@ -0,0 +1,17 @@ +interface Base { + baseProp; +} +interface Derived extends Base { + toBase?(): Base; +} + +var derived: Derived; + +declare function foo(x: T, func: (p: T) => T): T; +var result = foo(derived, d => d.toBase()); + +// bar should type check just like foo. +// result2 should have the same type as result +declare function bar(x: T, func: (p: T) => T): T; +declare function bar(x: T, func: (p: T) => T): T; +var result2 = bar(derived, d => d.toBase()); \ No newline at end of file diff --git a/tests/cases/fourslash/fixingTypeParametersQuickInfo.ts b/tests/cases/fourslash/fixingTypeParametersQuickInfo.ts new file mode 100644 index 0000000000..033663052b --- /dev/null +++ b/tests/cases/fourslash/fixingTypeParametersQuickInfo.ts @@ -0,0 +1,13 @@ +/// + +////declare function f(x: T, y: (p: T) => T, z: (p: T) => T): T; +////var /*1*/result = /*2*/f(0, /*3*/x => null, /*4*/x => x.blahblah); + +goTo.marker('1'); +verify.quickInfoIs('var result: number'); +goTo.marker('2'); +verify.quickInfoIs('function f(x: number, y: (p: number) => number, z: (p: number) => number): number'); +goTo.marker('3'); +verify.quickInfoIs('(parameter) x: number'); +goTo.marker('4'); +verify.quickInfoIs('(parameter) x: number'); \ No newline at end of file