Skip to content
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

Inconsistency in Generic Inference for Private Properties #58470

Open
lsby opened this issue May 8, 2024 · 4 comments
Open

Inconsistency in Generic Inference for Private Properties #58470

lsby opened this issue May 8, 2024 · 4 comments

Comments

@lsby
Copy link

lsby commented May 8, 2024

πŸ”Ž Search Terms

d.ts, dts, private, type inference, generics

πŸ•— Version & Regression Information

  • This is the behavior in every version

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.4.5#code/MYGwhgzhAECyCeBhcUCMAeAggPmgbwChpoAHAJwEsA3MAFwFNowAuaTAbiOmAHsA7CLTIBXYLR5kAFCzYBKfF2K0AFhQgA6MNAC8TLgF8Ch0JBgJkp1AGVhAIyy56ADwZ8AJmaQoIGHAsO08CSMABqoOnBeljb2fMIAtrb0ZI4u9O6eFmjoFHwAZsnQTrgA-EXQrHz0VMkEAPR10GHQatBxibX1jQDuyunQKowAKkH0VsCUJLTcPPEkFCCFAObpyXT0MIMzZGQbJPxuuUvQbuq0MHkL9F0t07MU5wN9A6NMfHw8tHQU-NB5Ek9GORqOtoPF6B0yEwWnxAZEsj4TvRLnwHj8+MwbpIhjwmFQeBQ3DN8sIIOiADTQbqMMC7bjKMB8JZHOF8MDgv5kWbw7zhcQ80wAJlkBDc9BMdJMUAFUEFDgUxGBNAYTE4xF4AiEonEUhkmBFxm8Moggpi8ucrg8xrlfkIAVeIUFEXM3lNdnQ7SSKWgFvSVpdQpy+UKxWgZScFTa1VqDSaTtawj4AGsPt0+AQgA

πŸ’» Code

class MyClass1<A> {
  private a: A;
  constructor(a: A) {
    this.a = a
  }
}
class MyClass1Sub<A> extends MyClass1<A> {
}
type X1 = MyClass1Sub<number> extends MyClass1<infer x> ? x : never
// X1 is number

// when the TypeScript compiler generates the corresponding d.ts file
// it omits the type annotation for the private member a in the MyClass1 definition:
// (To avoid confusion, we are changing the name from MyClass1 to MyClass2)
declare class MyClass2<A> {
  private a;
  constructor(a: A)
}
class MyClass2Sub<A> extends MyClass2<A> {
}
type X2 = MyClass2Sub<number> extends MyClass2<infer x> ? x : never
// X2 is unknown

πŸ™ Actual behavior

Because the type of a is lost after compilation, X2 is inferred as unknown.

πŸ™‚ Expected behavior

Consider I am writing a package, ProjectA, where I have MyClass1 defined.
Now, after compiling and publishing ProjectA, I reference it in another package, ProjectB.
In ProjectA, I can infer the generics of MyClass1Sub, but not in ProjectB.
I want the type inference to be consistent before and after code compilation.

Additional information about the issue

I've noticed this issue: #52127.
I can also understand the rationale behind it: since private properties aren't accessed externally, there might seem to be no need to annotate their types.
However, I believe this viewpoint is one-sided. Besides external access, there are many situations where type annotation is necessary, such as the cases of inheritance and inference that I mentioned here.

@MartinJohns
Copy link
Contributor

such as the cases of inheritance

Inherited classes should not access private members. Removing the type is/was necessary for compatibility/implementation-hiding reasons: #38953 (comment)

This is somewhat a duplicate of #51489.

@lsby
Copy link
Author

lsby commented May 8, 2024

@MartinJohns

Alright, after reviewing the information you provided, I understand the complexity of this issue.

There are indeed several ways to address this problem, such as using protected or using a symbol to retain the type.

const xxx: unique symbol = Symbol()
class MyClass1<A> {
  declare [xxx]: A
  private a: A;
  constructor(a: A) {
    this.a = a
  }
}

However, one concern is that I test my code in ProjectA, everything works fine, but only after compiling, publishing, and then importing into ProjectB do I encounter the issue. This situation is troublesome because I have to go back and modify ProjectA.

Ideally, if such coding practices are not allowed, I would prefer to be notified of errors while writing ProjectA.

I expect consistency in behavior before and after code compilation.

@lsby lsby closed this as completed May 8, 2024
@lsby lsby reopened this May 8, 2024
@lsby lsby changed the title The type annotations for private members are omitted in the compiler-generated d.ts file, resulting in the inability to infer the generics of subclasses. Inconsistency in Generic Inference for Private Properties May 8, 2024
@snarbies
Copy link

snarbies commented May 8, 2024

So even though the property is private, it has observable structural implications that are lost in the generated typings.

Would the ideal be that private properties be hidden from inference machinery so that code like type X2 = MyClass2Sub<number> extends MyClass2<infer x> ? x : never would always yield never?

@lsby
Copy link
Author

lsby commented May 8, 2024

So even though the property is private, it has observable structural implications that are lost in the generated typings.

Would the ideal be that private properties be hidden from inference machinery so that code like type X2 = MyClass2Sub<number> extends MyClass2<infer x> ? x : never would always yield never?

Yes, I'd rather catch issues while writing code than discover them after publishing a package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants