diff --git a/src/visitor.ts b/src/visitor.ts index bd4ee4b..8d79b1f 100755 --- a/src/visitor.ts +++ b/src/visitor.ts @@ -151,5 +151,32 @@ export function nodeVisitor(this: VisitorContext, node: ts.Node): ts.Node | unde factory.updateModuleDeclaration(node, node.modifiers, p, node.body), ); + /* + * TypeScript suspends the surrounding lexical environment while visiting a + * function's return type. Starting another lexical environment for an + * accessor inside that type currently triggers a compiler assertion + * (microsoft/TypeScript#58020). Visit bodyless accessors manually so import + * types in their annotations are still transformed without entering another + * parameter-list lexical environment. + */ + if (tsInstance.isGetAccessorDeclaration(node) && !node.body) + return factory.updateGetAccessorDeclaration( + node, + node.modifiers, + node.name, + node.parameters, + tsInstance.visitNode(node.type, this.getVisitor(), tsInstance.isTypeNode), + node.body, + ); + + if (tsInstance.isSetAccessorDeclaration(node) && !node.body) + return factory.updateSetAccessorDeclaration( + node, + node.modifiers, + node.name, + tsInstance.visitNodes(node.parameters, this.getVisitor(), tsInstance.isParameter), + node.body, + ); + return tsInstance.visitEachChild(node, this.getVisitor(), transformationContext); } diff --git a/test/projects/general/core/index.ts b/test/projects/general/core/index.ts index 1150ecd..618c518 100644 --- a/test/projects/general/core/index.ts +++ b/test/projects/general/core/index.ts @@ -24,6 +24,21 @@ const n: NoRuntimecodeHere = null as any; subs(2, 3); const a = new A(""); +export function MaySkipHooks() { + class SkippableOnce { + get skipHooks() { + return undefined; + } + } + + return SkippableOnce; +} + +export declare function AccessorTypes(): { + get value(): import("@utils/types-only").NoRuntimecodeHere; + set value(value: import("@utils/types-only").NoRuntimecodeHere); +}; + (async function () { const Logger = await (await import("@dynamic/logger")).Logger; const logger = new Logger(); diff --git a/test/tests/transformer/general.test.ts b/test/tests/transformer/general.test.ts index 52a9f59..12a3582 100755 --- a/test/tests/transformer/general.test.ts +++ b/test/tests/transformer/general.test.ts @@ -31,6 +31,7 @@ const getExpected = (tsInstance: typeof ts, fileName: string, original: string, describe(`Transformer -> General Tests`, () => { const projectRoot = path.join(projectsPaths, "general"); const tsConfigFile = path.join(projectRoot, "tsconfig.json"); + const coreIndexFile = path.join(projectRoot, "core/index.ts"); for (const [s, tsInstance] of tsModules) describe(`TypeScript ${s}`, () => { @@ -63,5 +64,15 @@ describe(`Transformer -> General Tests`, () => { test(`dts matches`, (t) => t.assert.strictEqual(transformed.dts, expected.dts)); }); } + + test(`handles accessors in declaration return types`, (t) => { + const dts = transformedFiles[coreIndexFile].dts; + + t.assert.match(dts, /export declare function MaySkipHooks\(\): \{[\s\S]*?get skipHooks\(\): any;[\s\S]*?\};/); + t.assert.match( + dts, + /export declare function AccessorTypes\(\): \{\s*get value\(\): import\("\.\.\/utils\/types-only"\)\.NoRuntimecodeHere;\s*set value\(value: import\("\.\.\/utils\/types-only"\)\.NoRuntimecodeHere\);\s*\};/, + ); + }); }); });