From 9544b23a89bbaf37a8155aae5d6c962247d0c446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 7 Aug 2025 00:01:38 +0200 Subject: [PATCH] Consider `as const` expressions as potentially context sensitive --- src/compiler/checker.ts | 3 + .../intraExpressionInferences.errors.txt | 21 +++ .../reference/intraExpressionInferences.js | 39 ++++++ .../intraExpressionInferences.symbols | 74 +++++++++++ .../reference/intraExpressionInferences.types | 125 ++++++++++++++++++ .../intraExpressionInferences.ts | 21 +++ 6 files changed, 283 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 75be36474222f..095f6ab6fe711 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21140,6 +21140,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const { expression } = node as JsxExpression; return !!expression && isContextSensitive(expression); } + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + return isConstTypeReference((node as AsExpression | TypeAssertion).type) && isContextSensitive((node as AsExpression | TypeAssertion).expression); } return false; diff --git a/tests/baselines/reference/intraExpressionInferences.errors.txt b/tests/baselines/reference/intraExpressionInferences.errors.txt index 82da18c788e9b..7d59718973252 100644 --- a/tests/baselines/reference/intraExpressionInferences.errors.txt +++ b/tests/baselines/reference/intraExpressionInferences.errors.txt @@ -344,4 +344,25 @@ intraExpressionInferences.ts(133,26): error TS2339: Property 'nonexistent' does }, consumer: (val) => {}, }); + + // https://github.com/microsoft/TypeScript/issues/62220 + + declare function testAsConst(arg: { + input: D; + produce: (arg: D) => T; + consume: (arg: T) => number; + }): D; + + const testAsConstResult1 = testAsConst({ + input: 100, + produce: (arg) => arg.toString(), + consume: (arg) => arg.length, + } as const); + + // included for comparison with the above + const testAsConstResult2 = testAsConst({ + input: 100, + produce: (arg) => arg.toString(), + consume: (arg) => arg.length, + }); \ No newline at end of file diff --git a/tests/baselines/reference/intraExpressionInferences.js b/tests/baselines/reference/intraExpressionInferences.js index fbdd0e64bd30e..7cfb811630731 100644 --- a/tests/baselines/reference/intraExpressionInferences.js +++ b/tests/baselines/reference/intraExpressionInferences.js @@ -331,6 +331,27 @@ const distantRes = distant({ }, consumer: (val) => {}, }); + +// https://github.com/microsoft/TypeScript/issues/62220 + +declare function testAsConst(arg: { + input: D; + produce: (arg: D) => T; + consume: (arg: T) => number; +}): D; + +const testAsConstResult1 = testAsConst({ + input: 100, + produce: (arg) => arg.toString(), + consume: (arg) => arg.length, +} as const); + +// included for comparison with the above +const testAsConstResult2 = testAsConst({ + input: 100, + produce: (arg) => arg.toString(), + consume: (arg) => arg.length, +}); //// [intraExpressionInferences.js] @@ -518,6 +539,17 @@ var distantRes = distant({ }, consumer: function (val) { }, }); +var testAsConstResult1 = testAsConst({ + input: 100, + produce: function (arg) { return arg.toString(); }, + consume: function (arg) { return arg.length; }, +}); +// included for comparison with the above +var testAsConstResult2 = testAsConst({ + input: 100, + produce: function (arg) { return arg.toString(); }, + consume: function (arg) { return arg.length; }, +}); //// [intraExpressionInferences.d.ts] @@ -648,3 +680,10 @@ declare function distant(args: { consumer: (val: T) => unknown; }): T; declare const distantRes: number; +declare function testAsConst(arg: { + input: D; + produce: (arg: D) => T; + consume: (arg: T) => number; +}): D; +declare const testAsConstResult1: 100; +declare const testAsConstResult2: number; diff --git a/tests/baselines/reference/intraExpressionInferences.symbols b/tests/baselines/reference/intraExpressionInferences.symbols index f0a01515373d5..7aff23d9a8457 100644 --- a/tests/baselines/reference/intraExpressionInferences.symbols +++ b/tests/baselines/reference/intraExpressionInferences.symbols @@ -1076,3 +1076,77 @@ const distantRes = distant({ }); +// https://github.com/microsoft/TypeScript/issues/62220 + +declare function testAsConst(arg: { +>testAsConst : Symbol(testAsConst, Decl(intraExpressionInferences.ts, 329, 3)) +>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 29)) +>D : Symbol(D, Decl(intraExpressionInferences.ts, 333, 31)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 333, 35)) + + input: D; +>input : Symbol(input, Decl(intraExpressionInferences.ts, 333, 41)) +>D : Symbol(D, Decl(intraExpressionInferences.ts, 333, 31)) + + produce: (arg: D) => T; +>produce : Symbol(produce, Decl(intraExpressionInferences.ts, 334, 11)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 335, 12)) +>D : Symbol(D, Decl(intraExpressionInferences.ts, 333, 31)) +>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 29)) + + consume: (arg: T) => number; +>consume : Symbol(consume, Decl(intraExpressionInferences.ts, 335, 25)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 336, 12)) +>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 29)) + +}): D; +>D : Symbol(D, Decl(intraExpressionInferences.ts, 333, 31)) + +const testAsConstResult1 = testAsConst({ +>testAsConstResult1 : Symbol(testAsConstResult1, Decl(intraExpressionInferences.ts, 339, 5)) +>testAsConst : Symbol(testAsConst, Decl(intraExpressionInferences.ts, 329, 3)) + + input: 100, +>input : Symbol(input, Decl(intraExpressionInferences.ts, 339, 40)) + + produce: (arg) => arg.toString(), +>produce : Symbol(produce, Decl(intraExpressionInferences.ts, 340, 13)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 341, 12)) +>arg.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 341, 12)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + + consume: (arg) => arg.length, +>consume : Symbol(consume, Decl(intraExpressionInferences.ts, 341, 35)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 342, 12)) +>arg.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 342, 12)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + +} as const); +>const : Symbol(const) + +// included for comparison with the above +const testAsConstResult2 = testAsConst({ +>testAsConstResult2 : Symbol(testAsConstResult2, Decl(intraExpressionInferences.ts, 346, 5)) +>testAsConst : Symbol(testAsConst, Decl(intraExpressionInferences.ts, 329, 3)) + + input: 100, +>input : Symbol(input, Decl(intraExpressionInferences.ts, 346, 40)) + + produce: (arg) => arg.toString(), +>produce : Symbol(produce, Decl(intraExpressionInferences.ts, 347, 13)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 348, 12)) +>arg.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 348, 12)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + + consume: (arg) => arg.length, +>consume : Symbol(consume, Decl(intraExpressionInferences.ts, 348, 35)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 349, 12)) +>arg.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 349, 12)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + +}); + diff --git a/tests/baselines/reference/intraExpressionInferences.types b/tests/baselines/reference/intraExpressionInferences.types index 37c49da2a03a3..76d077fb1c40a 100644 --- a/tests/baselines/reference/intraExpressionInferences.types +++ b/tests/baselines/reference/intraExpressionInferences.types @@ -1743,3 +1743,128 @@ const distantRes = distant({ }); +// https://github.com/microsoft/TypeScript/issues/62220 + +declare function testAsConst(arg: { +>testAsConst : (arg: { input: D; produce: (arg: D) => T; consume: (arg: T) => number; }) => D +> : ^ ^^ ^^ ^^ ^^^^^ +>arg : { input: D; produce: (arg: D) => T; consume: (arg: T) => number; } +> : ^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^ + + input: D; +>input : D +> : ^ + + produce: (arg: D) => T; +>produce : (arg: D) => T +> : ^ ^^ ^^^^^ +>arg : D +> : ^ + + consume: (arg: T) => number; +>consume : (arg: T) => number +> : ^ ^^ ^^^^^ +>arg : T +> : ^ + +}): D; + +const testAsConstResult1 = testAsConst({ +>testAsConstResult1 : 100 +> : ^^^ +>testAsConst({ input: 100, produce: (arg) => arg.toString(), consume: (arg) => arg.length,} as const) : 100 +> : ^^^ +>testAsConst : (arg: { input: D; produce: (arg: D) => T; consume: (arg: T) => number; }) => D +> : ^ ^^ ^^ ^^ ^^^^^ +>{ input: 100, produce: (arg) => arg.toString(), consume: (arg) => arg.length,} as const : { readonly input: 100; readonly produce: (arg: 100) => string; readonly consume: (arg: string) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ +>{ input: 100, produce: (arg) => arg.toString(), consume: (arg) => arg.length,} : { readonly input: 100; readonly produce: (arg: 100) => string; readonly consume: (arg: string) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ + + input: 100, +>input : 100 +> : ^^^ +>100 : 100 +> : ^^^ + + produce: (arg) => arg.toString(), +>produce : (arg: 100) => string +> : ^ ^^^^^^^^^^^^^^^^ +>(arg) => arg.toString() : (arg: 100) => string +> : ^ ^^^^^^^^^^^^^^^^ +>arg : 100 +> : ^^^ +>arg.toString() : string +> : ^^^^^^ +>arg.toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ +>arg : 100 +> : ^^^ +>toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ + + consume: (arg) => arg.length, +>consume : (arg: string) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>(arg) => arg.length : (arg: string) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>arg : string +> : ^^^^^^ +>arg.length : number +> : ^^^^^^ +>arg : string +> : ^^^^^^ +>length : number +> : ^^^^^^ + +} as const); + +// included for comparison with the above +const testAsConstResult2 = testAsConst({ +>testAsConstResult2 : number +> : ^^^^^^ +>testAsConst({ input: 100, produce: (arg) => arg.toString(), consume: (arg) => arg.length,}) : number +> : ^^^^^^ +>testAsConst : (arg: { input: D; produce: (arg: D) => T; consume: (arg: T) => number; }) => D +> : ^ ^^ ^^ ^^ ^^^^^ +>{ input: 100, produce: (arg) => arg.toString(), consume: (arg) => arg.length,} : { input: number; produce: (arg: number) => string; consume: (arg: string) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ + + input: 100, +>input : number +> : ^^^^^^ +>100 : 100 +> : ^^^ + + produce: (arg) => arg.toString(), +>produce : (arg: number) => string +> : ^ ^^^^^^^^^^^^^^^^^^^ +>(arg) => arg.toString() : (arg: number) => string +> : ^ ^^^^^^^^^^^^^^^^^^^ +>arg : number +> : ^^^^^^ +>arg.toString() : string +> : ^^^^^^ +>arg.toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ +>arg : number +> : ^^^^^^ +>toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ + + consume: (arg) => arg.length, +>consume : (arg: string) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>(arg) => arg.length : (arg: string) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>arg : string +> : ^^^^^^ +>arg.length : number +> : ^^^^^^ +>arg : string +> : ^^^^^^ +>length : number +> : ^^^^^^ + +}); + diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferences.ts b/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferences.ts index 6d23f571d41f3..08650a97effc2 100644 --- a/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferences.ts +++ b/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferences.ts @@ -331,3 +331,24 @@ const distantRes = distant({ }, consumer: (val) => {}, }); + +// https://github.com/microsoft/TypeScript/issues/62220 + +declare function testAsConst(arg: { + input: D; + produce: (arg: D) => T; + consume: (arg: T) => number; +}): D; + +const testAsConstResult1 = testAsConst({ + input: 100, + produce: (arg) => arg.toString(), + consume: (arg) => arg.length, +} as const); + +// included for comparison with the above +const testAsConstResult2 = testAsConst({ + input: 100, + produce: (arg) => arg.toString(), + consume: (arg) => arg.length, +});