Skip to content

Exported const type parameters is not renamed in constraint in another const in type declarations #56783

Open
@mcheshkov

Description

@mcheshkov

🔎 Search Terms

Generic methods
Constraints
Type parameters renaming
Type declarations

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play?target=7&allowSyntheticDefaultImports=false&ts=5.3.2#code/C4TwDgpgBACgTAHgDIBooCUB8UC8UlQBkUA3lANoDSUAlgHZQDWEIA9gGYYC6AXFHQFcAtgCMIAJygBfANwBYAFCLQkWAgAq2PGSq0GzNp3W9+wsZKmLFAY1Z0AzsCgAxVq1ylFUb1HYIAklAQAB7AEHQAJvawiCRSaP6YmAAUAJSk8V4+AOYBQaHhUWqJKelxKIqyVgq2Dk4AQgCGktpZ3n6BIWGR0fAI5VAlaRkVCj5QuZ0FPcVJw+WV8koKIWCs4k61jlBNAF4eJG2+eV2FvbHxg3NlmWM5J9NFMAHXI4vVq+ubdtsAIhCcVp3dpuPiuVijcYiZp8JriSE+aG7WGNXajKrLIA

💻 Code

type P2<L, R> = L & { [K in keyof R]: number };

type P<T> = { [K in keyof T]: number }

const Foo = {
    f<I extends P2<{}, I>>() {},
    g<I extends P<I>>() {},
};

const Bar = {
    f<I extends P2<{}, I>>() {},
    g<I extends P<I>>() {},
};

export const Baz = {
    f<I extends P2<{}, I>>() {},
    g<I extends P<I>>() {},
};

export const Def = {
    foo: Foo,
    bar: Bar,
    baz: Baz,
};

🙁 Actual behavior

Generated .d.ts on v5.3.2 looks like this:

type P<T> = {
    [K in keyof T]: number;
};
export declare const Baz: {
    f<I extends { [K in keyof I]: number; }>(): void;
    g<I_1 extends P<I_1>>(): void;
};
export declare const Def: {
    foo: {
        f<I extends { [K in keyof I]: number; }>(): void;
        g<I_1 extends P<I_1>>(): void;
    };
    bar: {
        f<I_2 extends { [K_1 in keyof I_2]: number; }>(): void;
        g<I_3 extends P<I_3>>(): void;
    };
    baz: {
        f<I_4 extends { [K in keyof I]: number; }>(): void;
        g<I_5 extends P<I_5>>(): void;
    };
};
export {};

Notice the line f<I_4 extends { [K in keyof I]: number; }>(): void; in baz, there's no I in scope.
Same line in bar, that's f<I_2 extends { [K_1 in keyof I_2]: number; }>(): void; has type parameters correctly renamed, which points to exported consts specifically.
g<I_5 extends P<I_5>>(): void; also have correct type params, so simple generic type like P is not enough.

🙂 Expected behavior

Generated .d.ts on v4.3.5 looks like this:

declare type P<T> = {
    [K in keyof T]: number;
};
export declare const Baz: {
    f<I extends { [K in keyof I]: number; }>(): void;
    g<I_1 extends P<I_1>>(): void;
};
export declare const Def: {
    foo: {
        f<I extends { [K in keyof I]: number; }>(): void;
        g<I_1 extends P<I_1>>(): void;
    };
    bar: {
        f<I_2 extends { [K_1 in keyof I_2]: number; }>(): void;
        g<I_3 extends P<I_3>>(): void;
    };
    baz: {
        f<I_4 extends { [K_2 in keyof I_4]: number; }>(): void;
        g<I_5 extends P<I_5>>(): void;
    };
};
export {};

Notice the line f<I_4 extends { [K_2 in keyof I_4]: number; }>(): void;, all type parameters nave been correctly renamed.

Additional information about the issue

Bisected commit passes sniff test: it introduces type caching and accessibility tracking, so maybe some of that triggers when emitting first constant, and poisons cache for visitDeclarationSubtree -> ... -> signatureToSignatureDeclarationHelper -> ... -> typeParameterToDeclaration call later

Origin of this issue is code generated by ts-proto with outputServices=generic-definitions option. See gist for example. Problem occuring here, on lines 70-79 in a.d.ts, where create and fromPartial have renamed type parameter, but original constraint (seemingly, from export declare const FooResponse)

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions