mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-17 16:30:39 -06:00
Merge pull request #1035 from Microsoft/improvedTypeInference
Improved type inference (fixes #1011)
This commit is contained in:
commit
d8080a0b46
@ -4007,8 +4007,10 @@ module ts {
|
||||
}
|
||||
|
||||
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
|
||||
var inferences: Type[][] = [];
|
||||
for (var i = 0; i < typeParameters.length; i++) inferences.push([]);
|
||||
var inferences: TypeInferences[] = [];
|
||||
for (var i = 0; i < typeParameters.length; i++) {
|
||||
inferences.push({ primary: undefined, secondary: undefined });
|
||||
}
|
||||
return {
|
||||
typeParameters: typeParameters,
|
||||
inferUnionTypes: inferUnionTypes,
|
||||
@ -4022,6 +4024,7 @@ module ts {
|
||||
var sourceStack: Type[];
|
||||
var targetStack: Type[];
|
||||
var depth = 0;
|
||||
var inferiority = 0;
|
||||
inferFromTypes(source, target);
|
||||
|
||||
function isInProcess(source: Type, target: Type) {
|
||||
@ -4050,9 +4053,11 @@ module ts {
|
||||
var typeParameters = context.typeParameters;
|
||||
for (var i = 0; i < typeParameters.length; i++) {
|
||||
if (target === typeParameters[i]) {
|
||||
context.inferenceCount++;
|
||||
var inferences = context.inferences[i];
|
||||
if (!contains(inferences, source)) inferences.push(source);
|
||||
var candidates = inferiority ?
|
||||
inferences.secondary || (inferences.secondary = []) :
|
||||
inferences.primary || (inferences.primary = []);
|
||||
if (!contains(candidates, source)) candidates.push(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4067,7 +4072,6 @@ module ts {
|
||||
}
|
||||
else if (target.flags & TypeFlags.Union) {
|
||||
var targetTypes = (<UnionType>target).types;
|
||||
var startCount = context.inferenceCount;
|
||||
var typeParameterCount = 0;
|
||||
var typeParameter: TypeParameter;
|
||||
// First infer to each type in union that isn't a type parameter
|
||||
@ -4081,9 +4085,11 @@ module ts {
|
||||
inferFromTypes(source, t);
|
||||
}
|
||||
}
|
||||
// If no inferences were produced above and union contains a single naked type parameter, infer to that type parameter
|
||||
if (context.inferenceCount === startCount && typeParameterCount === 1) {
|
||||
// If union contains a single naked type parameter, make a secondary inference to that type parameter
|
||||
if (typeParameterCount === 1) {
|
||||
inferiority++;
|
||||
inferFromTypes(source, typeParameter);
|
||||
inferiority--;
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Union) {
|
||||
@ -4153,10 +4159,15 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getInferenceCandidates(context: InferenceContext, index: number): Type[]{
|
||||
var inferences = context.inferences[index];
|
||||
return inferences.primary || inferences.secondary || emptyArray;
|
||||
}
|
||||
|
||||
function getInferredType(context: InferenceContext, index: number): Type {
|
||||
var inferredType = context.inferredTypes[index];
|
||||
if (!inferredType) {
|
||||
var inferences = context.inferences[index];
|
||||
var inferences = getInferenceCandidates(context, index);
|
||||
if (inferences.length) {
|
||||
// Infer widened union or supertype, or the undefined type for no common supertype
|
||||
var unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences) : getCommonSupertype(inferences);
|
||||
@ -4166,7 +4177,6 @@ module ts {
|
||||
// Infer the empty object type when no inferences were made
|
||||
inferredType = emptyObjectType;
|
||||
}
|
||||
|
||||
if (inferredType !== inferenceFailureType) {
|
||||
var constraint = getConstraintOfTypeParameter(context.typeParameters[index]);
|
||||
inferredType = constraint && !isTypeAssignableTo(inferredType, constraint) ? constraint : inferredType;
|
||||
@ -5404,7 +5414,7 @@ module ts {
|
||||
else {
|
||||
Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0);
|
||||
var failedTypeParameter = candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failedTypeParameterIndex];
|
||||
var inferenceCandidates = resultOfFailedInference.inferences[resultOfFailedInference.failedTypeParameterIndex];
|
||||
var inferenceCandidates = getInferenceCandidates(resultOfFailedInference, resultOfFailedInference.failedTypeParameterIndex);
|
||||
|
||||
var diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError
|
||||
Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly,
|
||||
|
||||
@ -1041,14 +1041,18 @@ module ts {
|
||||
(t: Type): Type;
|
||||
}
|
||||
|
||||
export interface TypeInferences {
|
||||
primary: Type[]; // Inferences made directly to a type parameter
|
||||
secondary: Type[]; // Inferences made to a type parameter in a union type
|
||||
}
|
||||
|
||||
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
|
||||
failedTypeParameterIndex?: number; // Index of type parameter for which inference failed
|
||||
// It is optional because in contextual signature instantiation, nothing fails
|
||||
typeParameters: TypeParameter[]; // Type parameters for which inferences are made
|
||||
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
|
||||
inferences: TypeInferences[]; // Inferences made for each type parameter
|
||||
inferredTypes: Type[]; // Inferred type for each type parameter
|
||||
failedTypeParameterIndex?: number; // Index of type parameter for which inference failed
|
||||
// It is optional because in contextual signature instantiation, nothing fails
|
||||
}
|
||||
|
||||
export interface DiagnosticMessage {
|
||||
|
||||
60
tests/baselines/reference/unionTypeInference.js
Normal file
60
tests/baselines/reference/unionTypeInference.js
Normal file
@ -0,0 +1,60 @@
|
||||
//// [unionTypeInference.ts]
|
||||
// Verify that inferences made *to* a type parameter in a union type are secondary
|
||||
// to inferences made directly to that type parameter
|
||||
|
||||
function f<T>(x: T, y: string|T): T {
|
||||
return x;
|
||||
}
|
||||
|
||||
var a1: number;
|
||||
var a1 = f(1, 2);
|
||||
var a2: number;
|
||||
var a2 = f(1, "hello");
|
||||
var a3: number;
|
||||
var a3 = f(1, a1 || "hello");
|
||||
var a4: any;
|
||||
var a4 = f(undefined, "abc");
|
||||
|
||||
function g<T>(value: [string, T]): T {
|
||||
return value[1];
|
||||
}
|
||||
|
||||
var b1: boolean;
|
||||
var b1 = g(["string", true]);
|
||||
|
||||
function h<T>(x: string|boolean|T): T {
|
||||
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
|
||||
}
|
||||
|
||||
var c1: number;
|
||||
var c1 = h(5);
|
||||
var c2: string;
|
||||
var c2 = h("abc");
|
||||
|
||||
|
||||
//// [unionTypeInference.js]
|
||||
// Verify that inferences made *to* a type parameter in a union type are secondary
|
||||
// to inferences made directly to that type parameter
|
||||
function f(x, y) {
|
||||
return x;
|
||||
}
|
||||
var a1;
|
||||
var a1 = f(1, 2);
|
||||
var a2;
|
||||
var a2 = f(1, "hello");
|
||||
var a3;
|
||||
var a3 = f(1, a1 || "hello");
|
||||
var a4;
|
||||
var a4 = f(undefined, "abc");
|
||||
function g(value) {
|
||||
return value[1];
|
||||
}
|
||||
var b1;
|
||||
var b1 = g(["string", true]);
|
||||
function h(x) {
|
||||
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
|
||||
}
|
||||
var c1;
|
||||
var c1 = h(5);
|
||||
var c2;
|
||||
var c2 = h("abc");
|
||||
109
tests/baselines/reference/unionTypeInference.types
Normal file
109
tests/baselines/reference/unionTypeInference.types
Normal file
@ -0,0 +1,109 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/typeInference/unionTypeInference.ts ===
|
||||
// Verify that inferences made *to* a type parameter in a union type are secondary
|
||||
// to inferences made directly to that type parameter
|
||||
|
||||
function f<T>(x: T, y: string|T): T {
|
||||
>f : <T>(x: T, y: string | T) => T
|
||||
>T : T
|
||||
>x : T
|
||||
>T : T
|
||||
>y : string | T
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
return x;
|
||||
>x : T
|
||||
}
|
||||
|
||||
var a1: number;
|
||||
>a1 : number
|
||||
|
||||
var a1 = f(1, 2);
|
||||
>a1 : number
|
||||
>f(1, 2) : number
|
||||
>f : <T>(x: T, y: string | T) => T
|
||||
|
||||
var a2: number;
|
||||
>a2 : number
|
||||
|
||||
var a2 = f(1, "hello");
|
||||
>a2 : number
|
||||
>f(1, "hello") : number
|
||||
>f : <T>(x: T, y: string | T) => T
|
||||
|
||||
var a3: number;
|
||||
>a3 : number
|
||||
|
||||
var a3 = f(1, a1 || "hello");
|
||||
>a3 : number
|
||||
>f(1, a1 || "hello") : number
|
||||
>f : <T>(x: T, y: string | T) => T
|
||||
>a1 || "hello" : string | number
|
||||
>a1 : number
|
||||
|
||||
var a4: any;
|
||||
>a4 : any
|
||||
|
||||
var a4 = f(undefined, "abc");
|
||||
>a4 : any
|
||||
>f(undefined, "abc") : any
|
||||
>f : <T>(x: T, y: string | T) => T
|
||||
>undefined : undefined
|
||||
|
||||
function g<T>(value: [string, T]): T {
|
||||
>g : <T>(value: [string, T]) => T
|
||||
>T : T
|
||||
>value : [string, T]
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
return value[1];
|
||||
>value[1] : T
|
||||
>value : [string, T]
|
||||
}
|
||||
|
||||
var b1: boolean;
|
||||
>b1 : boolean
|
||||
|
||||
var b1 = g(["string", true]);
|
||||
>b1 : boolean
|
||||
>g(["string", true]) : boolean
|
||||
>g : <T>(value: [string, T]) => T
|
||||
>["string", true] : [string, boolean]
|
||||
|
||||
function h<T>(x: string|boolean|T): T {
|
||||
>h : <T>(x: string | boolean | T) => T
|
||||
>T : T
|
||||
>x : string | boolean | T
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
|
||||
>typeof x === "string" || typeof x === "boolean" ? undefined : x : T
|
||||
>typeof x === "string" || typeof x === "boolean" : boolean
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : string
|
||||
>x : string | boolean | T
|
||||
>typeof x === "boolean" : boolean
|
||||
>typeof x : string
|
||||
>x : boolean | T
|
||||
>undefined : undefined
|
||||
>x : T
|
||||
}
|
||||
|
||||
var c1: number;
|
||||
>c1 : number
|
||||
|
||||
var c1 = h(5);
|
||||
>c1 : number
|
||||
>h(5) : number
|
||||
>h : <T>(x: string | boolean | T) => T
|
||||
|
||||
var c2: string;
|
||||
>c2 : string
|
||||
|
||||
var c2 = h("abc");
|
||||
>c2 : string
|
||||
>h("abc") : string
|
||||
>h : <T>(x: string | boolean | T) => T
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
// Verify that inferences made *to* a type parameter in a union type are secondary
|
||||
// to inferences made directly to that type parameter
|
||||
|
||||
function f<T>(x: T, y: string|T): T {
|
||||
return x;
|
||||
}
|
||||
|
||||
var a1: number;
|
||||
var a1 = f(1, 2);
|
||||
var a2: number;
|
||||
var a2 = f(1, "hello");
|
||||
var a3: number;
|
||||
var a3 = f(1, a1 || "hello");
|
||||
var a4: any;
|
||||
var a4 = f(undefined, "abc");
|
||||
|
||||
function g<T>(value: [string, T]): T {
|
||||
return value[1];
|
||||
}
|
||||
|
||||
var b1: boolean;
|
||||
var b1 = g(["string", true]);
|
||||
|
||||
function h<T>(x: string|boolean|T): T {
|
||||
return typeof x === "string" || typeof x === "boolean" ? undefined : x;
|
||||
}
|
||||
|
||||
var c1: number;
|
||||
var c1 = h(5);
|
||||
var c2: string;
|
||||
var c2 = h("abc");
|
||||
Loading…
x
Reference in New Issue
Block a user