-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Description
🔍 Search Terms
private field, private modifier, type definition, class properties, type inference
✅ Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ Suggestion
With the widespread use of # private fields and methods in TypeScript, it is no longer possible to define or access the types of private class properties or methods. Using the private modifier, class properties could be referenced in type definitions, but this is no longer the case with # private fields. Introducing a way to leverage private fields and methods for type definition purposes would be highly beneficial.
📃 Motivating Example
This feature was useful for defining types based on class properties or methods without exposing them publicly. The lack of this capability with # private fields limits the flexibility of type inference in certain scenarios.
class Test {
#a = 3;
private a = 4;
private method(): Test['a'] { // it works
return this.a;
}
#method(): Test['#a'] { // it doesn't work
return this.#a;
}
}
export type WorkingType = Test['method']; // it works
export type NotWorkingType = Test['#method']; // it doesn't workThis feature was useful for defining types based on class properties or methods. For example in our scenarios defining types using return types of internal generator/factory methods.
export type A = ReturnType<Test['method']>; // it works
export type B = ReturnType<Test['#method']>; // it doesn't work💻 Use Cases
What do you want to use this for?
In our scenarios we create an internal method that generates a complex value and we want to use its return type to define a public property of the same class. To achieve this we need to access the method’s return type from within the class scope. Additionally, we would to expose this type.
This could be done using the property’s type (because the property is public), but so far we have always accessed it using the return type of the private method, as shown in the following example.
class Test {
public property: PropertyType | null = null;
// the return type is inferenced
private propertyGenerator() {
return 1;
}
// somewhere in the code
// this.property = this.propertyGenerator();
}
export type PropertyType = ReturnType<Test['propertyGenerator']>;Our desiderata:
Option 1:
True private (#) method type is accessible like the private (modifier) method type.
class Test {
public property: PropertyType | null = null;
#propertyGenerator() {
return 1;
}
}
export type PropertyType = ReturnType<Test['#propertyGenerator']>;Option 2:
True private (#) method type is accessible inside its class.
class Test {
public property: ReturnType<Test['#propertyGenerator']> | null = null; // Property '#propertyGenerator' does not exist on type 'Test'.
#propertyGenerator() {
return 1;
}
}
export type PropertyType = Test['property'];What shortcomings exist with current approaches?
The main issue is that we can use the private methods and fields of the class inside the class scope, but we cannot use their types in the same way and in the same scope. In addition, avoiding # means losing all the advantages it provides over private.
What workarounds are you using in the meantime?
Currently, the only way to achieve similar behavior is by either making fields/method private, without using #, but by doing this you lose all the advantages of using the # instead of private.
An alternative, is to define the type by hand.