Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Last active May 12, 2022 21:39
Show Gist options
  • Save steveruizok/bd856d90e2635ccb1892e13cbfa8d18d to your computer and use it in GitHub Desktop.
Save steveruizok/bd856d90e2635ccb1892e13cbfa8d18d to your computer and use it in GitHub Desktop.
Stupidly complex TypeScript class solution
type Model<T> = {
[E in keyof T]: unknown;
}
export type DefaultModel = {
[k: string]: unknown;
};
class View<E extends Model<E> = Model<unknown>> {
model: E;
constructor(model: E) {
this.model = model;
}
update = <T extends E>(partialModel: Partial<T>) => {
Object.assign(this.model, partialModel);
};
}
// A
type ShipModel<T> = {
type: "ship";
} & Model<T>;
class ShipView<E extends ShipModel<E> = ShipModel<unknown>> extends View<E> {
static type = 'ship' as const
}
const shipA = new ShipView({
type: "ship"
});
shipA.update({ type: 'ship' })
// @ts-expect-error
shipA.update({ cat: 4 }) // No cat property on shipA!
const shipB = new ShipView({
type: "ship",
cat: 4
});
shipB.update({ cat: 4 }) // ok here
// B
type BoatModel<T> = {
type: "boat";
} & Model<T>;
class BoatView<E extends BoatModel<E> = BoatModel<unknown>> extends View<E> {
static type = 'boat' as const
}
const boat = new BoatView({
type: "boat",
});
// Instances...
type Views = ShipView | BoatView
const views: Views[] = [
shipA,
shipB,
boat,
]
// And their constructors...
interface ViewConstructor<A extends View> {
new(...args: A extends new (...args: infer U) => any ? U : never): A;
type: A["model"] extends { type: any } ? A["model"]["type"] : never;
}
const example: ViewConstructor<ShipView> = ShipView
const ViewCtors: ViewConstructor<Views>[] = [
ShipView, BoatView,
]
type A = ViewConstructor<ShipView>["type"] // "ship"
type B = ViewConstructor<ShipView>["type"] // "ship"
type C = ViewConstructor<BoatView>["type"] // "boat"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment