Skip to content

Using types from private class properties results in any types in .d.ts files #60230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
bradzacher opened this issue Oct 15, 2024 · 4 comments Β· May be fixed by #61291
Open

Using types from private class properties results in any types in .d.ts files #60230

bradzacher opened this issue Oct 15, 2024 · 4 comments Β· May be fixed by #61291
Assignees
Labels
Bug A bug in TypeScript

Comments

@bradzacher
Copy link
Contributor

bradzacher commented Oct 15, 2024

πŸ”Ž Search Terms

class private any index access

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/KYDwDg9gTgLgBAYwDYEMDOa4DEITgbwCg4S4woBLANxRmDgH1yIxhYBPALjjRkoDsA5nAC8cAOTiA3MVIII-XlACuCGNAAUKKIO44IAbXFMoLNjHbiAugEoCskgF9CzoA

πŸ’» Code

export class Foo {
    private _property: string = '';
    constructor(arg: Foo['_property']) {
    }
}

πŸ™ Actual behavior

Generated .d.ts is:

export declare class Foo {
    private _property;
    constructor(arg: Foo['_property']);
}

πŸ™‚ Expected behavior

Generated .d.ts is:

export declare class Foo {
    private _property: string;
    constructor(arg: Foo['_property']);
}
// or
export declare class Foo {
    private _property;
    constructor(arg: string);
}
// or
/* Typescript Errors on the code to tell you it's going to generate an `any` */

Additional information about the issue

This behaviour is problematic because it creates a desync between consumers of .ts files and consumers of .d.ts files for the same code.

For example:

new Foo(1);

If the Foo type comes from the .ts file, then TS will error on this code as it can see that the argument type is string.
OTOH if the Foo type comes from the .d.ts file, then TS will NOT error on this code as it sees the argument type as any.


We have just uncovered this at Canva.
A user reported an error showing up in their IDE against our master branch (i.e. code that has passed CI as typechecked).
The code is structured such that the file with the error (A) is in a separate project to the file declaring the class (B).
This means that we have the exact scenario above where (A) consumes (B)'s .d.ts during our CLI builds, but (A) consume's (B)'s .ts within the IDE.

This pattern of declaring a type based on a private property's type is quite pervasive across our codebase and it's surprising that this is the first problem that's been actively revealed.

@jcalz
Copy link
Contributor

jcalz commented Oct 15, 2024

Essentially a duplicate #29631, #52127. TS intentionally outputs private members without a type in declarations. I imagine the pattern of using indexed access of private members for declaration files is not supported.

@davo-canva
Copy link

To preempt the question as to why this doesn't use parameter properties, the actual code looks more like this:

export class Foo {
  private _property: string;
  constructor(opts: { _property: Foo['_property'] }) {
    this._property = opts._property;
  }
}

@bradzacher
Copy link
Contributor Author

I imagine the pattern of using indexed access of private members for declaration files is not supported

To be clear -- I'm 110% fine if it is an intentionally not supported pattern!

However it should really be a semantic error to do this with declaration: true.
There's a massive footgun that exists here and it's very opaque to try and debug the weird behaviour and bugs that come from it.
If TS errored letting you know that this was going to generate an any then you'd know not to do this and to instead extract the type to an alias and reuse it, or to copy+paste the type.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Oct 30, 2024
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 5.8.0 milestone Oct 30, 2024
@MichaelMitchell-at
Copy link
Contributor

I could see this still being a footgun with isolated declarations, since it doesn't seem like the checker could easily issue a semantic error without doing some type analysis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
6 participants