From f06f0f80e3358f9d67d75568a8b6f860d33841b2 Mon Sep 17 00:00:00 2001 From: Edward Nikolaev Date: Tue, 25 Aug 2026 23:39:43 +0300 Subject: [PATCH] Fix crash when a call signature's type parameter cannot be reused The PseudoTypeKindSingleCallSignature branch of pseudoTypeToNode appended the result of reuseNode into the type parameter list without checking it. reuseNode returns nil whenever the recovery boundary in tryReuseExistingNodeHelper fails, and that nil survived into the NodeList, where the printer dereferenced it in NodeList.HasTrailingComma while deciding whether to write a trailing comma. Serialize the type parameter from the checker instead, mirroring the fallback reuseTypeNode already performs for type nodes. Two regression scenarios: a mixed list where the first type parameter reuses fine and the second requires the fallback, and the object-literal method counterpart, which rewrites the same constraint successfully and pins that the sibling branch stays panic-free. Carried over from microsoft/typescript-go#4846 (approved there; repo migrated). Fixes #63865 Co-Authored-By: Claude Fable 5 --- tsc/internal/checker/pseudotypenodebuilder.go | 10 +- tsc/internal/execute/tsctests/tsc_test.go | 69 ++++++ ...od-type-parameter-that-cannot-be-reused.js | 226 +++++++++++++++++ ...h-type-parameters-that-cannot-be-reused.js | 228 ++++++++++++++++++ 4 files changed, 532 insertions(+), 1 deletion(-) create mode 100644 tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-a-method-type-parameter-that-cannot-be-reused.js create mode 100644 tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-type-parameters-that-cannot-be-reused.js diff --git a/tsc/internal/checker/pseudotypenodebuilder.go b/tsc/internal/checker/pseudotypenodebuilder.go index 9daf4b686fc43..b70632f64ae40 100644 --- a/tsc/internal/checker/pseudotypenodebuilder.go +++ b/tsc/internal/checker/pseudotypenodebuilder.go @@ -184,7 +184,15 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod if len(d.TypeParameters) > 0 { res := make([]*ast.Node, 0, len(d.TypeParameters)) for _, tp := range d.TypeParameters { - res = append(res, b.reuseNode(tp.AsNode())) + node := tp.AsNode() + reused := b.reuseNode(node) + if reused == nil { + // Reuse fails when the constraint references a name inaccessible from the emit target. + // A nil here would reach the printer, which dereferences it in NodeList.HasTrailingComma. + b.ctx.tracker.ReportInferenceFallback(node) + reused = b.typeParameterToDeclaration(b.ch.getDeclaredTypeOfTypeParameter(node.Symbol())) + } + res = append(res, reused) } typeParams = b.f.NewNodeList(res) } diff --git a/tsc/internal/execute/tsctests/tsc_test.go b/tsc/internal/execute/tsctests/tsc_test.go index 43e4949f3ba3c..3a28d8fe25d87 100644 --- a/tsc/internal/execute/tsctests/tsc_test.go +++ b/tsc/internal/execute/tsctests/tsc_test.go @@ -845,6 +845,75 @@ func TestTscDeclarationEmit(t *testing.T) { }, }, }, + { + // The declaration signature computed for b.ts inlines `setField` structurally. Its type + // parameters cannot be reused, because rewriting `typeof state` hits an inaccessible + // `unique symbol`. The pseudo type node builder used to store the resulting nils in the + // type parameter list, which crashed the printer (NodeList.HasTrailingComma). + // Two type parameters, so the fallback is exercised past the first list slot. + subScenario: "dts signature update with type parameters that cannot be reused", + files: FileMap{ + "/home/src/workspaces/project/tsconfig.json": stringtestutil.Dedent(` + { + "compilerOptions": { + "strict": true, + "incremental": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + }, + }`), + "/home/src/workspaces/project/a.ts": stringtestutil.Dedent(` + declare const brand: unique symbol; + const state = { name: "", count: 0, [brand]: true }; + export const api = { + setField: (key: K, value: (typeof state)[K], tag?: Tag): void => { + state[key] = value; + void tag; + }, + };`), + "/home/src/workspaces/project/b.ts": stringtestutil.Dedent(` + import { api } from "./a"; + export const merged = { ...api };`), + }, + edits: []*tscEdit{ + newTscEdit("modify b.ts", func(sys *TestSys) { + sys.appendFile("/home/src/workspaces/project/b.ts", "\nexport const touched = 1;") + }), + }, + }, + { + // Same defect through the object-literal *method* branch: a shorthand method's type + // parameter list is rebuilt with reuseNode too, and a failed reuse used to leave a nil + // in the list the printer dereferences. + subScenario: "dts signature update with a method type parameter that cannot be reused", + files: FileMap{ + "/home/src/workspaces/project/tsconfig.json": stringtestutil.Dedent(` + { + "compilerOptions": { + "strict": true, + "incremental": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + }, + }`), + "/home/src/workspaces/project/a.ts": stringtestutil.Dedent(` + declare const brand: unique symbol; + const state = { name: "", count: 0, [brand]: true }; + export const api = { + setField(key: K, value: (typeof state)[K]): void { + state[key] = value; + }, + };`), + "/home/src/workspaces/project/b.ts": stringtestutil.Dedent(` + import { api } from "./a"; + export const merged = { ...api };`), + }, + edits: []*tscEdit{ + newTscEdit("modify b.ts", func(sys *TestSys) { + sys.appendFile("/home/src/workspaces/project/b.ts", "\nexport const touched = 1;") + }), + }, + }, { subScenario: "when using Windows paths and uppercase letters", files: FileMap{ diff --git a/tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-a-method-type-parameter-that-cannot-be-reused.js b/tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-a-method-type-parameter-that-cannot-be-reused.js new file mode 100644 index 0000000000000..655595612b459 --- /dev/null +++ b/tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-a-method-type-parameter-that-cannot-be-reused.js @@ -0,0 +1,226 @@ +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: +//// [/home/src/workspaces/project/a.ts] *new* +declare const brand: unique symbol; +const state = { name: "", count: 0, [brand]: true }; +export const api = { + setField(key: K, value: (typeof state)[K]): void { + state[key] = value; + }, +}; +//// [/home/src/workspaces/project/b.ts] *new* +import { api } from "./a"; +export const merged = { ...api }; +//// [/home/src/workspaces/project/tsconfig.json] *new* +{ + "compilerOptions": { + "strict": true, + "incremental": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + }, +} + +tsgo +ExitStatus:: Success +Output:: +//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib* +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +interface SymbolConstructor { + (desc?: string | number): symbol; + for(name: string): symbol; + readonly toStringTag: symbol; +} +declare var Symbol: SymbolConstructor; +interface Symbol { + readonly [Symbol.toStringTag]: string; +} +declare const console: { log(msg: any): void; }; +//// [/home/src/workspaces/project/a.js] *new* +const state = { name: "", count: 0, [brand]: true }; +export const api = { + setField(key, value) { + state[key] = value; + }, +}; + +//// [/home/src/workspaces/project/b.js] *new* +import { api } from "./a"; +export const merged = { ...api }; + +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *new* +{"version":"FakeTSVersion","root":[[2,3]],"fileNames":["lib.es2025.full.d.ts","./a.ts","./b.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"c368b55602be64a94c816c58ca537602-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField(key: K, value: (typeof state)[K]): void {\n state[key] = value;\n },\n};","de62ab78d5ba64a5b325bfebf0f2e8d3-import { api } from \"./a\";\nexport const merged = { ...api };"],"fileIdsList":[[2]],"options":{"skipLibCheck":true,"strict":true,"skipDefaultLibCheck":true},"referencedMap":[[3,1]]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *new* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./a.ts", + "./b.ts" + ], + "original": [ + 2, + 3 + ] + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./a.ts", + "./b.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./a.ts", + "version": "c368b55602be64a94c816c58ca537602-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField(key: K, value: (typeof state)[K]): void {\n state[key] = value;\n },\n};", + "signature": "c368b55602be64a94c816c58ca537602-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField(key: K, value: (typeof state)[K]): void {\n state[key] = value;\n },\n};", + "impliedNodeFormat": "CommonJS" + }, + { + "fileName": "./b.ts", + "version": "de62ab78d5ba64a5b325bfebf0f2e8d3-import { api } from \"./a\";\nexport const merged = { ...api };", + "signature": "de62ab78d5ba64a5b325bfebf0f2e8d3-import { api } from \"./a\";\nexport const merged = { ...api };", + "impliedNodeFormat": "CommonJS" + } + ], + "fileIdsList": [ + [ + "./a.ts" + ] + ], + "options": { + "skipLibCheck": true, + "strict": true, + "skipDefaultLibCheck": true + }, + "referencedMap": { + "./b.ts": [ + "./a.ts" + ] + }, + "size": 1373 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/tslibs/TS/Lib/lib.es2025.full.d.ts +*refresh* /home/src/workspaces/project/a.ts +*refresh* /home/src/workspaces/project/b.ts +Signatures:: + + +Edit [0]:: modify b.ts +//// [/home/src/workspaces/project/b.ts] *modified* +import { api } from "./a"; +export const merged = { ...api }; +export const touched = 1; + +tsgo +ExitStatus:: Success +Output:: +//// [/home/src/workspaces/project/b.js] *modified* +import { api } from "./a"; +export const merged = { ...api }; +export const touched = 1; + +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* +{"version":"FakeTSVersion","root":[[2,3]],"fileNames":["lib.es2025.full.d.ts","./a.ts","./b.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"c368b55602be64a94c816c58ca537602-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField(key: K, value: (typeof state)[K]): void {\n state[key] = value;\n },\n};",{"version":"6fa50051a3b923ced64e589b1e168966-import { api } from \"./a\";\nexport const merged = { ...api };\nexport const touched = 1;","signature":"a3ac76954f3eefab9ab3a4c186b22a76-export declare const merged: {\n setField(key: K, value: ({\n name: string;\n count: number;\n [brand]: boolean;\n })[K]): void;\n};\nexport declare const touched = 1;\n\n(40,6): error2527: The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527\nmerged\nunique symbol\n\n(40,6): error4023: Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named_4023\nmerged\nbrand\n\"/home/src/workspaces/project/a\"\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"skipLibCheck":true,"strict":true,"skipDefaultLibCheck":true},"referencedMap":[[3,1]]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./a.ts", + "./b.ts" + ], + "original": [ + 2, + 3 + ] + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./a.ts", + "./b.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./a.ts", + "version": "c368b55602be64a94c816c58ca537602-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField(key: K, value: (typeof state)[K]): void {\n state[key] = value;\n },\n};", + "signature": "c368b55602be64a94c816c58ca537602-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField(key: K, value: (typeof state)[K]): void {\n state[key] = value;\n },\n};", + "impliedNodeFormat": "CommonJS" + }, + { + "fileName": "./b.ts", + "version": "6fa50051a3b923ced64e589b1e168966-import { api } from \"./a\";\nexport const merged = { ...api };\nexport const touched = 1;", + "signature": "a3ac76954f3eefab9ab3a4c186b22a76-export declare const merged: {\n setField(key: K, value: ({\n name: string;\n count: number;\n [brand]: boolean;\n })[K]): void;\n};\nexport declare const touched = 1;\n\n(40,6): error2527: The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527\nmerged\nunique symbol\n\n(40,6): error4023: Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named_4023\nmerged\nbrand\n\"/home/src/workspaces/project/a\"\n", + "impliedNodeFormat": "CommonJS", + "original": { + "version": "6fa50051a3b923ced64e589b1e168966-import { api } from \"./a\";\nexport const merged = { ...api };\nexport const touched = 1;", + "signature": "a3ac76954f3eefab9ab3a4c186b22a76-export declare const merged: {\n setField(key: K, value: ({\n name: string;\n count: number;\n [brand]: boolean;\n })[K]): void;\n};\nexport declare const touched = 1;\n\n(40,6): error2527: The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527\nmerged\nunique symbol\n\n(40,6): error4023: Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named_4023\nmerged\nbrand\n\"/home/src/workspaces/project/a\"\n", + "impliedNodeFormat": 1 + } + } + ], + "fileIdsList": [ + [ + "./a.ts" + ] + ], + "options": { + "skipLibCheck": true, + "strict": true, + "skipDefaultLibCheck": true + }, + "referencedMap": { + "./b.ts": [ + "./a.ts" + ] + }, + "size": 2028 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/workspaces/project/b.ts +Signatures:: +(computed .d.ts) /home/src/workspaces/project/b.ts diff --git a/tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-type-parameters-that-cannot-be-reused.js b/tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-type-parameters-that-cannot-be-reused.js new file mode 100644 index 0000000000000..5582ea6a03513 --- /dev/null +++ b/tsc/testdata/baselines/reference/tsc/declarationEmit/dts-signature-update-with-type-parameters-that-cannot-be-reused.js @@ -0,0 +1,228 @@ +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: +//// [/home/src/workspaces/project/a.ts] *new* +declare const brand: unique symbol; +const state = { name: "", count: 0, [brand]: true }; +export const api = { + setField: (key: K, value: (typeof state)[K], tag?: Tag): void => { + state[key] = value; + void tag; + }, +}; +//// [/home/src/workspaces/project/b.ts] *new* +import { api } from "./a"; +export const merged = { ...api }; +//// [/home/src/workspaces/project/tsconfig.json] *new* +{ + "compilerOptions": { + "strict": true, + "incremental": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + }, +} + +tsgo +ExitStatus:: Success +Output:: +//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib* +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +interface SymbolConstructor { + (desc?: string | number): symbol; + for(name: string): symbol; + readonly toStringTag: symbol; +} +declare var Symbol: SymbolConstructor; +interface Symbol { + readonly [Symbol.toStringTag]: string; +} +declare const console: { log(msg: any): void; }; +//// [/home/src/workspaces/project/a.js] *new* +const state = { name: "", count: 0, [brand]: true }; +export const api = { + setField: (key, value, tag) => { + state[key] = value; + void tag; + }, +}; + +//// [/home/src/workspaces/project/b.js] *new* +import { api } from "./a"; +export const merged = { ...api }; + +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *new* +{"version":"FakeTSVersion","root":[[2,3]],"fileNames":["lib.es2025.full.d.ts","./a.ts","./b.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"639378dd59ee034bc0a35c6155f1f39d-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField: (key: K, value: (typeof state)[K], tag?: Tag): void => {\n state[key] = value;\n void tag;\n },\n};","de62ab78d5ba64a5b325bfebf0f2e8d3-import { api } from \"./a\";\nexport const merged = { ...api };"],"fileIdsList":[[2]],"options":{"skipLibCheck":true,"strict":true,"skipDefaultLibCheck":true},"referencedMap":[[3,1]]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *new* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./a.ts", + "./b.ts" + ], + "original": [ + 2, + 3 + ] + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./a.ts", + "./b.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./a.ts", + "version": "639378dd59ee034bc0a35c6155f1f39d-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField: (key: K, value: (typeof state)[K], tag?: Tag): void => {\n state[key] = value;\n void tag;\n },\n};", + "signature": "639378dd59ee034bc0a35c6155f1f39d-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField: (key: K, value: (typeof state)[K], tag?: Tag): void => {\n state[key] = value;\n void tag;\n },\n};", + "impliedNodeFormat": "CommonJS" + }, + { + "fileName": "./b.ts", + "version": "de62ab78d5ba64a5b325bfebf0f2e8d3-import { api } from \"./a\";\nexport const merged = { ...api };", + "signature": "de62ab78d5ba64a5b325bfebf0f2e8d3-import { api } from \"./a\";\nexport const merged = { ...api };", + "impliedNodeFormat": "CommonJS" + } + ], + "fileIdsList": [ + [ + "./a.ts" + ] + ], + "options": { + "skipLibCheck": true, + "strict": true, + "skipDefaultLibCheck": true + }, + "referencedMap": { + "./b.ts": [ + "./a.ts" + ] + }, + "size": 1428 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/tslibs/TS/Lib/lib.es2025.full.d.ts +*refresh* /home/src/workspaces/project/a.ts +*refresh* /home/src/workspaces/project/b.ts +Signatures:: + + +Edit [0]:: modify b.ts +//// [/home/src/workspaces/project/b.ts] *modified* +import { api } from "./a"; +export const merged = { ...api }; +export const touched = 1; + +tsgo +ExitStatus:: Success +Output:: +//// [/home/src/workspaces/project/b.js] *modified* +import { api } from "./a"; +export const merged = { ...api }; +export const touched = 1; + +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* +{"version":"FakeTSVersion","root":[[2,3]],"fileNames":["lib.es2025.full.d.ts","./a.ts","./b.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"639378dd59ee034bc0a35c6155f1f39d-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField: (key: K, value: (typeof state)[K], tag?: Tag): void => {\n state[key] = value;\n void tag;\n },\n};",{"version":"6fa50051a3b923ced64e589b1e168966-import { api } from \"./a\";\nexport const merged = { ...api };\nexport const touched = 1;","signature":"dc61af78c711b5b05731ae4cbc47c2ca-export declare const merged: {\n setField: (key: K, value: ({\n name: string;\n count: number;\n [brand]: boolean;\n })[K], tag?: Tag) => void;\n};\nexport declare const touched = 1;\n\n(40,6): error2527: The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527\nmerged\nunique symbol\n\n(40,6): error4023: Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named_4023\nmerged\nbrand\n\"/home/src/workspaces/project/a\"\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"skipLibCheck":true,"strict":true,"skipDefaultLibCheck":true},"referencedMap":[[3,1]]} +//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* +{ + "version": "FakeTSVersion", + "root": [ + { + "files": [ + "./a.ts", + "./b.ts" + ], + "original": [ + 2, + 3 + ] + } + ], + "fileNames": [ + "lib.es2025.full.d.ts", + "./a.ts", + "./b.ts" + ], + "fileInfos": [ + { + "fileName": "lib.es2025.full.d.ts", + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": "CommonJS", + "original": { + "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + } + }, + { + "fileName": "./a.ts", + "version": "639378dd59ee034bc0a35c6155f1f39d-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField: (key: K, value: (typeof state)[K], tag?: Tag): void => {\n state[key] = value;\n void tag;\n },\n};", + "signature": "639378dd59ee034bc0a35c6155f1f39d-declare const brand: unique symbol;\nconst state = { name: \"\", count: 0, [brand]: true };\nexport const api = {\n setField: (key: K, value: (typeof state)[K], tag?: Tag): void => {\n state[key] = value;\n void tag;\n },\n};", + "impliedNodeFormat": "CommonJS" + }, + { + "fileName": "./b.ts", + "version": "6fa50051a3b923ced64e589b1e168966-import { api } from \"./a\";\nexport const merged = { ...api };\nexport const touched = 1;", + "signature": "dc61af78c711b5b05731ae4cbc47c2ca-export declare const merged: {\n setField: (key: K, value: ({\n name: string;\n count: number;\n [brand]: boolean;\n })[K], tag?: Tag) => void;\n};\nexport declare const touched = 1;\n\n(40,6): error2527: The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527\nmerged\nunique symbol\n\n(40,6): error4023: Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named_4023\nmerged\nbrand\n\"/home/src/workspaces/project/a\"\n", + "impliedNodeFormat": "CommonJS", + "original": { + "version": "6fa50051a3b923ced64e589b1e168966-import { api } from \"./a\";\nexport const merged = { ...api };\nexport const touched = 1;", + "signature": "dc61af78c711b5b05731ae4cbc47c2ca-export declare const merged: {\n setField: (key: K, value: ({\n name: string;\n count: number;\n [brand]: boolean;\n })[K], tag?: Tag) => void;\n};\nexport declare const touched = 1;\n\n(40,6): error2527: The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527\nmerged\nunique symbol\n\n(40,6): error4023: Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named_4023\nmerged\nbrand\n\"/home/src/workspaces/project/a\"\n", + "impliedNodeFormat": 1 + } + } + ], + "fileIdsList": [ + [ + "./a.ts" + ] + ], + "options": { + "skipLibCheck": true, + "strict": true, + "skipDefaultLibCheck": true + }, + "referencedMap": { + "./b.ts": [ + "./a.ts" + ] + }, + "size": 2118 +} + +tsconfig.json:: +SemanticDiagnostics:: +*refresh* /home/src/workspaces/project/b.ts +Signatures:: +(computed .d.ts) /home/src/workspaces/project/b.ts