Summary
When a function has static property assignments (e.g. fn.prop = value), tsgo emits the merged namespace declaration into the .d.ts even when the function is marked @internal (with stripInternal: true) — the function declaration is correctly stripped, but the namespace leaks.
tsc (6.0.3) correctly omits the function in this case.
NOTE: i used copilot to write the description and verified the behavior in our codebase manually.
Steps to reproduce
input.ts:
/** @internal */
export function internalFn(): string {
return "hello";
}
internalFn.debugFlag = true;
export function publicFn(): void {}
publicFn.metadata = "public";
tsconfig.json:
{
"compilerOptions": {
"declaration": true,
"stripInternal": true,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./out",
"target": "ES2024"
},
"include": ["input.ts"]
}
Behavior with typescript@6.0
Using 6.0.3, you get what you expect in the d.ts file:
export declare function publicFn(): void;
export declare namespace publicFn {
var metadata: string;
}
// internalFn is not here, since it was stripped
Behavior with tsgo
With the @beta version, you get an unexpected result:
// the function was stripped, but "its prop" survived
export declare namespace internalFn {
var debugFlag: boolean;
}
// this is the same as above
export declare function publicFn(): void;
export declare namespace publicFn {
var metadata: string;
}
Maybe that's intentional now? I wasn't able to find this in the migration docs however.
Use case
In our codebase, we have this pattern with some React components for debugging purposes (e.g., some internal component MyComponent that does MyComponent.whyDidYouRender = true).
DynamicMainMenu.tsx:
import type { JSX } from "react";
import type { MainMenuProps } from "./MainMenu.js";
/** @internal */
export function DynamicMainMenu(props: MainMenuProps): JSX.Element {
return <></>;
}
DynamicMainMenu.whyDidYouRender = true;
becomes this (note the unused imports stay in the file):
DynamicMainMenu.d.ts:
import type { JSX } from "react";
import type { MainMenuProps } from "./MainMenu.js";
export declare namespace DynamicMainMenu {
var whyDidYouRender: boolean;
}
whereas in 6.0.3, it just became an empty export statement.
Summary
When a function has static property assignments (e.g.
fn.prop = value),tsgoemits the merged namespace declaration into the.d.tseven when the function is marked@internal(withstripInternal: true) — the function declaration is correctly stripped, but the namespace leaks.tsc(6.0.3) correctly omits the function in this case.NOTE: i used copilot to write the description and verified the behavior in our codebase manually.
Steps to reproduce
input.ts:
tsconfig.json:
{ "compilerOptions": { "declaration": true, "stripInternal": true, "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "./out", "target": "ES2024" }, "include": ["input.ts"] }Behavior with
typescript@6.0Using 6.0.3, you get what you expect in the
d.tsfile:Behavior with
tsgoWith the
@betaversion, you get an unexpected result:Maybe that's intentional now? I wasn't able to find this in the migration docs however.
Use case
In our codebase, we have this pattern with some React components for debugging purposes (e.g., some internal component
MyComponentthat doesMyComponent.whyDidYouRender = true).DynamicMainMenu.tsx:
becomes this (note the unused imports stay in the file):
DynamicMainMenu.d.ts:
whereas in 6.0.3, it just became an empty
exportstatement.