Skip to content

Commit 68056d5

Browse files
committed
Clean up implementation
1 parent e19d934 commit 68056d5

File tree

2 files changed

+55
-46
lines changed

2 files changed

+55
-46
lines changed

src/compiler/checker.ts

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10134,13 +10134,12 @@ namespace ts {
1013410134
}
1013510135
}
1013610136

10137-
function createInferenceContext(callNode: CallLikeExpression, signature: Signature, inferUnionTypes: boolean, noInferenceType: Type): InferenceContext {
10137+
function createInferenceContext(callNode: CallLikeExpression, signature: Signature, flags: InferenceFlags): InferenceContext {
1013810138
return {
1013910139
callNode,
1014010140
signature,
1014110141
inferences: map(signature.typeParameters, createInferenceInfo),
10142-
inferUnionTypes,
10143-
noInferenceType
10142+
flags,
1014410143
};
1014510144
}
1014610145

@@ -10160,8 +10159,7 @@ namespace ts {
1016010159
callNode: context.callNode,
1016110160
signature: context.signature,
1016210161
inferences: map(context.inferences, cloneInferenceInfo),
10163-
inferUnionTypes: context.inferUnionTypes,
10164-
noInferenceType: silentNeverType
10162+
flags: context.flags | InferenceFlags.NoDefault,
1016510163
}
1016610164
}
1016710165

@@ -10243,10 +10241,6 @@ namespace ts {
1024310241
}
1024410242
}
1024510243

