From e755f394528f20f4721f8ae3c67d34c6db2c3e18 Mon Sep 17 00:00:00 2001 From: Mathias Picker <48158184+MathiasWP@users.noreply.github.com> Date: Wed, 5 Aug 2026 17:10:30 +0200 Subject: [PATCH] fix: preserve type-level syntax that was being dropped when printing --- .changeset/wild-jokes-listen.md | 5 +++++ .prettierignore | 1 + src/languages/ts/index.js | 12 ++++++++++++ src/languages/tsx/index.js | 5 +++++ test/samples/ts-type-level-syntax/expected.ts | 5 +++++ test/samples/ts-type-level-syntax/expected.ts.map | 11 +++++++++++ test/samples/ts-type-level-syntax/input.ts | 9 +++++++++ test/samples/tsx-element-type-arguments/expected.tsx | 3 +++ .../tsx-element-type-arguments/expected.tsx.map | 11 +++++++++++ test/samples/tsx-element-type-arguments/input.tsx | 3 +++ 10 files changed, 65 insertions(+) create mode 100644 .changeset/wild-jokes-listen.md create mode 100644 test/samples/ts-type-level-syntax/expected.ts create mode 100644 test/samples/ts-type-level-syntax/expected.ts.map create mode 100644 test/samples/ts-type-level-syntax/input.ts create mode 100644 test/samples/tsx-element-type-arguments/expected.tsx create mode 100644 test/samples/tsx-element-type-arguments/expected.tsx.map create mode 100644 test/samples/tsx-element-type-arguments/input.tsx diff --git a/.changeset/wild-jokes-listen.md b/.changeset/wild-jokes-listen.md new file mode 100644 index 00000000..1e150e0a --- /dev/null +++ b/.changeset/wild-jokes-listen.md @@ -0,0 +1,5 @@ +--- +'esrap': patch +--- + +fix: preserve type-level syntax that was being dropped when printing diff --git a/.prettierignore b/.prettierignore index a73deb65..26f99542 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,3 +6,4 @@ test/sandbox/_output.js expected.ts expected.jsx +expected.tsx diff --git a/src/languages/ts/index.js b/src/languages/ts/index.js index fb56022a..b46fc268 100644 --- a/src/languages/ts/index.js +++ b/src/languages/ts/index.js @@ -2086,6 +2086,11 @@ export default (options = {}) => { TSTypeQuery(node, context) { context.write('typeof '); context.visit(node.exprName); + + // instantiation expression (`typeof foo`) + if (node.typeArguments) { + context.visit(node.typeArguments); + } }, TSClassImplements(node, context) { @@ -2169,6 +2174,11 @@ export default (options = {}) => { }, TSMethodSignature(node, context) { + // accessor signatures (`get x(): string`, `set x(value: string)`) + if (node.kind === 'get' || node.kind === 'set') { + write_keyword(context, node, node.kind, ' '); + } + if (node.computed) context.write('[', token_before(node.key.loc?.start)); context.visit(node.key); if (node.computed) context.write(']', token_at(node.key.loc?.end)); @@ -2204,6 +2214,8 @@ export default (options = {}) => { TSNamedTupleMember(node, context) { context.visit(node.label); + // optional element (`[a?: string]`) + if (node.optional) context.write('?'); context.write(': '); context.visit(node.elementType); }, diff --git a/src/languages/tsx/index.js b/src/languages/tsx/index.js index eb39a143..770901e4 100644 --- a/src/languages/tsx/index.js +++ b/src/languages/tsx/index.js @@ -35,6 +35,11 @@ export default (options) => ({ context.visit(node.name); + // explicit type arguments (` ... />`) + if (node.typeArguments) { + context.visit(node.typeArguments); + } + for (const attribute of node.attributes) { context.write(' '); context.visit(attribute); diff --git a/test/samples/ts-type-level-syntax/expected.ts b/test/samples/ts-type-level-syntax/expected.ts new file mode 100644 index 00000000..f09821bf --- /dev/null +++ b/test/samples/ts-type-level-syntax/expected.ts @@ -0,0 +1,5 @@ +type Tuple = [required: string, optional?: number, ...rest: boolean[]]; + +interface Accessors { get value(): string; set value(next: string); method(): void } + +type Instantiated = typeof identity; \ No newline at end of file diff --git a/test/samples/ts-type-level-syntax/expected.ts.map b/test/samples/ts-type-level-syntax/expected.ts.map new file mode 100644 index 00000000..a6822565 --- /dev/null +++ b/test/samples/ts-type-level-syntax/expected.ts.map @@ -0,0 +1,11 @@ +{ + "version": 3, + "names": [], + "sources": [ + "input.js" + ], + "sourcesContent": [ + "type Tuple = [required: string, optional?: number, ...rest: boolean[]];\n\ninterface Accessors {\n\tget value(): string;\n\tset value(next: string);\n\tmethod(): void;\n}\n\ntype Instantiated = typeof identity;\n" + ], + "mappings": "KAAK,KAAK,IAAI,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,KAAK,IAAI,EAAE,OAAO;;UAEzD,SAAS,GAClB,GAAG,CAAC,KAAK,IAAI,MAAM,EACnB,GAAG,CAAC,KAAK,CAAC,IAAY,EAAN,MAAM,GACtB,MAAM,IAAI,IAAI;;KAGV,YAAY,UAAU,QAAQ,CAAC,MAAM" +} \ No newline at end of file diff --git a/test/samples/ts-type-level-syntax/input.ts b/test/samples/ts-type-level-syntax/input.ts new file mode 100644 index 00000000..88d4966a --- /dev/null +++ b/test/samples/ts-type-level-syntax/input.ts @@ -0,0 +1,9 @@ +type Tuple = [required: string, optional?: number, ...rest: boolean[]]; + +interface Accessors { + get value(): string; + set value(next: string); + method(): void; +} + +type Instantiated = typeof identity; diff --git a/test/samples/tsx-element-type-arguments/expected.tsx b/test/samples/tsx-element-type-arguments/expected.tsx new file mode 100644 index 00000000..7467c356 --- /dev/null +++ b/test/samples/tsx-element-type-arguments/expected.tsx @@ -0,0 +1,3 @@ +const list = items={items} />; +const nested = >{children}; +const plain =
; \ No newline at end of file diff --git a/test/samples/tsx-element-type-arguments/expected.tsx.map b/test/samples/tsx-element-type-arguments/expected.tsx.map new file mode 100644 index 00000000..0255bd20 --- /dev/null +++ b/test/samples/tsx-element-type-arguments/expected.tsx.map @@ -0,0 +1,11 @@ +{ + "version": 3, + "names": [], + "sources": [ + "input.js" + ], + "sourcesContent": [ + "const list = items={items} />;\nconst nested = >{children};\nconst plain =
;\n" + ], + "mappings": "AAAA,MAAM,AAAA,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK;AACvC,MAAM,AAAA,MAAM,IAAI,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK;AACpD,MAAM,AAAA,KAAK,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG" +} \ No newline at end of file diff --git a/test/samples/tsx-element-type-arguments/input.tsx b/test/samples/tsx-element-type-arguments/input.tsx new file mode 100644 index 00000000..57a09c11 --- /dev/null +++ b/test/samples/tsx-element-type-arguments/input.tsx @@ -0,0 +1,3 @@ +const list = items={items} />; +const nested = >{children}; +const plain =
;