From 8d95ac59681bd8a341cefa82b30441eb7f3d9a8d Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 5 Nov 2024 17:14:25 -0800 Subject: [PATCH] Fix `getEffectiveCheckNode` (#60309) --- src/compiler/checker.ts | 7 +-- src/compiler/factory/utilities.ts | 3 +- src/compiler/types.ts | 3 +- tests/baselines/reference/api/typescript.d.ts | 5 +- .../arrowExpressionBodyJSDoc.errors.txt | 26 +++++++++++ .../arrowExpressionBodyJSDoc.symbols | 23 ++++++++++ .../reference/arrowExpressionBodyJSDoc.types | 43 +++++++++++++++++ ...ypeSatisfaction_errorLocations1.errors.txt | 13 +++++- .../typeSatisfaction_errorLocations1.symbols | 6 +++ .../typeSatisfaction_errorLocations1.types | 46 +++++++++++++++++++ .../compiler/arrowExpressionBodyJSDoc.ts | 20 ++++++++ .../typeSatisfaction_errorLocations1.ts | 3 ++ 12 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/arrowExpressionBodyJSDoc.errors.txt create mode 100644 tests/baselines/reference/arrowExpressionBodyJSDoc.symbols create mode 100644 tests/baselines/reference/arrowExpressionBodyJSDoc.types create mode 100644 tests/cases/compiler/arrowExpressionBodyJSDoc.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bdd6450cc32..d7c9a4c0f27 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -726,7 +726,6 @@ import { isRightSideOfQualifiedNameOrPropertyAccess, isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, isSameEntityName, - isSatisfiesExpression, isSetAccessor, isSetAccessorDeclaration, isShorthandAmbientModuleSymbol, @@ -35356,8 +35355,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getEffectiveCheckNode(argument: Expression): Expression { - argument = skipParentheses(argument); - return isSatisfiesExpression(argument) ? skipParentheses(argument.expression) : argument; + const flags = isInJSFile(argument) + ? OuterExpressionKinds.Parentheses | OuterExpressionKinds.Satisfies | OuterExpressionKinds.ExcludeJSDocTypeAssertion + : OuterExpressionKinds.Parentheses | OuterExpressionKinds.Satisfies; + return skipOuterExpressions(argument, flags); } function getSignatureApplicabilityError( diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 92bd582bb16..80df86fd3ce 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -637,8 +637,9 @@ export function isOuterExpression(node: Node, kinds: OuterExpressionKinds = Oute return (kinds & OuterExpressionKinds.Parentheses) !== 0; case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: - case SyntaxKind.SatisfiesExpression: return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; + case SyntaxKind.SatisfiesExpression: + return (kinds & (OuterExpressionKinds.TypeAssertions | OuterExpressionKinds.Satisfies)) !== 0; case SyntaxKind.ExpressionWithTypeArguments: return (kinds & OuterExpressionKinds.ExpressionsWithTypeArguments) !== 0; case SyntaxKind.NonNullExpression: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 191e4675136..a719a5b5048 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8557,8 +8557,9 @@ export const enum OuterExpressionKinds { NonNullAssertions = 1 << 2, PartiallyEmittedExpressions = 1 << 3, ExpressionsWithTypeArguments = 1 << 4, + Satisfies = 1 << 5, - Assertions = TypeAssertions | NonNullAssertions, + Assertions = TypeAssertions | NonNullAssertions | Satisfies, All = Parentheses | Assertions | PartiallyEmittedExpressions | ExpressionsWithTypeArguments, ExcludeJSDocTypeAssertion = 1 << 31, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 0c2a334a415..4a0b8a9e151 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7434,8 +7434,9 @@ declare namespace ts { NonNullAssertions = 4, PartiallyEmittedExpressions = 8, ExpressionsWithTypeArguments = 16, - Assertions = 6, - All = 31, + Satisfies = 32, + Assertions = 38, + All = 63, ExcludeJSDocTypeAssertion = -2147483648, } type ImmediatelyInvokedFunctionExpression = CallExpression & { diff --git a/tests/baselines/reference/arrowExpressionBodyJSDoc.errors.txt b/tests/baselines/reference/arrowExpressionBodyJSDoc.errors.txt new file mode 100644 index 00000000000..d714c30f4d5 --- /dev/null +++ b/tests/baselines/reference/arrowExpressionBodyJSDoc.errors.txt @@ -0,0 +1,26 @@ +mytest.js(6,44): error TS2322: Type 'string' is not assignable to type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to 'string'. +mytest.js(13,44): error TS2322: Type 'string' is not assignable to type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to 'string'. + + +==== mytest.js (2 errors) ==== + /** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ + const foo1 = value => /** @type {string} */({ ...value }); + ~~~~~~~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'T'. +!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'string'. + + /** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ + const foo2 = value => /** @type {string} */(/** @type {T} */({ ...value })); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'T'. +!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'string'. \ No newline at end of file diff --git a/tests/baselines/reference/arrowExpressionBodyJSDoc.symbols b/tests/baselines/reference/arrowExpressionBodyJSDoc.symbols new file mode 100644 index 00000000000..ee8c5139226 --- /dev/null +++ b/tests/baselines/reference/arrowExpressionBodyJSDoc.symbols @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/arrowExpressionBodyJSDoc.ts] //// + +=== mytest.js === +/** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ +const foo1 = value => /** @type {string} */({ ...value }); +>foo1 : Symbol(foo1, Decl(mytest.js, 5, 5)) +>value : Symbol(value, Decl(mytest.js, 5, 12)) +>value : Symbol(value, Decl(mytest.js, 5, 12)) + +/** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ +const foo2 = value => /** @type {string} */(/** @type {T} */({ ...value })); +>foo2 : Symbol(foo2, Decl(mytest.js, 12, 5)) +>value : Symbol(value, Decl(mytest.js, 12, 12)) +>value : Symbol(value, Decl(mytest.js, 12, 12)) + diff --git a/tests/baselines/reference/arrowExpressionBodyJSDoc.types b/tests/baselines/reference/arrowExpressionBodyJSDoc.types new file mode 100644 index 00000000000..8e45774cbf4 --- /dev/null +++ b/tests/baselines/reference/arrowExpressionBodyJSDoc.types @@ -0,0 +1,43 @@ +//// [tests/cases/compiler/arrowExpressionBodyJSDoc.ts] //// + +=== mytest.js === +/** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ +const foo1 = value => /** @type {string} */({ ...value }); +>foo1 : (value: T | undefined) => T +> : ^ ^^ ^^ ^^^^^ +>value => /** @type {string} */({ ...value }) : (value: T | undefined) => T +> : ^ ^^ ^^ ^^^^^ +>value : T | undefined +> : ^^^^^^^^^^^^^ +>({ ...value }) : string +> : ^^^^^^ +>{ ...value } : {} +> : ^^ +>value : T | undefined +> : ^^^^^^^^^^^^^ + +/** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ +const foo2 = value => /** @type {string} */(/** @type {T} */({ ...value })); +>foo2 : (value: T | undefined) => T +> : ^ ^^ ^^ ^^^^^ +>value => /** @type {string} */(/** @type {T} */({ ...value })) : (value: T | undefined) => T +> : ^ ^^ ^^ ^^^^^ +>value : T | undefined +> : ^^^^^^^^^^^^^ +>(/** @type {T} */({ ...value })) : string +> : ^^^^^^ +>({ ...value }) : T +> : ^ +>{ ...value } : {} +> : ^^ +>value : T | undefined +> : ^^^^^^^^^^^^^ + diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt index 0d31158d642..59268272aa9 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt @@ -31,9 +31,11 @@ typeSatisfaction_errorLocations1.ts(47,24): error TS2322: Type 'number' is not a typeSatisfaction_errorLocations1.ts(48,21): error TS2322: Type '{ a: number; }' is not assignable to type '{ a: true; }'. Types of property 'a' are incompatible. Type 'number' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(50,23): error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +typeSatisfaction_errorLocations1.ts(51,24): error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. -==== typeSatisfaction_errorLocations1.ts (22 errors) ==== +==== typeSatisfaction_errorLocations1.ts (24 errors) ==== const obj1 = { a: 1 }; const fn1 = (s: { a: true }) => {}; @@ -143,4 +145,13 @@ typeSatisfaction_errorLocations1.ts(48,21): error TS2322: Type '{ a: number; }' !!! error TS2322: Type '{ a: number; }' is not assignable to type '{ a: true; }'. !!! error TS2322: Types of property 'a' are incompatible. !!! error TS2322: Type 'number' is not assignable to type 'true'. + + ((): { a: true } => (({}) satisfies unknown) satisfies unknown)(); + ~~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +!!! related TS2728 typeSatisfaction_errorLocations1.ts:50:8: 'a' is declared here. + ((): { a: true } => ((({}) satisfies unknown)) satisfies unknown)(); + ~~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +!!! related TS2728 typeSatisfaction_errorLocations1.ts:51:8: 'a' is declared here. \ No newline at end of file diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols index f44c4deaa52..1e10f9409ff 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols @@ -129,3 +129,9 @@ function fn6(): number { >a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 47, 6)) >obj1 : Symbol(obj1, Decl(typeSatisfaction_errorLocations1.ts, 0, 5)) +((): { a: true } => (({}) satisfies unknown) satisfies unknown)(); +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 49, 6)) + +((): { a: true } => ((({}) satisfies unknown)) satisfies unknown)(); +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 50, 6)) + diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.types b/tests/baselines/reference/typeSatisfaction_errorLocations1.types index 4a9abd2f968..eee81f12a26 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.types +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.types @@ -384,3 +384,49 @@ function fn6(): number { >obj1 : { a: number; } > : ^^^^^^^^^^^^^^ +((): { a: true } => (({}) satisfies unknown) satisfies unknown)(); +>((): { a: true } => (({}) satisfies unknown) satisfies unknown)() : { a: true; } +> : ^^^^^ ^^^ +>((): { a: true } => (({}) satisfies unknown) satisfies unknown) : () => { a: true; } +> : ^^^^^^ +>(): { a: true } => (({}) satisfies unknown) satisfies unknown : () => { a: true; } +> : ^^^^^^ +>a : true +> : ^^^^ +>true : true +> : ^^^^ +>(({}) satisfies unknown) satisfies unknown : {} +> : ^^ +>(({}) satisfies unknown) : {} +> : ^^ +>({}) satisfies unknown : {} +> : ^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ + +((): { a: true } => ((({}) satisfies unknown)) satisfies unknown)(); +>((): { a: true } => ((({}) satisfies unknown)) satisfies unknown)() : { a: true; } +> : ^^^^^ ^^^ +>((): { a: true } => ((({}) satisfies unknown)) satisfies unknown) : () => { a: true; } +> : ^^^^^^ +>(): { a: true } => ((({}) satisfies unknown)) satisfies unknown : () => { a: true; } +> : ^^^^^^ +>a : true +> : ^^^^ +>true : true +> : ^^^^ +>((({}) satisfies unknown)) satisfies unknown : {} +> : ^^ +>((({}) satisfies unknown)) : {} +> : ^^ +>(({}) satisfies unknown) : {} +> : ^^ +>({}) satisfies unknown : {} +> : ^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ + diff --git a/tests/cases/compiler/arrowExpressionBodyJSDoc.ts b/tests/cases/compiler/arrowExpressionBodyJSDoc.ts new file mode 100644 index 00000000000..639595fe7ab --- /dev/null +++ b/tests/cases/compiler/arrowExpressionBodyJSDoc.ts @@ -0,0 +1,20 @@ +// @strict: true +// @noEmit: true +// @checkJs: true +// @allowJs: true + +// @filename: mytest.js + +/** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ +const foo1 = value => /** @type {string} */({ ...value }); + +/** + * @template T + * @param {T|undefined} value value or not + * @returns {T} result value + */ +const foo2 = value => /** @type {string} */(/** @type {T} */({ ...value })); \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts index 11276d5ca26..966e710394c 100644 --- a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts +++ b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts @@ -49,3 +49,6 @@ function fn6(): number { ((): { a: true } => ({}) satisfies unknown)(); ((): { a: true } => ({ a: 1 }) satisfies unknown)(); ((): { a: true } => obj1 satisfies unknown)(); + +((): { a: true } => (({}) satisfies unknown) satisfies unknown)(); +((): { a: true } => ((({}) satisfies unknown)) satisfies unknown)();