Skip to content

Instantly share code, notes, and snippets.

@trueharuu
Last active December 6, 2022 17:40
Show Gist options
  • Save trueharuu/8243abe1ba45868c74b10435b4659603 to your computer and use it in GitHub Desktop.
Save trueharuu/8243abe1ba45868c74b10435b4659603 to your computer and use it in GitHub Desktop.
/* eslint-disable @typescript-eslint/ban-types */
export interface Struct<Static, Instance> {
self: Static & (new () => Instance);
build(): Static & (new () => Instance);
impl<U>(
builder: (constructor: Static) => U
): Struct<Static & ToStatic<Instance, U>, Instance & U>;
impl_static<U>(
builder: (self: Static & (new () => Instance)) => U
): Struct<Static & U, Instance>;
}
export type ToStatic<Self, U> = {
[P in keyof U]: U[P] extends (...args: infer P) => infer R
? (self: Self, ...args: P) => R
: (self: Self) => U[P];
};
export function struct<Instance, Static = {}>(): Struct<Static, Instance> {
return {
self: class {} as never,
build(): Static & (new () => Instance) {
return this.self as never;
},
impl<U>(
of: (constructor: Static) => U
): Struct<Static & ToStatic<Instance, U>, Instance & U> {
const i = of(this.self);
for (const key in i) {
this.self.prototype[key] = i[key];
this.self[key as never] = ((
self: Instance,
...args: Array<unknown>
): never => {
// @ts-expect-error ts(2864)
return of[key].bind(self as Instance)?.(...args) as never;
}) as never;
}
return this as never;
},
impl_static<U>(
builder: (self: Static & (new () => Instance)) => U
): Struct<Static & U, Instance> {
const of = builder(this.self);
for (const key in of) {
this.self[key as never] = of[key] as never;
}
return this as never;
},
};
}
interface Num {
value: number;
}
interface Add<Self> {
add(this: Self, value: Self): Self & this;
}
interface Sub<Self> {
sub(this: Self, value: Self): Self & this;
}
interface New<Self, U extends Array<unknown>> {
'new'(...args: U): Self;
}
const p = struct<Num>()
.impl_static<New<Num, [number]>>((self) => ({
new(int: number): Num {
const p = new self();
p.value = int;
return p;
},
}))
.impl<Add<Num>>((self) => ({
add(value): Add<Num> & Num {
return self.new(this.value + value.value) as never;
},
}))
.impl<Sub<Num>>((self) => ({
sub(value): Num & Sub<Num> {
return self.new(this.value - value.value) as never;
},
}))
.build();
p.new(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment