Skip to content

Commit 3a17b02

Browse files
committed
Improved type argument inference with union types
1 parent fd5b808 commit 3a17b02

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

src/compiler/checker.ts

+34-1
Original file line numberDiff line numberDiff line change
@@ -3720,6 +3720,7 @@ module ts {
37203720
for (var i = 0; i < typeParameters.length; i++) inferences.push([]);
37213721
return {
37223722
typeParameters: typeParameters,
3723+
inferenceCount: 0,
37233724
inferences: inferences,
37243725
inferredTypes: new Array(typeParameters.length),
37253726
};
@@ -3757,6 +3758,7 @@ module ts {
37573758
var typeParameters = context.typeParameters;
37583759
for (var i = 0; i < typeParameters.length; i++) {
37593760
if (target === typeParameters[i]) {
3761+
context.inferenceCount++;
37603762
var inferences = context.inferences[i];
37613763
if (!contains(inferences, source)) inferences.push(source);
37623764
break;
@@ -3771,6 +3773,35 @@ module ts {
37713773
inferFromTypes(sourceTypes[i], targetTypes[i]);
37723774
}
37733775
}
3776+
else if (target.flags & TypeFlags.Union) {
3777+
// Target is a union type
3778+
var targetTypes = (<UnionType>target).types;
3779+
var startCount = context.inferenceCount;
3780+
var typeParameterCount = 0;
3781+
var typeParameter: TypeParameter;
3782+
// First infer to each type in union that isn't a type parameter
3783+
for (var i = 0; i < targetTypes.length; i++) {
3784+
var t = targetTypes[i];
3785+
if (t.flags & TypeFlags.TypeParameter && contains(context.typeParameters, t)) {
3786+
typeParameter = <TypeParameter>t;
3787+
typeParameterCount++;
3788+
}
3789+
else {
3790+
inferFromTypes(source, t);
3791+
}
3792+
}
3793+
// If no inferences were produced above and union contains a single naked type parameter, infer to that type parameter
3794+
if (context.inferenceCount === startCount && typeParameterCount === 1) {
3795+
inferFromTypes(source, typeParameter);
3796+
}
3797+
}
3798+
else if (source.flags & TypeFlags.Union) {
3799+
// Source is a union type, infer from each consituent type
3800+
var sourceTypes = (<UnionType>source).types;
3801+
for (var i = 0; i < sourceTypes.length; i++) {
3802+
inferFromTypes(sourceTypes[i], target);
3803+
}
3804+
}
37743805
else if (source.flags & TypeFlags.ObjectType && (target.flags & (TypeFlags.Reference | TypeFlags.Tuple) ||
37753806
(target.flags & TypeFlags.Anonymous) && target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral))) {
37763807
// If source is an object type, and target is a type reference, a tuple type, the type of a method, or a type literal, infer from members
@@ -5169,7 +5200,9 @@ module ts {
51695200

51705201
// Try to return the best common type if we have any return expressions.
51715202
if (types.length > 0) {
5172-
var commonType = getCommonSupertype(types);
5203+
// When return statements are contextually typed we allow the return type to be a union type. Otherwise we require the
5204+
// return expressions to have a best common supertype.
5205+
var commonType = getContextualSignature(func) ? getUnionType(types) : getCommonSupertype(types);
51735206
if (!commonType) {
51745207
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
51755208

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ module ts {
970970

971971
export interface InferenceContext {
972972
typeParameters: TypeParameter[];
973+
inferenceCount: number;
973974
inferences: Type[][];
974975
inferredTypes: Type[];
975976
}

0 commit comments

Comments
 (0)