10246-
function inferTypesWithContext(context: InferenceContext, originalSource: Type, originalTarget: Type) {
10247-
inferTypes(context.inferences, originalSource, originalTarget);
10248-
}
10249-
1025010244
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
1025110245
let symbolStack: Symbol[];
1025210246
let visited: Map<boolean>;
@@ -10315,13 +10309,10 @@ namespace ts {
1031510309
}
1031610310
for (const inference of inferences) {
1031710311
if (target === inference.typeParameter) {
10318-
if (!inference.isFixed) {
10319-
// Any inferences that are made to a type parameter in a union type are inferior
10320-
// to inferences made to a flat (non-union) type. This is because if we infer to
10321-
// T | string[], we really don't know if we should be inferring to T or not (because
10322-
// the correct constituent on the target side could be string[]). Therefore, we put
10323-
// such inferior inferences into a secondary bucket, and only use them if the primary
10324-
// bucket is empty.
10312+
// Even if an inference is marked as fixed, we can add candidates from inferences made
10313+
// from the return type of generic functions (which only happens when no other candidates
10314+
// are present).
10315+
if (!inference.isFixed || priority & InferencePriority.ReturnType) {
1032510316
if (!inference.candidates || priority < inference.priority) {
1032610317
inference.candidates = [source];
1032710318
inference.priority = priority;
@@ -10543,15 +10534,21 @@ namespace ts {
1054310534
let inferenceSucceeded: boolean;
1054410535
if (!inferredType) {
1054510536
if (!inference.candidates && context.callNode && isExpression(context.callNode)) {
10537+
// We have no inference candidates. Now attempt to get the contextual type for the call
10538+
// expression associated with the context, and if a contextual type is available, infer
10539+
// from that type to the return type of the call expression. For example, given a
10540+
// 'function wrap<T, U>(cb: (x: T) => U): (x: T) => U' and a call expression
10541+
// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type
10542+
// of 'f' to the return type of 'wrap'.
1054610543
const contextualType = getContextualType(context.callNode);
1054710544
if (contextualType) {
10545+
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
10546+
// outer call expression. Effectively we just want a snapshot of whatever has been
10547+
// inferred for any outer call expression so far.
1054810548
const mapper = cloneTypeMapper(getContextualMapper(context.callNode));
1054910549
const instantiatedType = instantiateType(contextualType, mapper);
1055010550
const returnType = getReturnTypeOfSignature(context.signature);
10551-
const saveFixed = inference.isFixed;
10552-
inference.isFixed = false;
1055310551
inferTypes([inference], instantiatedType, returnType, InferencePriority.ReturnType);
10554-
inference.isFixed = saveFixed;
1055510552
}
1055610553
}
1055710554
if (inference.candidates) {
@@ -10564,30 +10561,37 @@ namespace ts {
1056410561
!hasPrimitiveConstraint(inference.typeParameter) &&
1056510562
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
1056610563
const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) : inference.candidates;
10567-
// Infer widened union or supertype, or the unknown type for no common supertype
10568-
const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates);
10564+
// Infer widened union or supertype, or the unknown type for no common supertype. We infer union types
10565+
// for inferences coming from return types in order to avoid common supertype failures.
10566+
const unionOrSuperType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ?
10567+
getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates);
1056910568
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType;
1057010569
inferenceSucceeded = !!unionOrSuperType;
1057110570
}
1057210571
else {
10573-
// Infer either the default or the empty object type when no inferences were
10574-
// made. It is important to remember that in this case, inference still
10575-
// succeeds, meaning there is no error for not having inference candidates. An
10576-
// inference error only occurs when there are *conflicting* candidates, i.e.
10577-
// candidates with no common supertype.
10578-
const defaultType = context.noInferenceType === silentNeverType ? undefined : getDefaultFromTypeParameter(inference.typeParameter);
10579-
if (defaultType) {
10580-
// Instantiate the default type. Any forward reference to a type
10581-
// parameter should be instantiated to the empty object type.
10582-
inferredType = instantiateType(defaultType,
10583-
combineTypeMappers(
10584-
createBackreferenceMapper(context.signature.typeParameters, index),
10585-
getInferenceMapper(context)));
10572+
if (context.flags & InferenceFlags.NoDefault) {
10573+
// We use silentNeverType as the wildcard that signals no inferences.
10574+
inferredType = silentNeverType;
1058610575
}
1058710576
else {
10588-
inferredType = context.noInferenceType;
10577+
// Infer either the default or the empty object type when no inferences were
10578+
// made. It is important to remember that in this case, inference still
10579+
// succeeds, meaning there is no error for not having inference candidates. An
10580+
// inference error only occurs when there are *conflicting* candidates, i.e.
10581+
// candidates with no common supertype.
10582+
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
10583+
if (defaultType) {
10584+
// Instantiate the default type. Any forward reference to a type
10585+
// parameter should be instantiated to the empty object type.
10586+
inferredType = instantiateType(defaultType,
10587+
combineTypeMappers(
10588+
createBackreferenceMapper(context.signature.typeParameters, index),
10589+
getInferenceMapper(context)));
10590+
}
10591+
else {
10592+
inferredType = context.flags & InferenceFlags.AnyDefault ? anyType : emptyObjectType;
10593+
}
1058910594
}
10590-
1059110595
inferenceSucceeded = true;
1059210596
}
1059310597
inference.inferredType = inferredType;
@@ -14872,10 +14876,10 @@ namespace ts {
1487214876

1487314877
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
1487414878
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
14875-
const context = createInferenceContext(/*callNode*/ undefined, signature, /*inferUnionTypes*/ true, /*noInferenceType*/ emptyObjectType);
14879+
const context = createInferenceContext(/*callNode*/ undefined, signature, InferenceFlags.InferUnionTypes);
1487614880
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
1487714881
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
14878-
inferTypesWithContext(context, instantiateType(source, contextualMapper), target);
14882+
inferTypes(context.inferences, instantiateType(source, contextualMapper), target);
1487914883
});
1488014884
return getSignatureInstantiation(signature, getInferredTypes(context));
1488114885
}
@@ -14910,7 +14914,7 @@ namespace ts {
1491014914
if (thisType) {
1491114915
const thisArgumentNode = getThisArgumentOfCall(node);
1491214916
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
14913-
inferTypesWithContext(context, thisArgumentType, thisType);
14917+
inferTypes(context.inferences, thisArgumentType, thisType);
1491414918
}
1491514919

1491614920
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
@@ -14932,7 +14936,7 @@ namespace ts {
1493214936
argType = checkExpressionWithContextualType(arg, paramType, mapper);
1493314937
}
1493414938

14935-
inferTypesWithContext(context, argType, paramType);
14939+
inferTypes(context.inferences, argType, paramType);
1493614940
}
1493714941
}
1493814942

@@ -14947,7 +14951,7 @@ namespace ts {
1494714951
if (excludeArgument[i] === false) {
1494814952
const arg = args[i];
1494914953
const paramType = getTypeAtPosition(signature, i);
14950-
inferTypesWithContext(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
14954+
inferTypes(context.inferences, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
1495114955
}
1495214956
}
1495314957
}
@@ -15606,7 +15610,7 @@ namespace ts {
1560615610
let candidate: Signature;
1560715611
let typeArgumentsAreValid: boolean;
1560815612
const inferenceContext = originalCandidate.typeParameters
15609-
? createInferenceContext(node, originalCandidate, /*inferUnionTypes*/ false, /*noInferenceType*/ isInJavaScriptFile(node) ? anyType : emptyObjectType)
15613+
? createInferenceContext(node, originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : 0)
1561015614
: undefined;
1561115615

1561215616
while (true) {
@@ -16192,7 +16196,7 @@ namespace ts {
1619216196
for (let i = 0; i < len; i++) {
1619316197
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
1619416198
if (declaration.type) {
16195-
inferTypesWithContext(mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
16199+
inferTypes(mapper.context.inferences, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
1619616200
}
1619716201
}
1619816202
}
@@ -16278,7 +16282,7 @@ namespace ts {
1627816282
// T in the second overload so that we do not infer Base as a candidate for T
1627916283
// (inferring Base would make type argument inference inconsistent between the two
1628016284
// overloads).
16281-
inferTypesWithContext(mapper.context, links.type, instantiateType(contextualType, mapper));
16285+
inferTypes(mapper.context.inferences, links.type, instantiateType(contextualType, mapper));
1628216286
}
1628316287
}
1628416288

src/compiler/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3356,14 +3356,19 @@ namespace ts {
33563356
isFixed: boolean;
33573357
}
33583358

3359+
export const enum InferenceFlags {
3360+
InferUnionTypes = 1 << 0, // Infer union types for disjoint candidates (otherwise unknownType)
3361+
NoDefault = 1 << 1, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
3362+
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
3363+
}
3364+
33593365
/* @internal */
33603366
export interface InferenceContext {
33613367
callNode: CallLikeExpression; // Call expression node for which inferences are made
33623368
signature: Signature; // Generic signature for which inferences are made
33633369
inferences: InferenceInfo[]; // Inferences made for each type parameter
3370+
flags: InferenceFlags; // Infer union types for disjoint candidates (otherwise undefinedType)
33643371
mapper?: TypeMapper; // Type mapper for this inference context
3365-
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
3366-
noInferenceType: Type; // Type to use for no inferences
33673372
failedTypeParameterIndex?: number; // Index of type parameter for which inference failed
33683373
// It is optional because in contextual signature instantiation, nothing fails
33693374
}

0 commit comments

Comments
 (0)