Skip to content

Instantly share code, notes, and snippets.

@trvswgnr
Last active November 10, 2024 22:28
Show Gist options
  • Save trvswgnr/94d41a49eb1eb8e5b106fe425a6ffec9 to your computer and use it in GitHub Desktop.
Save trvswgnr/94d41a49eb1eb8e5b106fe425a6ffec9 to your computer and use it in GitHub Desktop.
typescript traits
export const Traits = _Traits as unknown as (new <T>(...args: any[]) => T) & typeof _Traits;
function _Traits(): Mixed<unknown, unknown>;
function _Traits<M1>(m1: M1): Mixed<unknown, M1>;
function _Traits<M1, M2>(m1: M1, m2: M2): Mixed<unknown, M1 & M2>;
function _Traits<M1, M2, M3>(m1: M1, m2: M2, m3: M3): Mixed<unknown, M1 & M2 & M3>;
function _Traits<M1, M2, M3, M4>(m1: M1, m2: M2, m3: M3, m4: M4): Mixed<unknown, M1 & M2 & M3 & M4>;
function _Traits<M1, M2, M3, M4, M5>(m1: M1, m2: M2, m3: M3, m4: M4, m5: M5): Mixed<unknown, M1 & M2 & M3 & M4 & M5>;
function _Traits(...args: any[]) {
return args.reduce((c, m) => m(c));
}
export function dependency<T extends AbstractClass>(baseClass: T): Traitable<T>;
export function dependency<T extends AbstractClass>(baseClass: T) {
return new Traitable(baseClass);
}
export function TraitImpl<T>(f: (...args: any[]) => new (...args: any[]) => T): T {
return f as unknown as T;
}
type AbstractClass = abstract new (...args: any) => any;
type Mixed<Base, Mixin> = Base & Mixin & { new (...args: any[]): Base & Mixin };
class Traitable<T extends AbstractClass> {
private Base: T;
constructor(Base: T) {
this.Base = Base;
}
withTraits(): Mixed<T, unknown>;
withTraits<M1>(m1: M1): Mixed<T, M1>;
withTraits<M1, M2>(m1: M1, m2: M2): Mixed<T, M1 & M2>;
withTraits<M1, M2, M3>(m1: M1, m2: M2, m3: M3): Mixed<T, M1 & M2 & M3>;
withTraits<M1, M2, M3, M4>(m1: M1, m2: M2, m3: M3, m4: M4): Mixed<T, M1 & M2 & M3 & M4>;
withTraits<M1, M2, M3, M4, M5>(m1: M1, m2: M2, m3: M3, m4: M4, m5: M5): Mixed<T, M1 & M2 & M3 & M4 & M5>;
withTraits(...args: any[]) {
return args.reduce((c, m) => m(c), this.Base);
}
}
export type Option<T> = T | null;
export type Result<T, E> = [T, null] | [null, E];
export type AnyClass = new (...args: any[]) => any;
export interface Clone<T> {
clone(): T;
}
export interface Debug {
toString(): string;
}
export interface Display {
toString(): string;
}
export interface DoubleEndedIterator<Item> {
nextBack(): Option<Item>;
advanceBackBy(n: number): Result<void, number>;
rfold<B>(init: B, f: (b: B, a: Item) => B): B;
nthBack(n: number): Option<Item>;
rfind<P extends (a: Item) => boolean>(predicate: P): Option<Item>;
}
export interface Extend<A> {
extend<T extends Iterable<A>>(it: T): void;
extendOne(a: A): void;
}
export interface Rev<I> {
rev(): Rev<I>;
}
export interface IntoIterator<Item> {
intoIter(): Iterable<Item>;
}
/**
* A decorator that enforces static interface implementation at compile time.
* This decorator can be used to ensure that a class implements static methods and properties
* defined in one or more interfaces.
*
* @example
* ```ts
* interface StaticInterface {
* staticMethod(): void;
* }
*
* @staticImplements<StaticInterface>
* class MyClass {
* static staticMethod() { }
* }
* ```
*/
export function staticImplements<A>(ctor: A): void;
export function staticImplements<A, B>(ctor: A & B): void;
export function staticImplements<A, B, C>(ctor: A & B & C): void;
export function staticImplements(ctor: any): void {}
// interface StaticInterface {
// staticMethod(): void;
// }
// @staticImplements<StaticInterface>
// class MyClass {
// static staticMethod() { }
// }
export function methodStaticImplements<A>(method: A): TypedPropertyDescriptor<A>;
export function methodStaticImplements<A, B>(method: A & B): TypedPropertyDescriptor<A & B>;
export function methodStaticImplements<A, B, C>(method: A & B & C): TypedPropertyDescriptor<A & B & C>;
export function methodStaticImplements(method: any): TypedPropertyDescriptor<any> {
return {
value: method,
writable: true,
enumerable: false,
configurable: true,
};
}
export interface Default {
default<T extends abstract new (...args: any[]) => any>(): InstanceType<T>;
default(): unknown;
}
export class Default {
static default<T extends AnyClass>(ctor: T): InstanceType<T> {
if ("default" in ctor && typeof ctor.default === "function") {
return ctor.default();
}
try {
return new ctor();
} catch (e) {
throw new Error(
`Type ${ctor.name} does not implement static \`default\` method, and cannot be instantiated withpout arguments.`
);
}
}
}
export interface ToString {
toString(): string;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment