Skip to content

Commit d4848aa

Browse files
committed
Slightly different approach to forbid inference to specific expressions
1 parent d08ae97 commit d4848aa

File tree

2 files changed

+36
-26
lines changed

2 files changed

+36
-26
lines changed

src/compiler/checker.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,26 @@ namespace ts {
468468
getRootSymbols,
469469
getContextualType: (nodeIn: Expression, contextFlags?: ContextFlags) => {
470470
const node = getParseTreeNode(nodeIn, isExpression);
471-
return node ? getContextualType(node, contextFlags) : undefined;
471+
if (!node) {
472+
return undefined;
473+
}
474+
const links = getNodeLinks(node);
475+
const resolvedType = links.resolvedType;
476+
const skipDirectInference = links.skipDirectInference;
477+
const containingCall = findAncestor(node, isCallLikeExpression);
478+
const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
479+
if (contextFlags! & ContextFlags.BaseConstraint && containingCall) {
480+
links.resolvedType = undefined;
481+
links.skipDirectInference = true;
482+
getNodeLinks(containingCall).resolvedSignature = undefined;
483+
}
484+
const result = getContextualType(node, contextFlags);
485+
if (contextFlags! & ContextFlags.BaseConstraint && containingCall) {
486+
links.skipDirectInference = skipDirectInference;
487+
links.resolvedType = resolvedType;
488+
getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
489+
}
490+
return result;
472491
},
473492
getContextualTypeForObjectLiteralElement: nodeIn => {
474493
const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
@@ -17675,6 +17694,14 @@ namespace ts {
1767517694
undefined;
1767617695
}
1767717696

17697+
function hasSkipDirectInferenceFlag(node: Node) {
17698+
return !!getNodeLinks(node).skipDirectInference;
17699+
}
17700+
17701+
function isFromInferenceBlockedSource(type: Type) {
17702+
return !!(type.symbol && some(type.symbol.declarations, hasSkipDirectInferenceFlag));
17703+
}
17704+
1767817705
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
1767917706
let symbolStack: Symbol[];
1768017707
let visited: Map<number>;
@@ -17765,7 +17792,7 @@ namespace ts {
1776517792
// of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard
1776617793
// when constructing types from type parameters that had no inference candidates).
1776717794
if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType || source === silentNeverType ||
17768-
(priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) {
17795+
(priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) {
1776917796
return;
1777017797
}
1777117798
const inference = getInferenceInfoForType(target);
@@ -21330,34 +21357,16 @@ namespace ts {
2133021357
}
2133121358

2133221359
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
21333-
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression, contextFlags?: ContextFlags): Type | undefined {
21360+
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type | undefined {
2133421361
const args = getEffectiveCallArguments(callTarget);
2133521362
const argIndex = args.indexOf(arg); // -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression
21336-
return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex, contextFlags);
21363+
return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex);
2133721364
}
2133821365

21339-
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type {
21366+
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number): Type {
2134021367
// If we're already in the process of resolving the given signature, don't resolve again as
2134121368
// that could cause infinite recursion. Instead, return anySignature.
21342-
let signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
21343-
if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature !== resolvingSignature && !hasTypeArguments(callTarget)) {
21344-
if (isCallOrNewExpression(callTarget) && callTarget.arguments) {
21345-
let clone = signature.omittedParameterCache && signature.omittedParameterCache[argIndex];
21346-
if (!clone) {
21347-
// clone the ast, eliding the target argument
21348-
clone = getSynthesizedClone(callTarget) as CallExpression | NewExpression;
21349-
(clone as any).id = undefined;
21350-
clone.arguments = createNodeArray([...clone.arguments!.slice(0, argIndex), createOmittedExpression(), ...clone.arguments!.slice(argIndex + 1)]);
21351-
clone.arguments![argIndex].parent = clone;
21352-
clone.parent = callTarget.parent;
21353-
(signature.omittedParameterCache || (signature.omittedParameterCache = []))[argIndex] = clone;
21354-
}
21355-
signature = getResolvedSignature(clone);
21356-
}
21357-
else if (signature.target) {
21358-
signature = getBaseSignature(signature.target);
21359-
}
21360-
}
21369+
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
2136121370

2136221371
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
2136321372
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
@@ -21753,7 +21762,7 @@ namespace ts {
2175321762
}
2175421763
/* falls through */
2175521764
case SyntaxKind.NewExpression:
21756-
return getContextualTypeForArgument(<CallExpression | NewExpression>parent, node, contextFlags);
21765+
return getContextualTypeForArgument(<CallExpression | NewExpression>parent, node);
2175721766
case SyntaxKind.TypeAssertionExpression:
2175821767
case SyntaxKind.AsExpression:
2175921768
return isConstTypeReference((<AssertionExpression>parent).type) ? undefined : getTypeFromTypeNode((<AssertionExpression>parent).type);
@@ -21803,7 +21812,7 @@ namespace ts {
2180321812
// (as below) instead!
2180421813
return node.parent.contextualType;
2180521814
}
21806-
return getContextualTypeForArgumentAtIndex(node, 0, contextFlags);
21815+
return getContextualTypeForArgumentAtIndex(node, 0);
2180721816
}
2180821817

2180921818
function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) {

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4240,6 +4240,7 @@ namespace ts {
42404240
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
42414241
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic)
42424242
isExhaustive?: boolean; // Is node an exhaustive switch statement
4243+
skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `BaseConstraint` is passed to force the checker to skip making inferences to a node's type
42434244
}
42444245

42454246
export const enum TypeFlags {

0 commit comments

Comments
 (0)