Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check applicable signature fix #16104

Closed
wants to merge 10 commits into from
Closed
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
37 changes: 34 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ namespace ts {
const potentialNewTargetCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];

const fakeInferenceMapper = createFakeInferenceMapper();

const diagnostics = createDiagnosticCollection();

const enum TypeFacts {
Expand Down Expand Up @@ -7967,6 +7969,13 @@ namespace ts {
return type;
}

function createFakeInferenceMapper(): TypeMapper {
const fakeSignature = <Signature>{
typeParameters: []
};
return createInferenceContext(fakeSignature, 0);
}

function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
Expand Down Expand Up @@ -14880,12 +14889,34 @@ namespace ts {

// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
const context = createInferenceContext(signature, 0);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context.inferences, instantiateType(source, contextualMapper), target);
});
return getSignatureInstantiation(signature, getInferredTypes(context));
// If contextualMapper is fakeInferenceMapper we are being called by checkApplicableSignature.
if (contextualMapper === fakeInferenceMapper) {
let source, target;
if (contextualSignature.typePredicate && signature.typePredicate && contextualSignature.typePredicate.kind === signature.typePredicate.kind) {
source = contextualSignature.typePredicate.type;
target = signature.typePredicate.type;
}
else {
source = getReturnTypeOfSignature(contextualSignature);
target = getReturnTypeOfSignature(signature);
}
// source is already instantiated by the caller of checkApplicableSignature.
inferTypes(context.inferences, source, target);
}
const inferred = getInferredTypes(context);
for (let i = 0; i < inferred.length; ++i) {
// If inference has failed, use the first constituent type. During checking, the other
// constituents will fail to match, resulting in a nice error message pointing it out.
if (inferred[i] === unknownType) {
inferred[i] = context.inferences[i].candidates[0] || inferred[i];
}
}
return getSignatureInstantiation(signature, inferred);
}

function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): Type[] {
Expand Down Expand Up @@ -15073,7 +15104,7 @@ namespace ts {
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
if (argType === undefined) {
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : fakeInferenceMapper);
}

// Use argument expression as error location when reporting errors
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
tests/cases/conformance/types/typeRelationships/typeInference/contextualSignatureInstantiation.ts(19,13): error TS2345: Argument of type '(x: number, y: number) => number' is not assignable to parameter of type '(x: number, y: string) => number'.
Types of parameters 'y' and 'y' are incompatible.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/typeRelationships/typeInference/contextualSignatureInstantiation.ts(20,23): error TS2345: Argument of type '(x: number, y: number) => number' is not assignable to parameter of type '(x: number, y: string) => number'.
Types of parameters 'y' and 'y' are incompatible.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/typeRelationships/typeInference/contextualSignatureInstantiation.ts(21,23): error TS2345: Argument of type '(x: string, y: string) => string' is not assignable to parameter of type '(x: string, y: number) => string'.
Types of parameters 'y' and 'y' are incompatible.
Type 'number' is not assignable to type 'string'.


==== tests/cases/conformance/types/typeRelationships/typeInference/contextualSignatureInstantiation.ts (3 errors) ====
// TypeScript Spec, section 4.12.2:
// If e is an expression of a function type that contains exactly one generic call signature and no other members,
// and T is a function type with exactly one non - generic call signature and no other members, then any inferences
// made for type parameters referenced by the parameters of T's call signature are fixed, and e's type is changed
// to a function type with e's call signature instantiated in the context of T's call signature (section 3.8.5).

declare function foo<T>(cb: (x: number, y: string) => T): T;
declare function bar<T, U, V>(x: T, y: U, cb: (x: T, y: U) => V): V;
declare function baz<T, U>(x: T, y: T, cb: (x: T, y: T) => U): U;

declare function g<T>(x: T, y: T): T;
declare function h<T, U>(x: T, y: U): T[] | U[];

var a: number;
var a = bar(1, 1, g); // Should be number
var a = baz(1, 1, g); // Should be number

var b: number | string;
var b = foo(g); // Should error
~
!!! error TS2345: Argument of type '(x: number, y: number) => number' is not assignable to parameter of type '(x: number, y: string) => number'.
!!! error TS2345: Types of parameters 'y' and 'y' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
var b = bar(1, "one", g); // Should error
~
!!! error TS2345: Argument of type '(x: number, y: number) => number' is not assignable to parameter of type '(x: number, y: string) => number'.
!!! error TS2345: Types of parameters 'y' and 'y' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
var b = bar("one", 1, g); // Should error
~
!!! error TS2345: Argument of type '(x: string, y: string) => string' is not assignable to parameter of type '(x: string, y: number) => string'.
!!! error TS2345: Types of parameters 'y' and 'y' are incompatible.
!!! error TS2345: Type 'number' is not assignable to type 'string'.
var b = baz(b, b, g); // Should be number | string

var d: number[] | string[];
var d = foo(h); // Should be number[] | string[]
var d = bar(1, "one", h); // Should be number[] | string[]
var d = bar("one", 1, h); // Should be number[] | string[]
var d = baz(d, d, g); // Should be number[] | string[]

12 changes: 6 additions & 6 deletions tests/baselines/reference/contextualSignatureInstantiation.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ var a = bar(1, 1, g); // Should be number
var a = baz(1, 1, g); // Should be number

var b: number | string;
var b = foo(g); // Should be number | string
var b = bar(1, "one", g); // Should be number | string
var b = bar("one", 1, g); // Should be number | string
var b = foo(g); // Should error
var b = bar(1, "one", g); // Should error
var b = bar("one", 1, g); // Should error
var b = baz(b, b, g); // Should be number | string

var d: number[] | string[];
Expand All @@ -39,9 +39,9 @@ var a;
var a = bar(1, 1, g); // Should be number
var a = baz(1, 1, g); // Should be number
var b;
var b = foo(g); // Should be number | string
var b = bar(1, "one", g); // Should be number | string
var b = bar("one", 1, g); // Should be number | string
var b = foo(g); // Should error
var b = bar(1, "one", g); // Should error
var b = bar("one", 1, g); // Should error
var b = baz(b, b, g); // Should be number | string
var d;
var d = foo(h); // Should be number[] | string[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstrain
Type 'typeof C' provides no match for the signature '(x: string): string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(26,15): error TS2345: Argument of type 'new (x: string) => string' is not assignable to parameter of type '(x: string) => string'.
Type 'new (x: string) => string' provides no match for the signature '(x: string): string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(28,16): error TS2345: Argument of type '<U, V>(x: U, y: V) => U' is not assignable to parameter of type '(x: string) => string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(28,16): error TS2345: Argument of type '(x: string, y: {}) => string' is not assignable to parameter of type '(x: string) => string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(29,16): error TS2345: Argument of type 'typeof C2' is not assignable to parameter of type '(x: string) => string'.
Type 'typeof C2' provides no match for the signature '(x: string): string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(30,16): error TS2345: Argument of type 'new <T>(x: T) => T' is not assignable to parameter of type '(x: string) => string'.
Expand Down Expand Up @@ -75,7 +75,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstrain
var r8 = foo2(<U>(x: U) => x); // no error expected
var r11 = foo2(<U, V>(x: U, y: V) => x);
~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '<U, V>(x: U, y: V) => U' is not assignable to parameter of type '(x: string) => string'.
!!! error TS2345: Argument of type '(x: string, y: {}) => string' is not assignable to parameter of type '(x: string) => string'.
var r13 = foo2(C2);
~~
!!! error TS2345: Argument of type 'typeof C2' is not assignable to parameter of type '(x: string) => string'.
Expand Down
109 changes: 109 additions & 0 deletions tests/baselines/reference/functionConstraintSatisfaction3.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction3.ts(50,6): error TS2345: Argument of type '(x: string, y: string) => string' is not assignable to parameter of type '(x: string, y: number) => string'.
Types of parameters 'y' and 'y' are incompatible.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction3.ts(51,7): error TS2345: Argument of type '(x: string, y: string) => string' is not assignable to parameter of type '(x: string, y: number) => string'.
Types of parameters 'y' and 'y' are incompatible.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction3.ts(59,12): error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => string'.
Types of parameters 'x' and 'x' are incompatible.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction3.ts(60,11): error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => string'.
Types of parameters 'x' and 'x' are incompatible.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction3.ts(70,17): error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => string'.
Types of parameters 'x' and 'x' are incompatible.
Type 'string' is not assignable to type 'number'.


==== tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction3.ts (5 errors) ====
// satisfaction of a constraint to Function, no errors expected

function foo<T extends (x: string) => string>(x: T): T { return x; }

interface I {
(): string;
}
var i: I;

class C {
foo: string;
}

var a: { (): string };
var b: { new (): string };
var c: { (): string; (x): string };

var r1 = foo((x) => x);
var r2 = foo((x: string) => x);
var r3 = foo(function (x) { return x });
var r4 = foo(function (x: string) { return x });
var r5 = foo(i);
var r8 = foo(c);

interface I2<T> {
(x: T): T;
}
var i2: I2<string>;

class C2<T> {
foo: T;
}

var a2: { <T>(x: T): T };
var b2: { new <T>(x: T): T };
var c2: { <T>(x: T): T; <T>(x: T, y: T): T };

var r9 = foo(function <U>(x: U) { return x; });
var r10 = foo(<U extends string>(x: U) => x);
var r12 = foo(i2);
var r15 = foo(c2);

declare function id2<T>(x: T, y: T): T;
declare function id3<T>(x: T, y: T, z: T): T;

declare function boom<R>(f: (x: string, y: number) => R): R;
declare function boom2(f: (x: string, y: number) => string): void;
declare function boom3<R>(f: (x: string, y: number, z: R) => R): R;

boom(id2); // Should be an error T = [string, number]
~~~
!!! error TS2345: Argument of type '(x: string, y: string) => string' is not assignable to parameter of type '(x: string, y: number) => string'.
!!! error TS2345: Types of parameters 'y' and 'y' are incompatible.
!!! error TS2345: Type 'number' is not assignable to type 'string'.
boom2(id2); // Should be an error T = [string, number]
~~~
!!! error TS2345: Argument of type '(x: string, y: string) => string' is not assignable to parameter of type '(x: string, y: number) => string'.
!!! error TS2345: Types of parameters 'y' and 'y' are incompatible.
!!! error TS2345: Type 'number' is not assignable to type 'string'.
boom<string|number>(id2); // Should be OK
boom3<string|number>(id3); // Should be OK

declare function withNum<N extends number>(x: N): N;
declare function withString<S extends string>(f: (x: S) => S): void;
declare function useString(f: (x: string) => string): void;

withString(withNum); // Error
~~~~~~~
!!! error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => string'.
!!! error TS2345: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
useString(withNum); // Error
~~~~~~~
!!! error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => string'.
!!! error TS2345: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'number'.

declare function okay<R>(f: (x: 1, y: number) => R): R;
declare function okay2(f: (x: string, y: number) => string|number);
declare function transitive<T>(x: T, f: (x: T) => T): void;

okay(id2);
okay2(id2);

transitive(1, withNum);
transitive('1', withNum);
~~~~~~~
!!! error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => string'.
!!! error TS2345: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'number'.

42 changes: 41 additions & 1 deletion tests/baselines/reference/functionConstraintSatisfaction3.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,37 @@ var c2: { <T>(x: T): T; <T>(x: T, y: T): T };
var r9 = foo(function <U>(x: U) { return x; });
var r10 = foo(<U extends string>(x: U) => x);
var r12 = foo(i2);
var r15 = foo(c2);
var r15 = foo(c2);

declare function id2<T>(x: T, y: T): T;
declare function id3<T>(x: T, y: T, z: T): T;

declare function boom<R>(f: (x: string, y: number) => R): R;
declare function boom2(f: (x: string, y: number) => string): void;
declare function boom3<R>(f: (x: string, y: number, z: R) => R): R;

boom(id2); // Should be an error T = [string, number]
boom2(id2); // Should be an error T = [string, number]
boom<string|number>(id2); // Should be OK
boom3<string|number>(id3); // Should be OK

declare function withNum<N extends number>(x: N): N;
declare function withString<S extends string>(f: (x: S) => S): void;
declare function useString(f: (x: string) => string): void;

withString(withNum); // Error
useString(withNum); // Error

declare function okay<R>(f: (x: 1, y: number) => R): R;
declare function okay2(f: (x: string, y: number) => string|number);
declare function transitive<T>(x: T, f: (x: T) => T): void;

okay(id2);
okay2(id2);

transitive(1, withNum);
transitive('1', withNum);


//// [functionConstraintSatisfaction3.js]
// satisfaction of a constraint to Function, no errors expected
Expand Down Expand Up @@ -72,3 +102,13 @@ var r9 = foo(function (x) { return x; });
var r10 = foo(function (x) { return x; });
var r12 = foo(i2);
var r15 = foo(c2);
boom(id2); // Should be an error T = [string, number]
boom2(id2); // Should be an error T = [string, number]
boom(id2); // Should be OK
boom3(id3); // Should be OK
withString(withNum); // Error
useString(withNum); // Error
okay(id2);
okay2(id2);
transitive(1, withNum);
transitive('1', withNum);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
tests/cases/compiler/genericArgumentCallSigAssignmentCompat.ts(16,31): error TS2345: Argument of type '(value: string | number | boolean) => string | number | boolean' is not assignable to parameter of type 'Iterator<string | number | boolean, boolean>'.
Type 'string | number | boolean' is not assignable to type 'boolean'.
Type 'string' is not assignable to type 'boolean'.


==== tests/cases/compiler/genericArgumentCallSigAssignmentCompat.ts (1 errors) ====
module Underscore {
export interface Iterator<T, U> {
(value: T, index: any, list: any): U;
}

export interface Static {
all<T>(list: T[], iterator?: Iterator<T, boolean>, context?: any): boolean;
identity<T>(value: T): T;
}
}

declare var _: Underscore.Static;

// No error, Call signatures of types '<T>(value: T) => T' and 'Underscore.Iterator<{}, boolean>' are compatible when instantiated with any.
// Ideally, we would not have a generic signature here, because it should be instantiated with {} during inferential typing
_.all([true, 1, null, 'yes'], _.identity);
~~~~~~~~~~
!!! error TS2345: Argument of type '(value: string | number | boolean) => string | number | boolean' is not assignable to parameter of type 'Iterator<string | number | boolean, boolean>'.
!!! error TS2345: Type 'string | number | boolean' is not assignable to type 'boolean'.
!!! error TS2345: Type 'string' is not assignable to type 'boolean'.

// Ok, because fixing makes us infer boolean for T
_.all([true], _.identity);

Loading