Skip to content

Defer generic indexed access when obtaining contextual type within inferential check #52586

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down
Original file line number Diff line number Diff line change
@@ -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))

},
});

Original file line number Diff line number Diff line change
@@ -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

},
});

Original file line number Diff line number Diff line change
@@ -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) => {},
},
});