Include source node inferences in string literal completions deeper in arguments (#56182)

This commit is contained in:
Mateusz Burzyński 2025-12-08 19:30:33 +01:00 committed by GitHub
parent b2327c0f19
commit 69a0879743
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 167 additions and 8 deletions

View File

@ -32706,7 +32706,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return getContextualTypeForAwaitOperand(parent as AwaitExpression, contextFlags);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return getContextualTypeForArgument(parent as CallExpression | NewExpression | Decorator, node);
return getContextualTypeForArgument(parent as CallExpression | NewExpression, node);
case SyntaxKind.Decorator:
return getContextualTypeForDecorator(parent as Decorator);
case SyntaxKind.TypeAssertionExpression:

View File

@ -91,6 +91,7 @@ import {
isApplicableVersionedTypesKey,
isArray,
isCallExpression,
isCallLikeExpression,
isIdentifier,
isIdentifierText,
isImportCall,
@ -429,7 +430,15 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
// });
return stringLiteralCompletionsForObjectLiteral(typeChecker, parent.parent);
}
return fromContextualType() || fromContextualType(ContextFlags.None);
if (findAncestor(parent.parent, isCallLikeExpression)) {
const uniques = new Set<string>();
const stringLiteralTypes = concatenate(
getStringLiteralTypes(typeChecker.getContextualType(node, ContextFlags.None), uniques),
getStringLiteralTypes(typeChecker.getContextualType(node, ContextFlags.Completions), uniques),
);
return toStringLiteralCompletionsFromTypes(stringLiteralTypes);
}
return fromContextualType(ContextFlags.None);
case SyntaxKind.ElementAccessExpression: {
const { expression, argumentExpression } = parent as ElementAccessExpression;
@ -556,14 +565,14 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
function fromContextualType(contextFlags: ContextFlags = ContextFlags.IgnoreNodeInferences): StringLiteralCompletionsFromTypes | undefined {
// Get completion for string literal from string literal type
// i.e. var x: "hi" | "hello" = "/*completion position*/"
const types = getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, contextFlags));
if (!types.length) {
return;
}
return { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier: false };
return toStringLiteralCompletionsFromTypes(getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, contextFlags)));
}
}
function toStringLiteralCompletionsFromTypes(types: readonly StringLiteralType[]): StringLiteralCompletionsFromTypes | undefined {
return types.length ? { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier: false } : undefined;
}
function walkUpParentheses(node: Node) {
switch (node.kind) {
case SyntaxKind.ParenthesizedType:

View File

@ -0,0 +1,35 @@
//// [tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts] ////
=== generatorTypeCheck64.ts ===
function* g3(): Generator<Generator<(x: string) => number>> {
>g3 : Symbol(g3, Decl(generatorTypeCheck64.ts, 0, 0))
>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --))
>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --))
>x : Symbol(x, Decl(generatorTypeCheck64.ts, 0, 37))
yield function* () {
yield x => x.length;
>x : Symbol(x, Decl(generatorTypeCheck64.ts, 2, 13))
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(generatorTypeCheck64.ts, 2, 13))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
} ()
}
function* g4(): Iterator<Iterable<(x: string) => number>> {
>g4 : Symbol(g4, Decl(generatorTypeCheck64.ts, 4, 1))
>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.esnext.iterator.d.ts, --, --))
>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --))
>x : Symbol(x, Decl(generatorTypeCheck64.ts, 6, 35))
yield (function* () {
yield (x) => x.length;
>x : Symbol(x, Decl(generatorTypeCheck64.ts, 8, 11))
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(generatorTypeCheck64.ts, 8, 11))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
})();
}

View File

@ -0,0 +1,67 @@
//// [tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts] ////
=== Performance Stats ===
Type Count: 1,000
Instantiation count: 2,500
=== generatorTypeCheck64.ts ===
function* g3(): Generator<Generator<(x: string) => number>> {
>g3 : () => Generator<Generator<(x: string) => number>>
> : ^^^^^^
>x : string
> : ^^^^^^
yield function* () {
>yield function* () { yield x => x.length; } () : any
>function* () { yield x => x.length; } () : Generator<(x: string) => number, void, any>
> : ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>function* () { yield x => x.length; } : () => Generator<(x: string) => number, void, any>
> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
yield x => x.length;
>yield x => x.length : any
>x => x.length : (x: string) => number
> : ^ ^^^^^^^^^^^^^^^^^^^
>x : string
> : ^^^^^^
>x.length : number
> : ^^^^^^
>x : string
> : ^^^^^^
>length : number
> : ^^^^^^
} ()
}
function* g4(): Iterator<Iterable<(x: string) => number>> {
>g4 : () => Iterator<Iterable<(x: string) => number>>
> : ^^^^^^
>x : string
> : ^^^^^^
yield (function* () {
>yield (function* () { yield (x) => x.length; })() : any
>(function* () { yield (x) => x.length; })() : Generator<(x: string) => number, void, any>
> : ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>(function* () { yield (x) => x.length; }) : () => Generator<(x: string) => number, void, any>
> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>function* () { yield (x) => x.length; } : () => Generator<(x: string) => number, void, any>
> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
yield (x) => x.length;
>yield (x) => x.length : any
>(x) => x.length : (x: string) => number
> : ^ ^^^^^^^^^^^^^^^^^^^
>x : string
> : ^^^^^^
>x.length : number
> : ^^^^^^
>x : string
> : ^^^^^^
>length : number
> : ^^^^^^
})();
}

View File

@ -0,0 +1,15 @@
// @strict: true
// @target: esnext
// @noEmit: true
function* g3(): Generator<Generator<(x: string) => number>> {
yield function* () {
yield x => x.length;
} ()
}
function* g4(): Iterator<Iterable<(x: string) => number>> {
yield (function* () {
yield (x) => x.length;
})();
}

View File

@ -0,0 +1,33 @@
/// <reference path="fourslash.ts" />
// @strict: true
//// declare function createMachine<T>(config: {
//// initial: keyof T;
//// states: {
//// [K in keyof T]: {
//// on?: Record<string, keyof T>;
//// };
//// };
//// }): void;
////
//// createMachine({
//// initial: "a",
//// states: {
//// a: {
//// on: {
//// NEXT: "/*1*/",
//// },
//// },
//// b: {
//// on: {
//// NEXT: "/*2*/",
//// },
//// },
//// },
//// });
verify.completions({
marker: ["1", "2"],
exact: ["a", "b"]
})

View File

@ -25,7 +25,7 @@
goTo.marker("1");
edit.insert(`x`)
verify.completions({ exact: ["MORNING", "LUNCH_TIME", "ALOHA"] });
verify.completions({ exact: ["ALOHAx", "MORNING", "LUNCH_TIME", "ALOHA"] });
verify.getSemanticDiagnostics([{
code: 2322,
message: `Type 'RaiseActionObject<{ type: "ALOHAx"; }>' is not assignable to type 'RaiseActionObject<GreetingEvent>'.\n Type '{ type: "ALOHAx"; }' is not assignable to type 'GreetingEvent'.\n Type '{ type: "ALOHAx"; }' is not assignable to type '{ type: "ALOHA"; }'.\n Types of property 'type' are incompatible.\n Type '"ALOHAx"' is not assignable to type '"ALOHA"'.`,