Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created May 2, 2022 15:03
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 mizchi/4df9cbdece33c34fe6f1b3331a9e0763 to your computer and use it in GitHub Desktop.
Save mizchi/4df9cbdece33c34fe6f1b3331a9e0763 to your computer and use it in GitHub Desktop.
type StructFor<T extends symbol, O> = O & { t: T };
function struct2<T extends symbol, O extends {}>(t: T): (o: O) => ({ t: T } & O) {
const builder = (o: O) => ({ t: t, ...o });
return builder;
}
function create<S extends symbol, O extends {}>(t: S, o: O): StructFor<S, O> {
return { t, ...o };
}
function se<O extends { t: symbol }>(o: O): string {
return JSON.stringify(o, (_, v) => typeof v === 'symbol' ? v.toString().replace(/^Symbol\((.*)\)$/, '$1') : v);
}
function de<O extends { t: symbol }>(str: string): O {
return JSON.parse(str, (key, value) => {
if (key === 't') {
return Symbol.for(value);
}
return value;
});
}
const SYMBOL_A: unique symbol = Symbol.for("a");
const SYMBOL_B: unique symbol = Symbol.for("b");
const SYMBOL_NESTED: unique symbol = Symbol.for("b");
type A = StructFor<typeof SYMBOL_A, {
x: number;
}>;
type B = StructFor<typeof SYMBOL_B, {
y: number;
}>;
type Nested = StructFor<typeof SYMBOL_NESTED, {
a: A,
B: B,
}>;
const o1: A = create(SYMBOL_A, { x: 1 });
const o2: B = create(SYMBOL_B, { y: 2 });
const resolved = Math.random() > 0.5 ? o1 : o2;
if (resolved.t === SYMBOL_A) {
console.log(resolved.x);
}
if (resolved.t === SYMBOL_B) {
console.log(resolved.y);
}
const nested = create(SYMBOL_NESTED, {
a: create(SYMBOL_A, { x: 1 }),
b: create(SYMBOL_B, { y: 2 }),
});
const sed = se(nested);
const revive = de(sed);
console.log(sed, revive, nested.t === revive.t);
const struct = <T extends {}>(label?: string) => {
const t: unique symbol = Symbol(label);
type S = typeof t;
type Ins = typeof t;
console.log("Ins");
// type InnerStruct =
const builder = (o: T) => ({
t: t as any,
...o
});
builder.t = t;
builder.is = <_R extends { t: symbol }>(ins: _R) => { ins.t === t; }
return builder as unknown as {
readonly t: S;
(t: T): T & {
readonly t: S;
};
is: (ins: any) => ins is T;
se: (ins: any) => string;
de: (ins: any) => string;
};
};
const Person = struct<{ name: string, age: number }>();
const Animal = struct<{ age: number, owner: string }>();
const person = Person({ name: 'John', age: 42 });
const animal = Animal({ age: 3, owner: 'John' });
const livings = [person, animal];
for (const live of livings) {
// if (live.t === Person.t) {
if (Person.is(live)) {
console.log(live.name);
}
if (live.t === Person.t) {
// console.log(live.name)
}
}
// console.log(person.name);
//--------------------------
export type Point = {
t: 'point',
readonly x: number;
readonly y: number;
}
type Display<T> = {
display(t: T): string;
}
function display<T>(this: T, impl: Display<T>) {
return impl.display(this);
}
function get_distance(this: Point, other: Point) {
return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
}
// usage
const display_for_point: Display<Point> = {
display(self: Point) {
return `(${self.x}, ${self.y})`;
}
}
const p1: Point = { t: 'point', x: 0, y: 0 } as const;
const p2: Point = { t: 'point', x: 3, y: 4 } as const;
const ret = get_distance.call(p1, p2);
console.log(ret);
console.log(display.call(p2, display_for_point));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment