mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Consult cached contextual types only when no contextFlags (#52611)
This commit is contained in:
parent
1c9ce4fc2e
commit
c838b0cb7e
@ -2111,6 +2111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
const contextualTypeNodes: Node[] = [];
|
||||
const contextualTypes: (Type | undefined)[] = [];
|
||||
const contextualIsCache: boolean[] = [];
|
||||
let contextualTypeCount = 0;
|
||||
|
||||
const inferenceContextNodes: Node[] = [];
|
||||
@ -19191,7 +19192,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function checkExpressionForMutableLocationWithContextualType(next: Expression, sourcePropType: Type) {
|
||||
pushContextualType(next, sourcePropType);
|
||||
pushContextualType(next, sourcePropType, /*isCache*/ false);
|
||||
const result = checkExpressionForMutableLocation(next, CheckMode.Contextual);
|
||||
popContextualType();
|
||||
return result;
|
||||
@ -19508,7 +19509,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
// recreate a tuple from the elements, if possible
|
||||
// Since we're re-doing the expression type, we need to reapply the contextual type
|
||||
pushContextualType(node, target);
|
||||
pushContextualType(node, target, /*isCache*/ false);
|
||||
const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true);
|
||||
popContextualType();
|
||||
if (isTupleLikeType(tupleizedType)) {
|
||||
@ -29034,10 +29035,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// We cannot answer semantic questions within a with block, do not proceed any further
|
||||
return undefined;
|
||||
}
|
||||
const index = findContextualNode(node);
|
||||
// Cached contextual types are obtained with no ContextFlags, so we can only consult them for
|
||||
// requests with no ContextFlags.
|
||||
const index = findContextualNode(node, /*includeCaches*/ !contextFlags);
|
||||
if (index >= 0) {
|
||||
const cached = contextualTypes[index];
|
||||
if (cached || !contextFlags) return cached;
|
||||
return contextualTypes[index];
|
||||
}
|
||||
const { parent } = node;
|
||||
switch (parent.kind) {
|
||||
@ -29110,9 +29112,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function pushContextualType(node: Node, type: Type | undefined) {
|
||||
function pushCachedContextualType(node: Expression) {
|
||||
pushContextualType(node, getContextualType(node, /*contextFlags*/ undefined), /*isCache*/ true);
|
||||
}
|
||||
|
||||
function pushContextualType(node: Expression, type: Type | undefined, isCache: boolean) {
|
||||
contextualTypeNodes[contextualTypeCount] = node;
|
||||
contextualTypes[contextualTypeCount] = type;
|
||||
contextualIsCache[contextualTypeCount] = isCache;
|
||||
contextualTypeCount++;
|
||||
}
|
||||
|
||||
@ -29120,9 +29127,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
contextualTypeCount--;
|
||||
}
|
||||
|
||||
function findContextualNode(node: Node) {
|
||||
function findContextualNode(node: Node, includeCaches: boolean) {
|
||||
for (let i = contextualTypeCount - 1; i >= 0; i--) {
|
||||
if (node === contextualTypeNodes[i]) {
|
||||
if (node === contextualTypeNodes[i] && (includeCaches || !contextualIsCache[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -29149,7 +29156,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags: ContextFlags | undefined) {
|
||||
if (isJsxOpeningElement(node) && contextFlags !== ContextFlags.Completions) {
|
||||
const index = findContextualNode(node.parent);
|
||||
const index = findContextualNode(node.parent, /*includeCaches*/ !contextFlags);
|
||||
if (index >= 0) {
|
||||
// Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit
|
||||
// _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type
|
||||
@ -29485,7 +29492,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const elementCount = elements.length;
|
||||
const elementTypes: Type[] = [];
|
||||
const elementFlags: ElementFlags[] = [];
|
||||
pushContextualType(node, getContextualType(node, /*contextFlags*/ undefined));
|
||||
pushCachedContextualType(node);
|
||||
const inDestructuringPattern = isAssignmentTarget(node);
|
||||
const inConstContext = isConstContext(node);
|
||||
const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined);
|
||||
@ -29668,7 +29675,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let propertiesArray: Symbol[] = [];
|
||||
let spread: Type = emptyObjectType;
|
||||
|
||||
pushContextualType(node, getContextualType(node, /*contextFlags*/ undefined));
|
||||
pushCachedContextualType(node);
|
||||
const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined);
|
||||
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
|
||||
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
|
||||
@ -36823,8 +36830,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
type.flags & TypeFlags.InstantiableNonPrimitive && maybeTypeOfKind(getBaseConstraintOfType(type) || unknownType, TypeFlags.StringLike));
|
||||
}
|
||||
|
||||
function getContextNode(node: Expression): Node {
|
||||
if (node.kind === SyntaxKind.JsxAttributes && !isJsxSelfClosingElement(node.parent)) {
|
||||
function getContextNode(node: Expression): Expression {
|
||||
if (isJsxAttributes(node) && !isJsxSelfClosingElement(node.parent)) {
|
||||
return node.parent.parent; // Needs to be the root JsxElement, so it encompasses the attributes _and_ the children (which are essentially part of the attributes)
|
||||
}
|
||||
return node;
|
||||
@ -36832,7 +36839,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
function checkExpressionWithContextualType(node: Expression, contextualType: Type, inferenceContext: InferenceContext | undefined, checkMode: CheckMode): Type {
|
||||
const contextNode = getContextNode(node);
|
||||
pushContextualType(contextNode, contextualType);
|
||||
pushContextualType(contextNode, contextualType, /*isCache*/ false);
|
||||
pushInferenceContext(contextNode, inferenceContext);
|
||||
const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0));
|
||||
// In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type
|
||||
@ -37223,7 +37230,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (links.contextFreeType) {
|
||||
return links.contextFreeType;
|
||||
}
|
||||
pushContextualType(node, anyType);
|
||||
pushContextualType(node, anyType, /*isCache*/ false);
|
||||
const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive);
|
||||
popContextualType();
|
||||
return type;
|
||||
|
||||
62
tests/baselines/reference/contextualTypeCaching.symbols
Normal file
62
tests/baselines/reference/contextualTypeCaching.symbols
Normal file
@ -0,0 +1,62 @@
|
||||
=== tests/cases/compiler/contextualTypeCaching.ts ===
|
||||
// Repro from #52575
|
||||
|
||||
export interface Event<T> {
|
||||
>Event : Symbol(Event, Decl(contextualTypeCaching.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(contextualTypeCaching.ts, 2, 23))
|
||||
|
||||
callback: (response: T) => void;
|
||||
>callback : Symbol(Event.callback, Decl(contextualTypeCaching.ts, 2, 27))
|
||||
>response : Symbol(response, Decl(contextualTypeCaching.ts, 3, 15))
|
||||
>T : Symbol(T, Decl(contextualTypeCaching.ts, 2, 23))
|
||||
|
||||
nested: {
|
||||
>nested : Symbol(Event.nested, Decl(contextualTypeCaching.ts, 3, 36))
|
||||
|
||||
nestedCallback: (response: T) => void;
|
||||
>nestedCallback : Symbol(nestedCallback, Decl(contextualTypeCaching.ts, 4, 13))
|
||||
>response : Symbol(response, Decl(contextualTypeCaching.ts, 5, 25))
|
||||
>T : Symbol(T, Decl(contextualTypeCaching.ts, 2, 23))
|
||||
}
|
||||
}
|
||||
|
||||
export type CustomEvents = {
|
||||
>CustomEvents : Symbol(CustomEvents, Decl(contextualTypeCaching.ts, 7, 1))
|
||||
|
||||
a: Event<string>
|
||||
>a : Symbol(a, Decl(contextualTypeCaching.ts, 9, 28))
|
||||
>Event : Symbol(Event, Decl(contextualTypeCaching.ts, 0, 0))
|
||||
|
||||
b: Event<number>
|
||||
>b : Symbol(b, Decl(contextualTypeCaching.ts, 10, 20))
|
||||
>Event : Symbol(Event, Decl(contextualTypeCaching.ts, 0, 0))
|
||||
|
||||
};
|
||||
|
||||
declare function emit<T extends keyof CustomEvents>(type: T, data: CustomEvents[T]): void
|
||||
>emit : Symbol(emit, Decl(contextualTypeCaching.ts, 12, 2))
|
||||
>T : Symbol(T, Decl(contextualTypeCaching.ts, 14, 22))
|
||||
>CustomEvents : Symbol(CustomEvents, Decl(contextualTypeCaching.ts, 7, 1))
|
||||
>type : Symbol(type, Decl(contextualTypeCaching.ts, 14, 52))
|
||||
>T : Symbol(T, Decl(contextualTypeCaching.ts, 14, 22))
|
||||
>data : Symbol(data, Decl(contextualTypeCaching.ts, 14, 60))
|
||||
>CustomEvents : Symbol(CustomEvents, Decl(contextualTypeCaching.ts, 7, 1))
|
||||
>T : Symbol(T, Decl(contextualTypeCaching.ts, 14, 22))
|
||||
|
||||
emit('a', {
|
||||
>emit : Symbol(emit, Decl(contextualTypeCaching.ts, 12, 2))
|
||||
|
||||
callback: (r) => {},
|
||||
>callback : Symbol(callback, Decl(contextualTypeCaching.ts, 16, 11))
|
||||
>r : Symbol(r, Decl(contextualTypeCaching.ts, 17, 15))
|
||||
|
||||
nested: {
|
||||
>nested : Symbol(nested, Decl(contextualTypeCaching.ts, 17, 24))
|
||||
|
||||
nestedCallback: (r) => {},
|
||||
>nestedCallback : Symbol(nestedCallback, Decl(contextualTypeCaching.ts, 18, 13))
|
||||
>r : Symbol(r, Decl(contextualTypeCaching.ts, 19, 25))
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
56
tests/baselines/reference/contextualTypeCaching.types
Normal file
56
tests/baselines/reference/contextualTypeCaching.types
Normal file
@ -0,0 +1,56 @@
|
||||
=== tests/cases/compiler/contextualTypeCaching.ts ===
|
||||
// Repro from #52575
|
||||
|
||||
export interface Event<T> {
|
||||
callback: (response: T) => void;
|
||||
>callback : (response: T) => void
|
||||
>response : T
|
||||
|
||||
nested: {
|
||||
>nested : { nestedCallback: (response: T) => void; }
|
||||
|
||||
nestedCallback: (response: T) => void;
|
||||
>nestedCallback : (response: T) => void
|
||||
>response : T
|
||||
}
|
||||
}
|
||||
|
||||
export type CustomEvents = {
|
||||
>CustomEvents : { a: Event<string>; b: Event<number>; }
|
||||
|
||||
a: Event<string>
|
||||
>a : Event<string>
|
||||
|
||||
b: Event<number>
|
||||
>b : Event<number>
|
||||
|
||||
};
|
||||
|
||||
declare function emit<T extends keyof CustomEvents>(type: T, data: CustomEvents[T]): void
|
||||
>emit : <T extends keyof CustomEvents>(type: T, data: CustomEvents[T]) => void
|
||||
>type : T
|
||||
>data : CustomEvents[T]
|
||||
|
||||
emit('a', {
|
||||
>emit('a', { callback: (r) => {}, nested: { nestedCallback: (r) => {}, },}) : void
|
||||
>emit : <T extends keyof CustomEvents>(type: T, data: CustomEvents[T]) => void
|
||||
>'a' : "a"
|
||||
>{ callback: (r) => {}, nested: { nestedCallback: (r) => {}, },} : { callback: (r: string) => void; nested: { nestedCallback: (r: string) => void; }; }
|
||||
|
||||
callback: (r) => {},
|
||||
>callback : (r: string) => void
|
||||
>(r) => {} : (r: string) => void
|
||||
>r : string
|
||||
|
||||
nested: {
|
||||
>nested : { nestedCallback: (r: string) => void; }
|
||||
>{ nestedCallback: (r) => {}, } : { nestedCallback: (r: string) => void; }
|
||||
|
||||
nestedCallback: (r) => {},
|
||||
>nestedCallback : (r: string) => void
|
||||
>(r) => {} : (r: string) => void
|
||||
>r : string
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
25
tests/cases/compiler/contextualTypeCaching.ts
Normal file
25
tests/cases/compiler/contextualTypeCaching.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
// Repro from #52575
|
||||
|
||||
export interface Event<T> {
|
||||
callback: (response: T) => void;
|
||||
nested: {
|
||||
nestedCallback: (response: T) => void;
|
||||
}
|
||||
}
|
||||
|
||||
export type CustomEvents = {
|
||||
a: Event<string>
|
||||
b: Event<number>
|
||||
};
|
||||
|
||||
declare function emit<T extends keyof CustomEvents>(type: T, data: CustomEvents[T]): void
|
||||
|
||||
emit('a', {
|
||||
callback: (r) => {},
|
||||
nested: {
|
||||
nestedCallback: (r) => {},
|
||||
},
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user