Description
Suggestion
π Search Terms
hash private type reference # namespace exist
β Viability Checklist
My suggestion meets these guidelines:
- 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 feature would agree with the rest of TypeScript's Design Goals.
β Suggestion
(To my knowledge) it is not possible to reference the type of a hashed private field, the same way you might reference a privately declared field.
Either enabling this syntax, or providing some intrinstic wrapper to access the type would be helpful in DRYing up code using hashed private fields.
Suggested syntaxes:
- Foo.#attrName
- PrivateHash<Foo, 'attrName'>
To be clear, my request is only to access the type of hashed private fields. The actual output JS and handling of hashed private fields should remain unchanged. Any newly supported syntax should be stripped out at transpile time.
π Motivating Example
Consider the following TS declaration of a class using two, differently declared private fields
class Person {
private _firstName: string;
#lastName: string;
constructor(firstName: string, lastName: string) {
this._firstName = firstName;
this.#lastName = lastName;
}
sayHello() {
return `Hello ${this._firstName} ${this.#lastName}`;
}
}
This is currently valid typescript and is fully supported.
In some cases, it may be that "names" aren't only strings. Perhaps they can be complicated types such as string | null | () => string | NameClass | ...
. In such cases, it is convenient to write that union once, and reference it.
For typescripts private
declarations, that is supported, but not for JS's hashed private fields.
class Person {
private _firstName: string | null | undefined | NameClass | some | complicated | union | of | types;
#lastName: string | null | undefined | NameClass | some | other | and | different | union | of | types;
constructor(
firstName: Person['_firstName'],
// FAILS WITH TYPESCRIPT
lastName: Person.#lastName, // OR PrivateHash<Person, 'lastName'> OR Person['#lastName']
) {
this._firstName = firstName;
this.#lastName = lastName;
}
}
π» Use Cases
This feature is primarily one of convenience, attempting to achieve "feature parity" with Typescripts private
fields.
If the goal is to only write the type once (DRY) then the best workaround, for now, is to declare a type/interface and reference that in both the attribute declaration and usages. Arguably that makes the attribute itself not the "source of truth" for it's own type.
type ComplicatedName = string | null | undefined | NameClass | some | other | and | different | union | of | types;
class Person {
#lastName: ComplicatedName;
constructor(lastName: CompicatedName) {
this.#lastName = lastName;
}
}
#
prefixed fields are actually a JS feature, and therefore usage is restricted in ways that typescript cannot control. In the example above, it is possible to reference the type outside the class as well:
const person = new Person('Joe', 'Schmoe');
// Legal in TS + JS
type FirstName = typeof person['_firstName'];
const firstName = person['_firstName'];
// Illegal in TS + JS
type LastName = typeof person.#lastName;
const lastName = person.#lastName;
In this case, I would be fine with TS disabling access (i.e. do not apply this feature request) to hashed private fields outside of the class itself, to mirror actual usage.