Merge pull request #908 from Microsoft/contextualSignatureUnionTypes

Union types in contextual signature instantiations
This commit is contained in:
Anders Hejlsberg
2014-10-18 08:47:41 -07:00
5 changed files with 228 additions and 7 deletions

View File

@@ -3758,11 +3758,12 @@ module ts {
}
}
function createInferenceContext(typeParameters: TypeParameter[]): InferenceContext {
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
var inferences: Type[][] = [];
for (var i = 0; i < typeParameters.length; i++) inferences.push([]);
return {
typeParameters: typeParameters,
inferUnionTypes: inferUnionTypes,
inferenceCount: 0,
inferences: inferences,
inferredTypes: new Array(typeParameters.length),
@@ -3909,10 +3910,9 @@ module ts {
if (!result) {
var inferences = context.inferences[index];
if (inferences.length) {
// Find type that is supertype of all others
var supertype = getCommonSupertype(inferences);
// Infer widened supertype, or the undefined type for no common supertype
var inferredType = supertype ? getWidenedType(supertype) : undefinedType;
// Infer widened union or supertype, or the undefined type for no common supertype
var unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences) : getCommonSupertype(inferences);
var inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : undefinedType;
}
else {
// Infer the empty object type when no inferences were made
@@ -4976,7 +4976,7 @@ module 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 {
var context = createInferenceContext(signature.typeParameters);
var context = createInferenceContext(signature.typeParameters, /*inferUnionTypes*/ true);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context, instantiateType(source, contextualMapper), target);
@@ -4986,7 +4986,7 @@ module ts {
function inferTypeArguments(signature: Signature, args: Expression[], excludeArgument?: boolean[]): Type[] {
var typeParameters = signature.typeParameters;
var context = createInferenceContext(typeParameters);
var context = createInferenceContext(typeParameters, /*inferUnionTypes*/ false);
var mapper = createInferenceMapper(context);
// First infer from arguments that are not context sensitive
for (var i = 0; i < args.length; i++) {

View File

@@ -1010,6 +1010,7 @@ module ts {
export interface InferenceContext {
typeParameters: TypeParameter[]; // Type parameters for which inferences are made
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
inferenceCount: number; // Incremented for every inference made (whether new or not)
inferences: Type[][]; // Inferences made for each type parameter
inferredTypes: Type[]; // Inferred type for each type parameter

View File

@@ -0,0 +1,50 @@
//// [contextualSignatureInstantiation.ts]
// 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 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 = 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[]
//// [contextualSignatureInstantiation.js]
// 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).
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 = baz(b, b, g); // Should be number | string
var d;
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[]

View File

@@ -0,0 +1,142 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/contextualSignatureInstantiation.ts ===
// 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;
>foo : <T>(cb: (x: number, y: string) => T) => T
>T : T
>cb : (x: number, y: string) => T
>x : number
>y : string
>T : T
>T : T
declare function bar<T, U, V>(x: T, y: U, cb: (x: T, y: U) => V): V;
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
>T : T
>U : U
>V : V
>x : T
>T : T
>y : U
>U : U
>cb : (x: T, y: U) => V
>x : T
>T : T
>y : U
>U : U
>V : V
>V : V
declare function baz<T, U>(x: T, y: T, cb: (x: T, y: T) => U): U;
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
>T : T
>U : U
>x : T
>T : T
>y : T
>T : T
>cb : (x: T, y: T) => U
>x : T
>T : T
>y : T
>T : T
>U : U
>U : U
declare function g<T>(x: T, y: T): T;
>g : <T>(x: T, y: T) => T
>T : T
>x : T
>T : T
>y : T
>T : T
>T : T
declare function h<T, U>(x: T, y: U): T[] | U[];
>h : <T, U>(x: T, y: U) => T[] | U[]
>T : T
>U : U
>x : T
>T : T
>y : U
>U : U
>T : T
>U : U
var a: number;
>a : number
var a = bar(1, 1, g); // Should be number
>a : number
>bar(1, 1, g) : number
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
>g : <T>(x: T, y: T) => T
var a = baz(1, 1, g); // Should be number
>a : number
>baz(1, 1, g) : number
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
>g : <T>(x: T, y: T) => T
var b: number | string;
>b : string | number
var b = foo(g); // Should be number | string
>b : string | number
>foo(g) : string | number
>foo : <T>(cb: (x: number, y: string) => T) => T
>g : <T>(x: T, y: T) => T
var b = bar(1, "one", g); // Should be number | string
>b : string | number
>bar(1, "one", g) : string | number
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
>g : <T>(x: T, y: T) => T
var b = bar("one", 1, g); // Should be number | string
>b : string | number
>bar("one", 1, g) : string | number
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
>g : <T>(x: T, y: T) => T
var b = baz(b, b, g); // Should be number | string
>b : string | number
>baz(b, b, g) : string | number
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
>b : string | number
>b : string | number
>g : <T>(x: T, y: T) => T
var d: number[] | string[];
>d : string[] | number[]
var d = foo(h); // Should be number[] | string[]
>d : string[] | number[]
>foo(h) : string[] | number[]
>foo : <T>(cb: (x: number, y: string) => T) => T
>h : <T, U>(x: T, y: U) => T[] | U[]
var d = bar(1, "one", h); // Should be number[] | string[]
>d : string[] | number[]
>bar(1, "one", h) : string[] | number[]
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
>h : <T, U>(x: T, y: U) => T[] | U[]
var d = bar("one", 1, h); // Should be number[] | string[]
>d : string[] | number[]
>bar("one", 1, h) : string[] | number[]
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
>h : <T, U>(x: T, y: U) => T[] | U[]
var d = baz(d, d, g); // Should be number[] | string[]
>d : string[] | number[]
>baz(d, d, g) : string[] | number[]
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
>d : string[] | number[]
>d : string[] | number[]
>g : <T>(x: T, y: T) => T

View File

@@ -0,0 +1,28 @@
// 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 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 = 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[]