Skip to content

Instantly share code, notes, and snippets.

@jaxrtech
Created May 15, 2018 05:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaxrtech/b1f0e97988c093d7556f3f4fc4daeb44 to your computer and use it in GitHub Desktop.
Save jaxrtech/b1f0e97988c093d7556f3f4fc4daeb44 to your computer and use it in GitHub Desktop.
So you said you wanted typesafe AST nodes (based on TypeScript's compiler design)...
enum ShapeKind {
Circle,
Rectangle,
Square
}
type Shape =
| Circle
| Rectangle
| Square
type ShapeTypes = {
[ShapeKind.Circle]: Circle,
[ShapeKind.Square]: Square,
[ShapeKind.Rectangle]: Rectangle,
}
interface ShapeLike<TKind extends ShapeKind> {
kind: TKind;
}
interface Circle extends ShapeLike<ShapeKind.Circle> {
radius: number,
}
interface Rectangle extends ShapeLike<ShapeKind.Rectangle> {
width: number,
height: number,
}
interface Square extends ShapeLike<ShapeKind.Square> {
sideLength: number,
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type ShapeProps<T extends ShapeLike<ShapeKind>> = Omit<ShapeLike<ShapeKind>, 'kind'>
class ShapeObject<TKind extends ShapeKind, U extends ShapeLike<TKind>> implements ShapeLike<TKind> {
public kind: TKind
public constructor(kind: TKind, props: ShapeProps<U>) {
this.kind = kind;
Object.assign(this, props);
}
}
function createNode<
TKind extends keyof ShapeTypes,
T extends ShapeTypes[TKind] & ShapeLike<TKind>
> (kind: TKind, props: ShapeProps<T>): T {
const node = new ShapeObject<TKind, T>(kind, props);
return node as ShapeObject<TKind, T> & T;
}
function foo() {
const node = createNode(ShapeKind.Circle, { radius: 10 });
node.radius = 20;
console.log({ radius: node.radius });
}
foo();
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function area(shape: Shape) {
switch (shape.kind) {
case ShapeKind.Circle:
return Math.PI * Math.pow(shape.radius, 2);
case ShapeKind.Rectangle:
return shape.width * shape.height;
case ShapeKind.Square:
return shape.sideLength * shape.sideLength;
default: assertNever(shape);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment