diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c61581facde83..ab6cc383c543e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28962,6 +28962,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getContextualTypeForObjectLiteralMethod(node, contextFlags) : getContextualType(node, contextFlags); const instantiatedType = instantiateContextualType(contextualType, node, contextFlags); + if (instantiatedType && contextFlags && contextFlags & ContextFlags.DeferGenericIndexedAccess && shouldDeferIndexType(instantiatedType)) { + return instantiatedType; + } if (instantiatedType && !(contextFlags && contextFlags & ContextFlags.NoConstraints && instantiatedType.flags & TypeFlags.TypeVariable)) { const apparentType = mapType(instantiatedType, getApparentType, /*noReductions*/ true); return apparentType.flags & TypeFlags.Union && isObjectLiteralExpression(node) ? discriminateContextualTypeByObjectMembers(node, apparentType as UnionType) : @@ -29669,7 +29672,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let propertiesArray: Symbol[] = []; let spread: Type = emptyObjectType; - pushContextualType(node, getContextualType(node, /*contextFlags*/ undefined)); + pushContextualType(node, getContextualType(node, checkMode && checkMode & CheckMode.Inferential ? ContextFlags.DeferGenericIndexedAccess : undefined)); const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined); const contextualTypeHasPattern = contextualType && contextualType.pattern && (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 87a72ea860f3b..b39b7e30da9d6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5262,6 +5262,7 @@ export const enum ContextFlags { NoConstraints = 1 << 1, // Don't obtain type variable constraints Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions SkipBindingPatterns = 1 << 3, // Ignore contextual types applied by binding patterns + DeferGenericIndexedAccess = 1 << 4, // Defer generic indexed access type instead of resolving it immediately to the constraint } // NOTE: If modifying this enum, must modify `TypeFormatFlags` too! diff --git a/tests/baselines/reference/contextualSignatureInNestedPropOfGenericIndexedAccess.symbols b/tests/baselines/reference/contextualSignatureInNestedPropOfGenericIndexedAccess.symbols new file mode 100644 index 0000000000000..2b3bdeb152873 --- /dev/null +++ b/tests/baselines/reference/contextualSignatureInNestedPropOfGenericIndexedAccess.symbols @@ -0,0 +1,62 @@ +=== tests/cases/compiler/contextualSignatureInNestedPropOfGenericIndexedAccess.ts === +// repro from #52575 + +export interface Event<T> { +>Event : Symbol(Event, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 0, 0)) +>T : Symbol(T, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 2, 23)) + + callback: (response: T) => void; +>callback : Symbol(Event.callback, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 2, 27)) +>response : Symbol(response, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 3, 15)) +>T : Symbol(T, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 2, 23)) + + nested: { +>nested : Symbol(Event.nested, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 3, 36)) + + nestedCallback: (response: T) => void; +>nestedCallback : Symbol(nestedCallback, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 4, 13)) +>response : Symbol(response, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 5, 25)) +>T : Symbol(T, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 2, 23)) + } +} + +export type CustomEvents = { +>CustomEvents : Symbol(CustomEvents, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 7, 1)) + + a: Event<string> +>a : Symbol(a, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 9, 28)) +>Event : Symbol(Event, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 0, 0)) + + b: Event<number> +>b : Symbol(b, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 10, 20)) +>Event : Symbol(Event, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 0, 0)) + +}; + +declare function emit<T extends keyof CustomEvents>(type: T, data: CustomEvents[T]): void +>emit : Symbol(emit, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 12, 2)) +>T : Symbol(T, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 14, 22)) +>CustomEvents : Symbol(CustomEvents, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 7, 1)) +>type : Symbol(type, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 14, 52)) +>T : Symbol(T, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 14, 22)) +>data : Symbol(data, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 14, 60)) +>CustomEvents : Symbol(CustomEvents, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 7, 1)) +>T : Symbol(T, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 14, 22)) + +emit('a', { +>emit : Symbol(emit, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 12, 2)) + + callback: (r) => {}, +>callback : Symbol(callback, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 16, 11)) +>r : Symbol(r, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 17, 15)) + + nested: { +>nested : Symbol(nested, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 17, 24)) + + nestedCallback: (r) => {}, +>nestedCallback : Symbol(nestedCallback, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 18, 13)) +>r : Symbol(r, Decl(contextualSignatureInNestedPropOfGenericIndexedAccess.ts, 19, 25)) + + }, +}); + diff --git a/tests/baselines/reference/contextualSignatureInNestedPropOfGenericIndexedAccess.types b/tests/baselines/reference/contextualSignatureInNestedPropOfGenericIndexedAccess.types new file mode 100644 index 0000000000000..f9c0fbded1f8f --- /dev/null +++ b/tests/baselines/reference/contextualSignatureInNestedPropOfGenericIndexedAccess.types @@ -0,0 +1,56 @@ +=== tests/cases/compiler/contextualSignatureInNestedPropOfGenericIndexedAccess.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 + + }, +}); + diff --git a/tests/cases/compiler/contextualSignatureInNestedPropOfGenericIndexedAccess.ts b/tests/cases/compiler/contextualSignatureInNestedPropOfGenericIndexedAccess.ts new file mode 100644 index 0000000000000..6718365ade181 --- /dev/null +++ b/tests/cases/compiler/contextualSignatureInNestedPropOfGenericIndexedAccess.ts @@ -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) => {}, + }, +});