Skip to content

Commit 6f06d06

Browse files
committed
Simplify tracking of top-level type inferences
1 parent 31a94fc commit 6f06d06

File tree

2 files changed

+29
-30
lines changed

2 files changed

+29
-30
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7521,7 +7521,7 @@ namespace ts {
75217521
return {
75227522
primary: undefined,
75237523
secondary: undefined,
7524-
shallow: true,
7524+
topLevel: true,
75257525
isFixed: false,
75267526
};
75277527
}
@@ -7543,14 +7543,18 @@ namespace ts {
75437543
return type.couldContainTypeParameters;
75447544
}
75457545

7546-
function inferTypes(context: InferenceContext, source: Type, target: Type) {
7546+
function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean {
7547+
return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter));
7548+
}
7549+
7550+
function inferTypes(context: InferenceContext, originalSource: Type, originalTarget: Type) {
75477551
const typeParameters = context.signature.typeParameters;
75487552
let sourceStack: Type[];
75497553
let targetStack: Type[];
75507554
let depth = 0;
75517555
let inferiority = 0;
75527556
const visited = createMap<boolean>();
7553-
inferFromTypes(source, target, /*nested*/ false);
7557+
inferFromTypes(originalSource, originalTarget);
75547558

75557559
function isInProcess(source: Type, target: Type) {
75567560
for (let i = 0; i < depth; i++) {
@@ -7561,7 +7565,7 @@ namespace ts {
75617565
return false;
75627566
}
75637567

7564-
function inferFromTypes(source: Type, target: Type, nested: boolean) {
7568+
function inferFromTypes(source: Type, target: Type) {
75657569
if (!couldContainTypeParameters(target)) {
75667570
return;
75677571
}
@@ -7571,7 +7575,7 @@ namespace ts {
75717575
// are the same type, just relate each constituent type to itself.
75727576
if (source === target) {
75737577
for (const t of (<UnionOrIntersectionType>source).types) {
7574-
inferFromTypes(t, t, /*nested*/ false);
7578+
inferFromTypes(t, t);
75757579
}
75767580
return;
75777581
}
@@ -7583,7 +7587,7 @@ namespace ts {
75837587
for (const t of (<UnionOrIntersectionType>target).types) {
75847588
if (typeIdenticalToSomeType(t, (<UnionOrIntersectionType>source).types)) {
75857589
(matchingTypes || (matchingTypes = [])).push(t);
7586-
inferFromTypes(t, t, /*nested*/ false);
7590+
inferFromTypes(t, t);
75877591
}
75887592
}
75897593
// Next, to improve the quality of inferences, reduce the source and target types by
@@ -7620,8 +7624,8 @@ namespace ts {
76207624
if (!contains(candidates, source)) {
76217625
candidates.push(source);
76227626
}
7623-
if (nested) {
7624-
inferences.shallow = false;
7627+
if (!isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
7628+
inferences.topLevel = false;
76257629
}
76267630
}
76277631
return;
@@ -7634,7 +7638,7 @@ namespace ts {
76347638
const targetTypes = (<TypeReference>target).typeArguments || emptyArray;
76357639
const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
76367640
for (let i = 0; i < count; i++) {
7637-
inferFromTypes(sourceTypes[i], targetTypes[i], /*nested*/ true);
7641+
inferFromTypes(sourceTypes[i], targetTypes[i]);
76387642
}
76397643
}
76407644
else if (target.flags & TypeFlags.UnionOrIntersection) {
@@ -7648,23 +7652,23 @@ namespace ts {
76487652
typeParameterCount++;
76497653
}
76507654
else {
7651-
inferFromTypes(source, t, /*nested*/ false);
7655+
inferFromTypes(source, t);
76527656
}
76537657
}
76547658
// Next, if target containings a single naked type parameter, make a secondary inference to that type
76557659
// parameter. This gives meaningful results for union types in co-variant positions and intersection
76567660
// types in contra-variant positions (such as callback parameters).
76577661
if (typeParameterCount === 1) {
76587662
inferiority++;
7659-
inferFromTypes(source, typeParameter, /*nested*/ false);
7663+
inferFromTypes(source, typeParameter);
76607664
inferiority--;
76617665
}
76627666
}
76637667
else if (source.flags & TypeFlags.UnionOrIntersection) {
76647668
// Source is a union or intersection type, infer from each constituent type
76657669
const sourceTypes = (<UnionOrIntersectionType>source).types;
76667670
for (const sourceType of sourceTypes) {
7667-
inferFromTypes(sourceType, target, /*nested*/ false);
7671+
inferFromTypes(sourceType, target);
76687672
}
76697673
}
76707674
else {
@@ -7702,7 +7706,7 @@ namespace ts {
77027706
for (const targetProp of properties) {
77037707
const sourceProp = getPropertyOfObjectType(source, targetProp.name);
77047708
if (sourceProp) {
7705-
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), /*nested*/ true);
7709+
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
77067710
}
77077711
}
77087712
}
@@ -7719,17 +7723,17 @@ namespace ts {
77197723
}
77207724

77217725
function inferFromParameterTypes(source: Type, target: Type) {
7722-
return inferFromTypes(source, target, /*nested*/ true);
7726+
return inferFromTypes(source, target);
77237727
}
77247728

77257729
function inferFromSignature(source: Signature, target: Signature) {
77267730
forEachMatchingParameterType(source, target, inferFromParameterTypes);
77277731

77287732
if (source.typePredicate && target.typePredicate && source.typePredicate.kind === target.typePredicate.kind) {
7729-
inferFromTypes(source.typePredicate.type, target.typePredicate.type, /*nested*/ true);
7733+
inferFromTypes(source.typePredicate.type, target.typePredicate.type);
77307734
}
77317735
else {
7732-
inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target), /*nested*/ true);
7736+
inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
77337737
}
77347738
}
77357739

@@ -7739,7 +7743,7 @@ namespace ts {
77397743
const sourceIndexType = getIndexTypeOfType(source, IndexKind.String) ||
77407744
getImplicitIndexTypeOfType(source, IndexKind.String);
77417745
if (sourceIndexType) {
7742-
inferFromTypes(sourceIndexType, targetStringIndexType, /*nested*/ true);
7746+
inferFromTypes(sourceIndexType, targetStringIndexType);
77437747
}
77447748
}
77457749
const targetNumberIndexType = getIndexTypeOfType(target, IndexKind.Number);
@@ -7748,7 +7752,7 @@ namespace ts {
77487752
getIndexTypeOfType(source, IndexKind.String) ||
77497753
getImplicitIndexTypeOfType(source, IndexKind.Number);
77507754
if (sourceIndexType) {
7751-
inferFromTypes(sourceIndexType, targetNumberIndexType, /*nested*/ true);
7755+
inferFromTypes(sourceIndexType, targetNumberIndexType);
77527756
}
77537757
}
77547758
}
@@ -7787,25 +7791,20 @@ namespace ts {
77877791
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive);
77887792
}
77897793

7790-
function hasTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean {
7791-
return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => hasTypeParameterAtTopLevel(t, typeParameter));
7792-
}
7793-
7794-
77957794
function getInferredType(context: InferenceContext, index: number): Type {
77967795
let inferredType = context.inferredTypes[index];
77977796
let inferenceSucceeded: boolean;
77987797
if (!inferredType) {
77997798
const inferences = getInferenceCandidates(context, index);
78007799
if (inferences.length) {
7801-
// We keep inferences of literal types if
7802-
// we made at least one inference that wasn't shallow, or
7803-
// the type parameter has a primitive type constraint, or
7804-
// the type parameter wasn't fixed and is referenced at top level in the return type.
7800+
// We widen inferred literal types if
7801+
// all inferences were made to top-level ocurrences of the type parameter, and
7802+
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
7803+
// the type parameter was fixed during inference or does not occur at top-level in the return type.
78057804
const signature = context.signature;
7806-
const widenLiteralTypes = context.inferences[index].shallow &&
7805+
const widenLiteralTypes = context.inferences[index].topLevel &&
78077806
!hasPrimitiveConstraint(signature.typeParameters[index]) &&
7808-
(context.inferences[index].isFixed || !hasTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), signature.typeParameters[index]));
7807+
(context.inferences[index].isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), signature.typeParameters[index]));
78097808
const baseInferences = widenLiteralTypes ? map(inferences, getBaseTypeOfLiteralType) : inferences;
78107809
// Infer widened union or supertype, or the unknown type for no common supertype
78117810
const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseInferences, /*subtypeReduction*/ true) : getCommonSupertype(baseInferences);

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2583,7 +2583,7 @@ namespace ts {
25832583
export interface TypeInferences {
25842584
primary: Type[]; // Inferences made directly to a type parameter
25852585
secondary: Type[]; // Inferences made to a type parameter in a union type
2586-
shallow: boolean; // True if all inferences were made from shallow (not nested in object type) locations
2586+
topLevel: boolean; // True if all inferences were made from top-level (not nested in object type) locations
25872587
isFixed: boolean; // Whether the type parameter is fixed, as defined in section 4.12.2 of the TypeScript spec
25882588
// If a type parameter is fixed, no more inferences can be made for the type parameter
25892589
}

0 commit comments

Comments
 (0)