diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 295b8b3c2578a..cafd07ea8ae14 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -725,7 +725,6 @@ import { isRightSideOfQualifiedNameOrPropertyAccess, isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, isSameEntityName, - isSatisfiesExpression, isSetAccessor, isSetAccessorDeclaration, isShorthandAmbientModuleSymbol, @@ -35288,8 +35287,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 92bd582bb16e2..80df86fd3ceae 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 374451a2aadb1..88396c282c4b0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8552,8 +8552,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 0ed1d1e839fcd..41d3d9e7a06d6 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7426,8 +7426,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 0000000000000..d714c30f4d564 --- /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 0000000000000..ee8c51392263b --- /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 0000000000000..8e45774cbf4c2 --- /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 0d31158d64227..59268272aa9df 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 f44c4deaa529e..1e10f9409ff5c 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 4a9abd2f9683c..eee81f12a2601 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 0000000000000..639595fe7abc9 --- /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 11276d5ca26c9..966e710394cfb 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)();