馃攷 Search Terms
Searched open and closed issues in this repository and the legacy microsoft/typescript-go repository for: declaration emit, shadowed/rebound generic, same name type/value parameter, merged symbol, typeof, type predicate, asserts, generated parameter suffix, TS1225, and TS2693.
Related reports inspected: #16313, #47561 / #47609, typescript-go#3757 / typescript-go#3761, and #58456. I did not find a report of a merged type/value parameter symbol causing a generated type-parameter name to replace a predicate/assertion target or a typeof value operand. The examples below also demonstrate silent capture of a different parameter.
馃晽 Version & Regression Information
Reproduced with native TypeScript on macOS arm64:
7.1.0-dev.20261009.1 (typescript@next at the time of reporting), revision 6ad8c56f9b5a9bb910046c56059296311adc24ba.
7.1.0-dev.20260929.1, revision 0681ef7fa3a2378ccf49645b6d5b7463bdca74bb.
I have not bisected older versions, so this report does not claim a regression boundary.
馃捇 Code
src/repro.ts:
export function predicate<value>(value: value) {
return function <value>(value: value): value is value & string {
return typeof value === "string";
};
}
export function query<value>(value: value) {
return function <value>(value: value): typeof value {
return value;
};
}
Compile with strict: true, target: "ESNext", module: "NodeNext", declaration: true, emitDeclarationOnly: true, rootDir: "src", and outDir: "dist". The source checks and declaration emit succeeds. Then check a consumer importing dist/repro.js, with skipLibCheck: false.
馃檨 Actual behavior
The emitted declarations are:
export declare function predicate<value>(value: value):
<value_1>(value: value_1) => value_1 is value_1 & string;
export declare function query<value>(value: value):
<value_1>(value: value_1) => typeof value_1;
The inner type parameter was renamed to value_1, but the value parameter declaration remains value. The emitter incorrectly changes references that need the value namespace:
- The predicate target produces TS1225: Cannot find parameter 'value_1'.
- The type-query operand produces TS2693: 'value_1' only refers to a type, but is being used as a value here.
asserts value and asserts value is value & string have the same incorrect target rewrite.
It can also produce a legal declaration with the wrong meaning. Add a second parameter:
export function silentQuery<value>(value: value) {
return function <value>(
value: value,
value_1: number
): typeof value {
return value;
};
}
export function silentPredicate<value>(value: value) {
return function <value>(
value: value,
value_1: unknown
): value is value & string {
return typeof value === "string";
};
}
Actual emit:
export declare function silentQuery<value>(value: value):
<value_1>(value: value_1, value_1: number) => typeof value_1;
export declare function silentPredicate<value>(value: value):
<value_1>(value: value_1, value_1: unknown) => value_1 is value_1 & string;
Both declarations check without declaration errors. The query now describes returning the second parameter's number type, although the implementation returns the first argument. The predicate now narrows the second argument, although the source predicate narrows the first. The same silent target change occurs with assertion predicates.
馃檪 Expected behavior
The generated type-parameter name should apply only to type references. For example:
export declare function predicate<value>(value: value):
<value_1>(value: value_1) => value is value_1 & string;
export declare function query<value>(value: value):
<value_1>(value: value_1) => typeof value;
Alternatively, a consistent rename of the value parameter declaration and all its value references would preserve the signature. The emitted API should retain the source's returned type and the predicate/assertion's parameter index.
Additional information about the issue
- Distinct generic/value names pass, even when the inner generic still shadows the outer generic.
- Distinct outer and inner generic names avoid the issue.
- An explicit outer return type containing the nested callable avoids it.
- An explicit type annotation only on an inner callable variable, followed by an inferred outer return, still reproduces it.
- A fresh shared name for the entire inner type/value pair avoids it.
- Returned arrow functions, returned object methods, nested generic callback types, and deeper nested signatures also reproduce the problem.
- Type queries in inner parameter annotations also reproduce it:
return function <value>(value: value, other: typeof value) { return value; } emits other: typeof value_1. A local annotated object type containing typeof value, returned by the inner function, can be corrupted too. An explicit inner return annotation is therefore not required for the type-query variant.
- Inferred non-nullish predicates preserve the value target in the tested nested case.
Potential cause at the September revision: the shared identifier rewriter in nodecopy.go uses typeParameterToName whenever a symbol has TypeParameter flags. Compatible type/value declarations can share that symbol. Both type queries and explicit predicate targets pass through that rewriter.
The reproductions were isolated with Codex and verified against both exact native compiler builds using separate source and emitted-declaration consumers. All source assertions passed; the corresponding declaration-consumer assertions expose the mismatches.
馃攷 Search Terms
Searched open and closed issues in this repository and the legacy
microsoft/typescript-gorepository for: declaration emit, shadowed/rebound generic, same name type/value parameter, merged symbol, typeof, type predicate, asserts, generated parameter suffix, TS1225, and TS2693.Related reports inspected: #16313, #47561 / #47609, typescript-go#3757 / typescript-go#3761, and #58456. I did not find a report of a merged type/value parameter symbol causing a generated type-parameter name to replace a predicate/assertion target or a
typeofvalue operand. The examples below also demonstrate silent capture of a different parameter.馃晽 Version & Regression Information
Reproduced with native TypeScript on macOS arm64:
7.1.0-dev.20261009.1(typescript@nextat the time of reporting), revision6ad8c56f9b5a9bb910046c56059296311adc24ba.7.1.0-dev.20260929.1, revision0681ef7fa3a2378ccf49645b6d5b7463bdca74bb.I have not bisected older versions, so this report does not claim a regression boundary.
馃捇 Code
src/repro.ts:Compile with
strict: true,target: "ESNext",module: "NodeNext",declaration: true,emitDeclarationOnly: true,rootDir: "src", andoutDir: "dist". The source checks and declaration emit succeeds. Then check a consumer importingdist/repro.js, withskipLibCheck: false.馃檨 Actual behavior
The emitted declarations are:
The inner type parameter was renamed to
value_1, but the value parameter declaration remainsvalue. The emitter incorrectly changes references that need the value namespace:asserts valueandasserts value is value & stringhave the same incorrect target rewrite.It can also produce a legal declaration with the wrong meaning. Add a second parameter:
Actual emit:
Both declarations check without declaration errors. The query now describes returning the second parameter's
numbertype, although the implementation returns the first argument. The predicate now narrows the second argument, although the source predicate narrows the first. The same silent target change occurs with assertion predicates.馃檪 Expected behavior
The generated type-parameter name should apply only to type references. For example:
Alternatively, a consistent rename of the value parameter declaration and all its value references would preserve the signature. The emitted API should retain the source's returned type and the predicate/assertion's parameter index.
Additional information about the issue
return function <value>(value: value, other: typeof value) { return value; }emitsother: typeof value_1. A local annotated object type containingtypeof value, returned by the inner function, can be corrupted too. An explicit inner return annotation is therefore not required for the type-query variant.Potential cause at the September revision: the shared identifier rewriter in nodecopy.go uses
typeParameterToNamewhenever a symbol hasTypeParameterflags. Compatible type/value declarations can share that symbol. Both type queries and explicit predicate targets pass through that rewriter.The reproductions were isolated with Codex and verified against both exact native compiler builds using separate source and emitted-declaration consumers. All source assertions passed; the corresponding declaration-consumer assertions expose the mismatches.