From 183578b151ea28f8e443e84f30ebb88cbd6946b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 23 Oct 2023 11:20:51 +0200 Subject: [PATCH 1/6] Include source node inferences in string literal completions deeper in arguments --- src/compiler/checker.ts | 20 +++++++++ src/compiler/types.ts | 1 + src/compiler/utilities.ts | 34 +++++++++++++++ src/services/completions.ts | 3 +- src/services/stringCompletions.ts | 14 +++---- src/services/utilities.ts | 41 +------------------ ...rredObjectWhenItsKeysAreUsedOutsideOfIt.ts | 33 +++++++++++++++ ...ErrorAfterStringCompletionsInNestedCall.ts | 2 +- ...rrorAfterStringCompletionsInNestedCall2.ts | 2 +- 9 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 tests/cases/fourslash/stringLiteralCompletionsWithinInferredObjectWhenItsKeysAreUsedOutsideOfIt.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 69933033d3929..b06b3776475c8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6,6 +6,7 @@ import { addRange, addRelatedInfo, addSyntheticLeadingComment, + addToSeen, AliasDeclarationNode, AllAccessorDeclarations, AmbientModuleDeclaration, @@ -254,6 +255,7 @@ import { getContainingClassStaticBlock, getContainingFunction, getContainingFunctionOrClassStaticBlock, + getContextualTypeFromParent, getDeclarationModifierFlagsFromSymbol, getDeclarationOfKind, getDeclarationsOfKind, @@ -1655,6 +1657,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getTypeOfPropertyOfContextualType, getFullyQualifiedName, getResolvedSignature: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal), + getContextualStringLiteralCompletionTypes, getCandidateSignaturesForStringLiteralCompletions, getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) => runWithoutResolvedSignatureCaching(node, () => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp)), getExpandedParameters, @@ -1835,6 +1838,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { typeHasCallOrConstructSignatures, }; + function getContextualStringLiteralCompletionTypes(expression: Expression) { + const seen = new Map(); + + return [ + ...getStringLiteralTypes(getContextualTypeFromParent(expression, checker, ContextFlags.None), seen), + ...getStringLiteralTypes(getContextualTypeFromParent(expression, checker, ContextFlags.Completions), seen), + ]; + } + + function getStringLiteralTypes(type: Type | undefined, uniques = new Map()): readonly StringLiteralType[] { + if (!type) return emptyArray; + // skip constraint + type = type.flags & TypeFlags.TypeParameter ? getBaseConstraintOfType(type) || type : type; + return type.flags & TypeFlags.Union ? flatMap((type as UnionType).types, t => getStringLiteralTypes(t, uniques)) : + type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value) ? [type as StringLiteralType] : emptyArray; + } + function getCandidateSignaturesForStringLiteralCompletions(call: CallLikeExpression, editingArgument: Node) { const candidatesSet = new Set(); const candidates: Signature[] = []; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3a45f9e277642..263e21f4afa08 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5033,6 +5033,7 @@ export interface TypeChecker { */ getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined; /** @internal */ getResolvedSignatureForSignatureHelp(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined; + /** @internal */ getContextualStringLiteralCompletionTypes(expression: Expression): StringLiteralType[]; /** @internal */ getCandidateSignaturesForStringLiteralCompletions(call: CallLikeExpression, editingArgument: Node): Signature[]; /** @internal */ getExpandedParameters(sig: Signature): readonly (readonly Symbol[])[]; /** @internal */ hasEffectiveRestParameter(sig: Signature): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0d1e4a4ac6836..298230cbd4869 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -78,6 +78,7 @@ import { ContainerFlags, contains, containsPath, + ContextFlags, createGetCanonicalFileName, createMultiMap, createScanner, @@ -118,6 +119,7 @@ import { EntityNameOrEntityNameExpression, EnumDeclaration, EqualityComparer, + EqualityOperator, equalOwnProperties, EqualsToken, equateValues, @@ -10464,3 +10466,35 @@ export function hasResolutionModeOverride(node: ImportTypeNode | ImportDeclarati } return !!getResolutionModeOverride(node.attributes); } + +/** @internal */ +export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator { + switch (kind) { + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + return true; + default: + return false; + } +} + +/** @internal */ +export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined { + const parent = walkUpParenthesizedExpressions(node.parent); + switch (parent.kind) { + case SyntaxKind.NewExpression: + return checker.getContextualType(parent as NewExpression, contextFlags); + case SyntaxKind.BinaryExpression: { + const { left, operatorToken, right } = parent as BinaryExpression; + return isEqualityOperatorKind(operatorToken.kind) + ? checker.getTypeAtLocation(node === right ? left : right) + : checker.getContextualType(node, contextFlags); + } + case SyntaxKind.CaseClause: + return checker.getTypeAtLocation((parent as CaseClause).parent.parent.expression); + default: + return checker.getContextualType(node, contextFlags); + } +} diff --git a/src/services/completions.ts b/src/services/completions.ts index d1f8bd04d4094..3bbee015ef5f1 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -105,7 +105,6 @@ import { getReplacementSpanForContextToken, getRootDeclaration, getSourceFileOfModule, - getSwitchedType, getSymbolId, getSynthesizedDeepClone, getTokenAtPosition, @@ -3109,7 +3108,7 @@ function getContextualType(previousToken: Node, position: number, sourceFile: So return checker.getContextualType(parent as Expression); case SyntaxKind.CaseKeyword: const caseClause = tryCast(parent, isCaseClause); - return caseClause ? getSwitchedType(caseClause, checker) : undefined; + return caseClause ? checker.getTypeAtLocation(caseClause.parent.parent.expression) : undefined; case SyntaxKind.OpenBraceToken: return isJsxExpression(parent) && !isJsxElement(parent.parent) && !isJsxFragment(parent.parent) ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined; default: diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 47ec1126728d0..cd1de0a55f6fa 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -388,7 +388,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL // }); return stringLiteralCompletionsForObjectLiteral(typeChecker, parent.parent); } - return fromContextualType() || fromContextualType(ContextFlags.None); + return toStringLiteralCompletionsFromTypes(typeChecker.getContextualStringLiteralCompletionTypes(node)); case SyntaxKind.ElementAccessExpression: { const { expression, argumentExpression } = parent as ElementAccessExpression; @@ -435,7 +435,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL const literals = contextualTypes.types.filter(literal => !tracker.hasValue(literal.value)); return { kind: StringLiteralCompletionKind.Types, types: literals, isNewIdentifier: false }; default: - return fromContextualType() || fromContextualType(ContextFlags.None); + return toStringLiteralCompletionsFromTypes(typeChecker.getContextualStringLiteralCompletionTypes(node)); } function fromUnionableLiteralType(grandParent: Node): StringLiteralCompletionsFromTypes | StringLiteralCompletionsFromProperties | undefined { @@ -479,14 +479,14 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL function fromContextualType(contextFlags: ContextFlags = ContextFlags.Completions): 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: diff --git a/src/services/utilities.ts b/src/services/utilities.ts index b14d16bd671c2..62dabd162d240 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -29,7 +29,6 @@ import { CompilerOptions, ConditionalExpression, contains, - ContextFlags, createPrinterWithRemoveCommentsOmitTrailingSemicolon, createRange, createScanner, @@ -59,7 +58,6 @@ import { EndOfFileToken, endsWith, ensureScriptKind, - EqualityOperator, escapeString, ExportAssignment, ExportDeclaration, @@ -88,6 +86,7 @@ import { FunctionLikeDeclaration, getAssignmentDeclarationKind, getCombinedNodeFlagsAlwaysIncludeJSDoc, + getContextualTypeFromParent, getDirectoryPath, getEmitModuleKind, getEmitScriptTarget, @@ -371,7 +370,6 @@ import { VariableDeclaration, visitEachChild, VoidExpression, - walkUpParenthesizedExpressions, YieldExpression, } from "./_namespaces/ts"; @@ -3359,25 +3357,6 @@ export function needsParentheses(expression: Expression): boolean { || (isAsExpression(expression) || isSatisfiesExpression(expression)) && isObjectLiteralExpression(expression.expression); } -/** @internal */ -export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined { - const parent = walkUpParenthesizedExpressions(node.parent); - switch (parent.kind) { - case SyntaxKind.NewExpression: - return checker.getContextualType(parent as NewExpression, contextFlags); - case SyntaxKind.BinaryExpression: { - const { left, operatorToken, right } = parent as BinaryExpression; - return isEqualityOperatorKind(operatorToken.kind) - ? checker.getTypeAtLocation(node === right ? left : right) - : checker.getContextualType(node, contextFlags); - } - case SyntaxKind.CaseClause: - return getSwitchedType(parent as CaseClause, checker); - default: - return checker.getContextualType(node, contextFlags); - } -} - /** @internal */ export function quote(sourceFile: SourceFile, preferences: UserPreferences, text: string): string { // Editors can pass in undefined or empty string - we want to infer the preference in those cases. @@ -3386,19 +3365,6 @@ export function quote(sourceFile: SourceFile, preferences: UserPreferences, text return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace(/'/g, "\\'").replace(/\\"/g, '"')}'` : quoted; } -/** @internal */ -export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator { - switch (kind) { - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - return true; - default: - return false; - } -} - /** @internal */ export function isStringLiteralOrTemplate(node: Node): node is StringLiteralLike | TemplateExpression | TaggedTemplateExpression { switch (node.kind) { @@ -3417,11 +3383,6 @@ export function hasIndexSignature(type: Type): boolean { return !!type.getStringIndexType() || !!type.getNumberIndexType(); } -/** @internal */ -export function getSwitchedType(caseClause: CaseClause, checker: TypeChecker): Type | undefined { - return checker.getTypeAtLocation(caseClause.parent.parent.expression); -} - /** @internal */ export const ANONYMOUS = "anonymous function"; diff --git a/tests/cases/fourslash/stringLiteralCompletionsWithinInferredObjectWhenItsKeysAreUsedOutsideOfIt.ts b/tests/cases/fourslash/stringLiteralCompletionsWithinInferredObjectWhenItsKeysAreUsedOutsideOfIt.ts new file mode 100644 index 0000000000000..55ce6d20995b7 --- /dev/null +++ b/tests/cases/fourslash/stringLiteralCompletionsWithinInferredObjectWhenItsKeysAreUsedOutsideOfIt.ts @@ -0,0 +1,33 @@ +/// + +// @strict: true + +//// declare function createMachine(config: { +//// initial: keyof T; +//// states: { +//// [K in keyof T]: { +//// on?: Record; +//// }; +//// }; +//// }): void; +//// +//// createMachine({ +//// initial: "a", +//// states: { +//// a: { +//// on: { +//// NEXT: "/*1*/", +//// }, +//// }, +//// b: { +//// on: { +//// NEXT: "/*2*/", +//// }, +//// }, +//// }, +//// }); + +verify.completions({ + marker: ["1", "2"], + exact: ["a", "b"] +}) diff --git a/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall.ts b/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall.ts index 6a9909c4a20e3..ce3d5fa967c32 100644 --- a/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall.ts +++ b/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall.ts @@ -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'.\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"'.`, diff --git a/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts b/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts index 2bb3bf259ee29..a8cbf876120c9 100644 --- a/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts +++ b/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts @@ -50,5 +50,5 @@ goTo.marker("1"); edit.insert(`x`) -verify.completions({ exact: ["FOO", "BAR"] }); +verify.completions({ exact: ["BARx", "FOO", "BAR"] }); verify.baselineSyntacticAndSemanticDiagnostics() \ No newline at end of file From 615284824f87b8f45b70bea46e48abfea0164ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 29 Aug 2024 11:43:12 +0200 Subject: [PATCH 2/6] move `NewExpression` case to the checker --- src/compiler/checker.ts | 5 ++++- src/compiler/utilities.ts | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 97bba1e0702d0..93d648a72d5dc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32148,7 +32148,10 @@ 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); + if (node === (parent as CallExpression | NewExpression).expression) { + return getContextualType(parent as CallExpression | NewExpression, contextFlags); + } + return getContextualTypeForArgument(parent as CallExpression | NewExpression, node); case SyntaxKind.Decorator: return getContextualTypeForDecorator(parent as Decorator); case SyntaxKind.TypeAssertionExpression: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0ad8df27a712c..d8822e14c65d7 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11896,8 +11896,6 @@ export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperat export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined { const parent = walkUpParenthesizedExpressions(node.parent); switch (parent.kind) { - case SyntaxKind.NewExpression: - return checker.getContextualType(parent as NewExpression, contextFlags); case SyntaxKind.BinaryExpression: { const { left, operatorToken, right } = parent as BinaryExpression; return isEqualityOperatorKind(operatorToken.kind) From 5daf0c0d75332d051fdba51540a3766bf3ee56e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 29 Aug 2024 18:31:49 +0200 Subject: [PATCH 3/6] exclude iffes from `getContextualType` --- src/compiler/checker.ts | 4 ++ .../reference/generatorTypeCheck64.symbols | 35 ++++++++++ .../reference/generatorTypeCheck64.types | 67 +++++++++++++++++++ .../yieldExpressions/generatorTypeCheck64.ts | 15 +++++ 4 files changed, 121 insertions(+) create mode 100644 tests/baselines/reference/generatorTypeCheck64.symbols create mode 100644 tests/baselines/reference/generatorTypeCheck64.types create mode 100644 tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 93d648a72d5dc..5cc762bccdf30 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32149,6 +32149,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: if (node === (parent as CallExpression | NewExpression).expression) { + if (getImmediatelyInvokedFunctionExpression(skipParentheses(node))) { + // iifes themselves can't be contextually-typed (unlike their parameters) + return undefined; + } return getContextualType(parent as CallExpression | NewExpression, contextFlags); } return getContextualTypeForArgument(parent as CallExpression | NewExpression, node); diff --git a/tests/baselines/reference/generatorTypeCheck64.symbols b/tests/baselines/reference/generatorTypeCheck64.symbols new file mode 100644 index 0000000000000..dd0656f99f5e8 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck64.symbols @@ -0,0 +1,35 @@ +//// [tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts] //// + +=== generatorTypeCheck64.ts === +function* g3(): Generator 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 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, --, --)) + + })(); +} + diff --git a/tests/baselines/reference/generatorTypeCheck64.types b/tests/baselines/reference/generatorTypeCheck64.types new file mode 100644 index 0000000000000..3bcc3d4917712 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck64.types @@ -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 number>> { +>g3 : () => Generator 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 number>> { +>g4 : () => Iterator 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 +> : ^^^^^^ + + })(); +} + diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts new file mode 100644 index 0000000000000..30e9e40d96f81 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck64.ts @@ -0,0 +1,15 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +function* g3(): Generator number>> { + yield function* () { + yield x => x.length; + } () +} + +function* g4(): Iterator number>> { + yield (function* () { + yield (x) => x.length; + })(); +} From 9422555e96c20305b7acd9bf08bd974cde52bcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 29 Aug 2024 20:48:21 +0200 Subject: [PATCH 4/6] move `BinaryExpression` case to the checker too --- src/compiler/checker.ts | 5 +++ src/compiler/utilities.ts | 6 ---- ...erConstrainedByLiteralToLiteral.errors.txt | 4 +-- ...rameterConstrainedByLiteralToLiteral.types | 8 ++--- .../reference/controlFlowGenericTypes.types | 16 ++++----- .../intersectionNarrowing.errors.txt | 4 +-- .../reference/intersectionNarrowing.types | 4 +-- .../intersectionsOfLargeUnions.types | 4 +-- .../intersectionsOfLargeUnions2.types | 4 +-- .../reference/recursiveTypeRelations.types | 4 +-- .../reference/trackedSymbolsNoCrash.types | 4 +-- .../reference/unknownControlFlow.errors.txt | 5 +-- .../reference/unknownControlFlow.types | 36 +++++++++---------- 13 files changed, 50 insertions(+), 54 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5cc762bccdf30..0a0f700f37805 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31565,6 +31565,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.AmpersandAmpersandToken: case SyntaxKind.CommaToken: return node === right ? getContextualType(binaryExpression, contextFlags) : undefined; + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + return getTypeOfExpression(node === right ? left : right); default: return undefined; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d8822e14c65d7..04eb30b8e0ff5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11896,12 +11896,6 @@ export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperat export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined { const parent = walkUpParenthesizedExpressions(node.parent); switch (parent.kind) { - case SyntaxKind.BinaryExpression: { - const { left, operatorToken, right } = parent as BinaryExpression; - return isEqualityOperatorKind(operatorToken.kind) - ? checker.getTypeAtLocation(node === right ? left : right) - : checker.getContextualType(node, contextFlags); - } case SyntaxKind.CaseClause: return checker.getTypeAtLocation((parent as CaseClause).parent.parent.expression); default: diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt index c5dc83ca01755..0857f317cae7c 100644 --- a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt @@ -1,4 +1,4 @@ -compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This comparison appears to be unintentional because the types 'T' and '"x"' have no overlap. +compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This comparison appears to be unintentional because the types '"a" | "b"' and '"x"' have no overlap. ==== compareTypeParameterConstrainedByLiteralToLiteral.ts (1 errors) ==== @@ -8,6 +8,6 @@ compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This co t === "a"; // Should be allowed t === "x"; // Should be error ~~~~~~~~~ -!!! error TS2367: This comparison appears to be unintentional because the types 'T' and '"x"' have no overlap. +!!! error TS2367: This comparison appears to be unintentional because the types '"a" | "b"' and '"x"' have no overlap. } \ No newline at end of file diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types index 44d78f175980e..86f35b077c8c0 100644 --- a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types @@ -12,16 +12,16 @@ function foo(t: T) { t === "a"; // Should be allowed >t === "a" : boolean > : ^^^^^^^ ->t : T -> : ^ +>t : "a" | "b" +> : ^^^^^^^^^ >"a" : "a" > : ^^^ t === "x"; // Should be error >t === "x" : boolean > : ^^^^^^^ ->t : T -> : ^ +>t : "a" | "b" +> : ^^^^^^^^^ >"x" : "x" > : ^^^ } diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types index 34aaf80709138..7bdd5cf7af918 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.types +++ b/tests/baselines/reference/controlFlowGenericTypes.types @@ -266,8 +266,8 @@ export function bounceAndTakeIfA(value: AB): AB { if (value === 'A') { >value === 'A' : boolean > : ^^^^^^^ ->value : AB -> : ^^ +>value : "A" | "B" +> : ^^^^^^^^^ >'A' : "A" > : ^^^ @@ -517,8 +517,8 @@ function get(key: K, obj: A): number { if (value !== null) { >value !== null : boolean > : ^^^^^^^ ->value : A[K] -> : ^^^^ +>value : number | null +> : ^^^^^^^^^^^^^ return value; >value : number @@ -729,8 +729,8 @@ class TableBaseEnum< if (iSpec === undefined) { >iSpec === undefined : boolean > : ^^^^^^^ ->iSpec : InternalSpec -> : ^^^^^^^^^^^^ +>iSpec : Record | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ @@ -852,8 +852,8 @@ function update(control : T | undefined, k if (control !== undefined) { >control !== undefined : boolean > : ^^^^^^^ ->control : T | undefined -> : ^^^^^^^^^^^^^ +>control : Control | undefined +> : ^^^^^^^^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ diff --git a/tests/baselines/reference/intersectionNarrowing.errors.txt b/tests/baselines/reference/intersectionNarrowing.errors.txt index 8c2f93d8284dd..17a7366c52dbe 100644 --- a/tests/baselines/reference/intersectionNarrowing.errors.txt +++ b/tests/baselines/reference/intersectionNarrowing.errors.txt @@ -1,4 +1,4 @@ -intersectionNarrowing.ts(36,16): error TS2367: This comparison appears to be unintentional because the types 'T & number' and 'string' have no overlap. +intersectionNarrowing.ts(36,16): error TS2367: This comparison appears to be unintentional because the types 'number' and 'string' have no overlap. ==== intersectionNarrowing.ts (1 errors) ==== @@ -39,6 +39,6 @@ intersectionNarrowing.ts(36,16): error TS2367: This comparison appears to be uni function f5(x: T & number) { const t1 = x === "hello"; // Should be an error ~~~~~~~~~~~~~ -!!! error TS2367: This comparison appears to be unintentional because the types 'T & number' and 'string' have no overlap. +!!! error TS2367: This comparison appears to be unintentional because the types 'number' and 'string' have no overlap. } \ No newline at end of file diff --git a/tests/baselines/reference/intersectionNarrowing.types b/tests/baselines/reference/intersectionNarrowing.types index 69f17ad48e2af..9bfbf34bbee64 100644 --- a/tests/baselines/reference/intersectionNarrowing.types +++ b/tests/baselines/reference/intersectionNarrowing.types @@ -110,8 +110,8 @@ function f5(x: T & number) { > : ^^^^^^^ >x === "hello" : boolean > : ^^^^^^^ ->x : T & number -> : ^^^^^^^^^^ +>x : number +> : ^^^^^^ >"hello" : "hello" > : ^^^^^^^ } diff --git a/tests/baselines/reference/intersectionsOfLargeUnions.types b/tests/baselines/reference/intersectionsOfLargeUnions.types index 9fd70c791c43d..1d50a8aaf5d0b 100644 --- a/tests/baselines/reference/intersectionsOfLargeUnions.types +++ b/tests/baselines/reference/intersectionsOfLargeUnions.types @@ -78,8 +78,8 @@ export function assertNodeTagName< > : ^^^^^^^ >nodeTagName : string > : ^^^^^^ ->tagName : T -> : ^ +>tagName : "symbol" | "animate" | "animateMotion" | "animateTransform" | "circle" | "clipPath" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "filter" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "marker" | "mask" | "metadata" | "mpath" | "path" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "set" | "stop" | "svg" | "switch" | "text" | "textPath" | "tspan" | "use" | "view" | keyof HTMLElementTagNameMap +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } return false; >false : false diff --git a/tests/baselines/reference/intersectionsOfLargeUnions2.types b/tests/baselines/reference/intersectionsOfLargeUnions2.types index 7216f5c7e2a10..b91e1d862ebd1 100644 --- a/tests/baselines/reference/intersectionsOfLargeUnions2.types +++ b/tests/baselines/reference/intersectionsOfLargeUnions2.types @@ -95,8 +95,8 @@ export function assertNodeTagName< > : ^^^^^^^ >nodeTagName : string > : ^^^^^^ ->tagName : T -> : ^ +>tagName : "symbol" | "animate" | "animateMotion" | "animateTransform" | "circle" | "clipPath" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "filter" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "marker" | "mask" | "metadata" | "mpath" | "path" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "set" | "stop" | "svg" | "switch" | "text" | "textPath" | "tspan" | "use" | "view" | keyof HTMLElementTagNameMap +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } return false; >false : false diff --git a/tests/baselines/reference/recursiveTypeRelations.types b/tests/baselines/reference/recursiveTypeRelations.types index 14fc3c854d501..d1d4677736344 100644 --- a/tests/baselines/reference/recursiveTypeRelations.types +++ b/tests/baselines/reference/recursiveTypeRelations.types @@ -66,8 +66,8 @@ export function css(styles: S, ...classNam if (arg == null) { >arg == null : boolean > : ^^^^^^^ ->arg : ClassNameArg -> : ^^^^^^^^^^^^^^^ +>arg : string | number | symbol | ClassNameObjectMap +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return null; } diff --git a/tests/baselines/reference/trackedSymbolsNoCrash.types b/tests/baselines/reference/trackedSymbolsNoCrash.types index 02ee07d283a42..edc94f027a950 100644 --- a/tests/baselines/reference/trackedSymbolsNoCrash.types +++ b/tests/baselines/reference/trackedSymbolsNoCrash.types @@ -1054,7 +1054,7 @@ export const isNodeOfType = > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ >kind : ast.SyntaxKind | undefined > : ^^^^^^^^^^^^^^^^^^^^^^^^^^ ->nodeType : NodeType -> : ^^^^^^^^ +>nodeType : ast.SyntaxKind +> : ^^^^^^^^^^^^^^ diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index e8f4f198a505a..63512fdf05065 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -4,10 +4,9 @@ unknownControlFlow.ts(290,11): error TS2345: Argument of type 'string' is not as unknownControlFlow.ts(291,5): error TS2345: Argument of type 'null' is not assignable to parameter of type 'never'. unknownControlFlow.ts(293,5): error TS2345: Argument of type 'null' is not assignable to parameter of type 'never'. unknownControlFlow.ts(323,9): error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. -unknownControlFlow.ts(341,9): error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. -==== unknownControlFlow.ts (7 errors) ==== +==== unknownControlFlow.ts (6 errors) ==== type T01 = {} & string; // {} & string type T02 = {} & 'a'; // 'a' type T03 = {} & object; // object @@ -361,8 +360,6 @@ unknownControlFlow.ts(341,9): error TS2367: This comparison appears to be uninte function fx4(value: T & ({} | null)) { if (value === 42) { - ~~~~~~~~~~~~ -!!! error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. value; // T & {} } else { diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index 2c84aa0d19d3e..f74a8275ee80c 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -380,8 +380,8 @@ function f22(x: T) { if (x !== undefined) { >x !== undefined : boolean > : ^^^^^^^ ->x : T -> : ^ +>x : {} | undefined +> : ^^^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ @@ -397,8 +397,8 @@ function f22(x: T) { if (x !== null) { >x !== null : boolean > : ^^^^^^^ ->x : T -> : ^ +>x : {} | undefined +> : ^^^^^^^^^^^^^^ x; // T >x : T @@ -414,14 +414,14 @@ function f22(x: T) { > : ^^^^^^^ >x !== undefined : boolean > : ^^^^^^^ ->x : T -> : ^ +>x : {} | undefined +> : ^^^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ >x !== null : boolean > : ^^^^^^^ ->x : T & {} -> : ^^^^^^ +>x : {} +> : ^^ x; // T & {} >x : T & {} @@ -435,8 +435,8 @@ function f22(x: T) { if (x != undefined) { >x != undefined : boolean > : ^^^^^^^ ->x : T -> : ^ +>x : {} | undefined +> : ^^^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ @@ -452,8 +452,8 @@ function f22(x: T) { if (x != null) { >x != null : boolean > : ^^^^^^^ ->x : T -> : ^ +>x : {} | undefined +> : ^^^^^^^^^^^^^^ x; // T & {} >x : NonNullable @@ -1255,8 +1255,8 @@ function fx3(value: T & ({} | null)) { if (value === 42) { >value === 42 : boolean > : ^^^^^^^ ->value : T & {} -> : ^^^^^^ +>value : {} +> : ^^ >42 : 42 > : ^^ @@ -1280,8 +1280,8 @@ function fx4(value: T & ({} | null)) { if (value === 42) { >value === 42 : boolean > : ^^^^^^^ ->value : T -> : ^ +>value : {} | null +> : ^^^^^^^^^ >42 : 42 > : ^^ @@ -1305,8 +1305,8 @@ function fx5(value: T & ({} | null)) { if (value === 42) { >value === 42 : boolean > : ^^^^^^^ ->value : T & ({} | null) -> : ^^^^^^^^^^^^^^^ +>value : {} | null +> : ^^^^^^^^^ >42 : 42 > : ^^ From 39ddd4460349ac8fb838662d074c5a1ecf9474db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 29 Aug 2024 22:51:05 +0200 Subject: [PATCH 5/6] move logic back to services --- src/compiler/checker.ts | 32 --------------- src/compiler/types.ts | 1 - src/compiler/utilities.ts | 26 ------------ src/services/completions.ts | 3 +- src/services/stringCompletions.ts | 14 ++++++- src/services/utilities.ts | 41 ++++++++++++++++++- ...erConstrainedByLiteralToLiteral.errors.txt | 4 +- ...rameterConstrainedByLiteralToLiteral.types | 8 ++-- .../reference/controlFlowGenericTypes.types | 16 ++++---- .../intersectionNarrowing.errors.txt | 4 +- .../reference/intersectionNarrowing.types | 4 +- .../intersectionsOfLargeUnions.types | 4 +- .../intersectionsOfLargeUnions2.types | 4 +- .../reference/recursiveTypeRelations.types | 4 +- .../reference/trackedSymbolsNoCrash.types | 4 +- .../reference/unknownControlFlow.errors.txt | 5 ++- .../reference/unknownControlFlow.types | 36 ++++++++-------- ...rrorAfterStringCompletionsInNestedCall2.ts | 2 +- 18 files changed, 103 insertions(+), 109 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a0f700f37805..e29eef785ac15 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6,7 +6,6 @@ import { addRange, addRelatedInfo, addSyntheticLeadingComment, - addToSeen, AliasDeclarationNode, AllAccessorDeclarations, AmbientModuleDeclaration, @@ -265,7 +264,6 @@ import { getContainingClassStaticBlock, getContainingFunction, getContainingFunctionOrClassStaticBlock, - getContextualTypeFromParent, getDeclarationModifierFlagsFromSymbol, getDeclarationOfKind, getDeclarationsOfKind, @@ -1755,7 +1753,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getTypeOfPropertyOfContextualType, getFullyQualifiedName, getResolvedSignature: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal), - getContextualStringLiteralCompletionTypes, getCandidateSignaturesForStringLiteralCompletions, getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) => runWithoutResolvedSignatureCaching(node, () => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp)), getExpandedParameters, @@ -1931,23 +1928,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getSymbolFlags, }; - function getContextualStringLiteralCompletionTypes(expression: Expression) { - const seen = new Map(); - - return [ - ...getStringLiteralTypes(getContextualTypeFromParent(expression, checker, ContextFlags.None), seen), - ...getStringLiteralTypes(getContextualTypeFromParent(expression, checker, ContextFlags.Completions), seen), - ]; - } - - function getStringLiteralTypes(type: Type | undefined, uniques = new Map()): readonly StringLiteralType[] { - if (!type) return emptyArray; - // skip constraint - type = type.flags & TypeFlags.TypeParameter ? getBaseConstraintOfType(type) || type : type; - return type.flags & TypeFlags.Union ? flatMap((type as UnionType).types, t => getStringLiteralTypes(t, uniques)) : - type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value) ? [type as StringLiteralType] : emptyArray; - } - function getCandidateSignaturesForStringLiteralCompletions(call: CallLikeExpression, editingArgument: Node) { const candidatesSet = new Set(); const candidates: Signature[] = []; @@ -31565,11 +31545,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.AmpersandAmpersandToken: case SyntaxKind.CommaToken: return node === right ? getContextualType(binaryExpression, contextFlags) : undefined; - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - return getTypeOfExpression(node === right ? left : right); default: return undefined; } @@ -32153,13 +32128,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getContextualTypeForAwaitOperand(parent as AwaitExpression, contextFlags); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: - if (node === (parent as CallExpression | NewExpression).expression) { - if (getImmediatelyInvokedFunctionExpression(skipParentheses(node))) { - // iifes themselves can't be contextually-typed (unlike their parameters) - return undefined; - } - return getContextualType(parent as CallExpression | NewExpression, contextFlags); - } return getContextualTypeForArgument(parent as CallExpression | NewExpression, node); case SyntaxKind.Decorator: return getContextualTypeForDecorator(parent as Decorator); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f4d0532c494e2..ec5aa60564eac 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5175,7 +5175,6 @@ export interface TypeChecker { */ getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined; /** @internal */ getResolvedSignatureForSignatureHelp(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined; - /** @internal */ getContextualStringLiteralCompletionTypes(expression: Expression): StringLiteralType[]; /** @internal */ getCandidateSignaturesForStringLiteralCompletions(call: CallLikeExpression, editingArgument: Node): Signature[]; /** @internal */ getExpandedParameters(sig: Signature): readonly (readonly Symbol[])[]; /** @internal */ hasEffectiveRestParameter(sig: Signature): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 04eb30b8e0ff5..fb4da5887538d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -78,7 +78,6 @@ import { ContainerFlags, contains, containsPath, - ContextFlags, createGetCanonicalFileName, createMultiMap, createScanner, @@ -119,7 +118,6 @@ import { EntityNameOrEntityNameExpression, EnumDeclaration, EqualityComparer, - EqualityOperator, equalOwnProperties, EqualsToken, equateValues, @@ -11878,27 +11876,3 @@ export const nodeCoreModules = new Set([ ...unprefixedNodeCoreModulesList.map(name => `node:${name}`), ...exclusivelyPrefixedNodeCoreModules, ]); - -/** @internal */ -export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator { - switch (kind) { - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - return true; - default: - return false; - } -} - -/** @internal */ -export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined { - const parent = walkUpParenthesizedExpressions(node.parent); - switch (parent.kind) { - case SyntaxKind.CaseClause: - return checker.getTypeAtLocation((parent as CaseClause).parent.parent.expression); - default: - return checker.getContextualType(node, contextFlags); - } -} diff --git a/src/services/completions.ts b/src/services/completions.ts index 639623e039673..65de51651a0f1 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -109,6 +109,7 @@ import { getResolvePackageJsonExports, getRootDeclaration, getSourceFileOfModule, + getSwitchedType, getSymbolId, getSynthesizedDeepClone, getTokenAtPosition, @@ -3259,7 +3260,7 @@ function getContextualType(previousToken: Node, position: number, sourceFile: So return checker.getContextualType(parent as Expression); case SyntaxKind.CaseKeyword: const caseClause = tryCast(parent, isCaseClause); - return caseClause ? checker.getTypeAtLocation(caseClause.parent.parent.expression) : undefined; + return caseClause ? getSwitchedType(caseClause, checker) : undefined; case SyntaxKind.OpenBraceToken: return isJsxExpression(parent) && !isJsxElement(parent.parent) && !isJsxFragment(parent.parent) ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined; default: diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index ad67d6e7929cd..cda6c70891eb9 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -27,6 +27,7 @@ import { CompletionEntry, CompletionEntryDetails, CompletionInfo, + concatenate, contains, containsPath, ContextFlags, @@ -83,6 +84,7 @@ import { isApplicableVersionedTypesKey, isArray, isCallExpression, + isCallLikeExpression, isIdentifier, isIdentifierText, isImportCall, @@ -411,7 +413,15 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL // }); return stringLiteralCompletionsForObjectLiteral(typeChecker, parent.parent); } - return toStringLiteralCompletionsFromTypes(typeChecker.getContextualStringLiteralCompletionTypes(node)); + if (findAncestor(parent.parent, isCallLikeExpression)) { + const uniques = new Map(); + 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; @@ -477,7 +487,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL return { kind: StringLiteralCompletionKind.Properties, symbols: uniques, hasIndexSignature: false }; default: - return toStringLiteralCompletionsFromTypes(typeChecker.getContextualStringLiteralCompletionTypes(node)); + return fromContextualType() || fromContextualType(ContextFlags.None); } function fromUnionableLiteralType(grandParent: Node): StringLiteralCompletionsFromTypes | StringLiteralCompletionsFromProperties | undefined { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e52fc1ceb9c35..0cabbecaa4c8b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -29,6 +29,7 @@ import { CompilerOptions, ConditionalExpression, contains, + ContextFlags, createRange, createScanner, createTextSpan, @@ -57,6 +58,7 @@ import { endsWith, ensureScriptKind, EqualityComparer, + EqualityOperator, equateStringsCaseInsensitive, equateStringsCaseSensitive, escapeString, @@ -90,7 +92,6 @@ import { getAssignmentDeclarationKind, getBaseFileName, getCombinedNodeFlagsAlwaysIncludeJSDoc, - getContextualTypeFromParent, getDirectoryPath, getEmitModuleKind, getEmitScriptTarget, @@ -386,6 +387,7 @@ import { VariableDeclaration, visitEachChild, VoidExpression, + walkUpParenthesizedExpressions, YieldExpression, } from "./_namespaces/ts.js"; @@ -3369,6 +3371,25 @@ export function needsParentheses(expression: Expression): boolean { || (isAsExpression(expression) || isSatisfiesExpression(expression)) && isObjectLiteralExpression(expression.expression); } +/** @internal */ +export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined { + const parent = walkUpParenthesizedExpressions(node.parent); + switch (parent.kind) { + case SyntaxKind.NewExpression: + return checker.getContextualType(parent as NewExpression, contextFlags); + case SyntaxKind.BinaryExpression: { + const { left, operatorToken, right } = parent as BinaryExpression; + return isEqualityOperatorKind(operatorToken.kind) + ? checker.getTypeAtLocation(node === right ? left : right) + : checker.getContextualType(node, contextFlags); + } + case SyntaxKind.CaseClause: + return getSwitchedType(parent as CaseClause, checker); + default: + return checker.getContextualType(node, contextFlags); + } +} + /** @internal */ export function quote(sourceFile: SourceFile, preferences: UserPreferences, text: string): string { // Editors can pass in undefined or empty string - we want to infer the preference in those cases. @@ -3377,6 +3398,19 @@ export function quote(sourceFile: SourceFile, preferences: UserPreferences, text return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace(/'/g, () => "\\'").replace(/\\"/g, '"')}'` : quoted; } +/** @internal */ +export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator { + switch (kind) { + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + return true; + default: + return false; + } +} + /** @internal */ export function isStringLiteralOrTemplate(node: Node): node is StringLiteralLike | TemplateExpression | TaggedTemplateExpression { switch (node.kind) { @@ -3395,6 +3429,11 @@ export function hasIndexSignature(type: Type): boolean { return !!type.getStringIndexType() || !!type.getNumberIndexType(); } +/** @internal */ +export function getSwitchedType(caseClause: CaseClause, checker: TypeChecker): Type | undefined { + return checker.getTypeAtLocation(caseClause.parent.parent.expression); +} + /** @internal */ export const ANONYMOUS = "anonymous function"; diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt index 0857f317cae7c..c5dc83ca01755 100644 --- a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt @@ -1,4 +1,4 @@ -compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This comparison appears to be unintentional because the types '"a" | "b"' and '"x"' have no overlap. +compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This comparison appears to be unintentional because the types 'T' and '"x"' have no overlap. ==== compareTypeParameterConstrainedByLiteralToLiteral.ts (1 errors) ==== @@ -8,6 +8,6 @@ compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This co t === "a"; // Should be allowed t === "x"; // Should be error ~~~~~~~~~ -!!! error TS2367: This comparison appears to be unintentional because the types '"a" | "b"' and '"x"' have no overlap. +!!! error TS2367: This comparison appears to be unintentional because the types 'T' and '"x"' have no overlap. } \ No newline at end of file diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types index 86f35b077c8c0..44d78f175980e 100644 --- a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types @@ -12,16 +12,16 @@ function foo(t: T) { t === "a"; // Should be allowed >t === "a" : boolean > : ^^^^^^^ ->t : "a" | "b" -> : ^^^^^^^^^ +>t : T +> : ^ >"a" : "a" > : ^^^ t === "x"; // Should be error >t === "x" : boolean > : ^^^^^^^ ->t : "a" | "b" -> : ^^^^^^^^^ +>t : T +> : ^ >"x" : "x" > : ^^^ } diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types index 7bdd5cf7af918..34aaf80709138 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.types +++ b/tests/baselines/reference/controlFlowGenericTypes.types @@ -266,8 +266,8 @@ export function bounceAndTakeIfA(value: AB): AB { if (value === 'A') { >value === 'A' : boolean > : ^^^^^^^ ->value : "A" | "B" -> : ^^^^^^^^^ +>value : AB +> : ^^ >'A' : "A" > : ^^^ @@ -517,8 +517,8 @@ function get(key: K, obj: A): number { if (value !== null) { >value !== null : boolean > : ^^^^^^^ ->value : number | null -> : ^^^^^^^^^^^^^ +>value : A[K] +> : ^^^^ return value; >value : number @@ -729,8 +729,8 @@ class TableBaseEnum< if (iSpec === undefined) { >iSpec === undefined : boolean > : ^^^^^^^ ->iSpec : Record | undefined -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>iSpec : InternalSpec +> : ^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ @@ -852,8 +852,8 @@ function update(control : T | undefined, k if (control !== undefined) { >control !== undefined : boolean > : ^^^^^^^ ->control : Control | undefined -> : ^^^^^^^^^^^^^^^^^^^ +>control : T | undefined +> : ^^^^^^^^^^^^^ >undefined : undefined > : ^^^^^^^^^ diff --git a/tests/baselines/reference/intersectionNarrowing.errors.txt b/tests/baselines/reference/intersectionNarrowing.errors.txt index 17a7366c52dbe..8c2f93d8284dd 100644 --- a/tests/baselines/reference/intersectionNarrowing.errors.txt +++ b/tests/baselines/reference/intersectionNarrowing.errors.txt @@ -1,4 +1,4 @@ -intersectionNarrowing.ts(36,16): error TS2367: This comparison appears to be unintentional because the types 'number' and 'string' have no overlap. +intersectionNarrowing.ts(36,16): error TS2367: This comparison appears to be unintentional because the types 'T & number' and 'string' have no overlap. ==== intersectionNarrowing.ts (1 errors) ==== @@ -39,6 +39,6 @@ intersectionNarrowing.ts(36,16): error TS2367: This comparison appears to be uni function f5(x: T & number) { const t1 = x === "hello"; // Should be an error ~~~~~~~~~~~~~ -!!! error TS2367: This comparison appears to be unintentional because the types 'number' and 'string' have no overlap. +!!! error TS2367: This comparison appears to be unintentional because the types 'T & number' and 'string' have no overlap. } \ No newline at end of file diff --git a/tests/baselines/reference/intersectionNarrowing.types b/tests/baselines/reference/intersectionNarrowing.types index 9bfbf34bbee64..69f17ad48e2af 100644 --- a/tests/baselines/reference/intersectionNarrowing.types +++ b/tests/baselines/reference/intersectionNarrowing.types @@ -110,8 +110,8 @@ function f5(x: T & number) { > : ^^^^^^^ >x === "hello" : boolean > : ^^^^^^^ ->x : number -> : ^^^^^^ +>x : T & number +> : ^^^^^^^^^^ >"hello" : "hello" > : ^^^^^^^ } diff --git a/tests/baselines/reference/intersectionsOfLargeUnions.types b/tests/baselines/reference/intersectionsOfLargeUnions.types index 1d50a8aaf5d0b..9fd70c791c43d 100644 --- a/tests/baselines/reference/intersectionsOfLargeUnions.types +++ b/tests/baselines/reference/intersectionsOfLargeUnions.types @@ -78,8 +78,8 @@ export function assertNodeTagName< > : ^^^^^^^ >nodeTagName : string > : ^^^^^^ ->tagName : "symbol" | "animate" | "animateMotion" | "animateTransform" | "circle" | "clipPath" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "filter" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "marker" | "mask" | "metadata" | "mpath" | "path" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "set" | "stop" | "svg" | "switch" | "text" | "textPath" | "tspan" | "use" | "view" | keyof HTMLElementTagNameMap -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>tagName : T +> : ^ } return false; >false : false diff --git a/tests/baselines/reference/intersectionsOfLargeUnions2.types b/tests/baselines/reference/intersectionsOfLargeUnions2.types index b91e1d862ebd1..7216f5c7e2a10 100644 --- a/tests/baselines/reference/intersectionsOfLargeUnions2.types +++ b/tests/baselines/reference/intersectionsOfLargeUnions2.types @@ -95,8 +95,8 @@ export function assertNodeTagName< > : ^^^^^^^ >nodeTagName : string > : ^^^^^^ ->tagName : "symbol" | "animate" | "animateMotion" | "animateTransform" | "circle" | "clipPath" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "filter" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "marker" | "mask" | "metadata" | "mpath" | "path" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "set" | "stop" | "svg" | "switch" | "text" | "textPath" | "tspan" | "use" | "view" | keyof HTMLElementTagNameMap -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>tagName : T +> : ^ } return false; >false : false diff --git a/tests/baselines/reference/recursiveTypeRelations.types b/tests/baselines/reference/recursiveTypeRelations.types index d1d4677736344..14fc3c854d501 100644 --- a/tests/baselines/reference/recursiveTypeRelations.types +++ b/tests/baselines/reference/recursiveTypeRelations.types @@ -66,8 +66,8 @@ export function css(styles: S, ...classNam if (arg == null) { >arg == null : boolean > : ^^^^^^^ ->arg : string | number | symbol | ClassNameObjectMap -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arg : ClassNameArg +> : ^^^^^^^^^^^^^^^ return null; } diff --git a/tests/baselines/reference/trackedSymbolsNoCrash.types b/tests/baselines/reference/trackedSymbolsNoCrash.types index edc94f027a950..02ee07d283a42 100644 --- a/tests/baselines/reference/trackedSymbolsNoCrash.types +++ b/tests/baselines/reference/trackedSymbolsNoCrash.types @@ -1054,7 +1054,7 @@ export const isNodeOfType = > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ >kind : ast.SyntaxKind | undefined > : ^^^^^^^^^^^^^^^^^^^^^^^^^^ ->nodeType : ast.SyntaxKind -> : ^^^^^^^^^^^^^^ +>nodeType : NodeType +> : ^^^^^^^^ diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index 63512fdf05065..e8f4f198a505a 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -4,9 +4,10 @@ unknownControlFlow.ts(290,11): error TS2345: Argument of type 'string' is not as unknownControlFlow.ts(291,5): error TS2345: Argument of type 'null' is not assignable to parameter of type 'never'. unknownControlFlow.ts(293,5): error TS2345: Argument of type 'null' is not assignable to parameter of type 'never'. unknownControlFlow.ts(323,9): error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. +unknownControlFlow.ts(341,9): error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. -==== unknownControlFlow.ts (6 errors) ==== +==== unknownControlFlow.ts (7 errors) ==== type T01 = {} & string; // {} & string type T02 = {} & 'a'; // 'a' type T03 = {} & object; // object @@ -360,6 +361,8 @@ unknownControlFlow.ts(323,9): error TS2367: This comparison appears to be uninte function fx4(value: T & ({} | null)) { if (value === 42) { + ~~~~~~~~~~~~ +!!! error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. value; // T & {} } else { diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index f74a8275ee80c..2c84aa0d19d3e 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -380,8 +380,8 @@ function f22(x: T) { if (x !== undefined) { >x !== undefined : boolean > : ^^^^^^^ ->x : {} | undefined -> : ^^^^^^^^^^^^^^ +>x : T +> : ^ >undefined : undefined > : ^^^^^^^^^ @@ -397,8 +397,8 @@ function f22(x: T) { if (x !== null) { >x !== null : boolean > : ^^^^^^^ ->x : {} | undefined -> : ^^^^^^^^^^^^^^ +>x : T +> : ^ x; // T >x : T @@ -414,14 +414,14 @@ function f22(x: T) { > : ^^^^^^^ >x !== undefined : boolean > : ^^^^^^^ ->x : {} | undefined -> : ^^^^^^^^^^^^^^ +>x : T +> : ^ >undefined : undefined > : ^^^^^^^^^ >x !== null : boolean > : ^^^^^^^ ->x : {} -> : ^^ +>x : T & {} +> : ^^^^^^ x; // T & {} >x : T & {} @@ -435,8 +435,8 @@ function f22(x: T) { if (x != undefined) { >x != undefined : boolean > : ^^^^^^^ ->x : {} | undefined -> : ^^^^^^^^^^^^^^ +>x : T +> : ^ >undefined : undefined > : ^^^^^^^^^ @@ -452,8 +452,8 @@ function f22(x: T) { if (x != null) { >x != null : boolean > : ^^^^^^^ ->x : {} | undefined -> : ^^^^^^^^^^^^^^ +>x : T +> : ^ x; // T & {} >x : NonNullable @@ -1255,8 +1255,8 @@ function fx3(value: T & ({} | null)) { if (value === 42) { >value === 42 : boolean > : ^^^^^^^ ->value : {} -> : ^^ +>value : T & {} +> : ^^^^^^ >42 : 42 > : ^^ @@ -1280,8 +1280,8 @@ function fx4(value: T & ({} | null)) { if (value === 42) { >value === 42 : boolean > : ^^^^^^^ ->value : {} | null -> : ^^^^^^^^^ +>value : T +> : ^ >42 : 42 > : ^^ @@ -1305,8 +1305,8 @@ function fx5(value: T & ({} | null)) { if (value === 42) { >value === 42 : boolean > : ^^^^^^^ ->value : {} | null -> : ^^^^^^^^^ +>value : T & ({} | null) +> : ^^^^^^^^^^^^^^^ >42 : 42 > : ^^ diff --git a/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts b/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts index a8cbf876120c9..2bb3bf259ee29 100644 --- a/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts +++ b/tests/cases/fourslash/typeErrorAfterStringCompletionsInNestedCall2.ts @@ -50,5 +50,5 @@ goTo.marker("1"); edit.insert(`x`) -verify.completions({ exact: ["BARx", "FOO", "BAR"] }); +verify.completions({ exact: ["FOO", "BAR"] }); verify.baselineSyntacticAndSemanticDiagnostics() \ No newline at end of file From 6adcd82efe48ecbf24cb65a6b7692a0695696c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 29 Aug 2024 23:03:30 +0200 Subject: [PATCH 6/6] fmt --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3bf06705afc57..c59fd8db2006e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22056,7 +22056,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const [sourceType, targetType] = getTypeNamesForErrorDisplay(source, target); let generalizedSource = source; let generalizedSourceType = sourceType; - + // Don't generalize on 'never' - we really want the original type // to be displayed for use-cases like 'assertNever'. if (!(target.flags & TypeFlags.Never) && isLiteralType(source) && !typeCouldHaveTopLevelSingletonTypes(target)) {