Description
π Search Terms
class static members type
class static type
class static interface
class static implements
class static members constraint
β 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
Add the ability to type classes static members using the satisfies
keyword.
Reuse satisfies
because it must not alter the effective type of the class, only constrained it.
π Motivating Example
TypeScript let you type constrained instanciated objects.
type Chatty = {
speak: () => string;
};
class Dog implements Chatty
{
private name: string;
public constructor(name: string)
{
this.name = name;
}
public speak(): string
{
return `${this.name} says "Woof!".`;
}
}
Let's imagine that we want to abstract object serialization, it would be natural to require it to have both a serialize
method, and a deserialize
static method.
Originally we would have only been able to define serialize
as it was impossible to type the static members of a class. Until now!
type Serializable = {
serialize(): string;
};
type ReversiblySerializable<T> = {
new(...args: any[]): T & Serializable;
deserialize(stringified: string): T;
};
class Point satisfies ReversiblySerializable<Point>
{
public x: number;
public y: number;
public constructor(x: number, y: number)
{
this.x = x;
this.y = y;
}
public serialize(): string
{
return `${this.x},${this.y}`;
}
public static deserialize(stringified: string): Point
{
const [x, y] = stringified.split(",");
return new Point(+x, +y);
}
}
π» Use Cases
-
What do you want to use this for?
Enforce generalized class static members to be properly normalized. -
What shortcomings exist with current approaches?
You cannot type static members cleanly. -
What workarounds are you using in the meantime?
I have a utility function that does nothing for the sole purpose of generating errors in TS in the file where the class is declared rather than where the constructor is used.
But it pollute the resulting JS code with pointless code at runtime.
// @ts-expect-error: unused parameter because it's not supposed to do anything at runtime.
export function staticImplements<T>(constructor: T): void {}
Example of use:
export class Point { /* ... */ }
staticImplements<ReversiblySerializable<Point>>(Point);