ts-union
Version:
ADT (sum type) in typescript inspired by ML language family
142 lines (141 loc) • 7.16 kB
TypeScript
export interface Const<T> {
readonly _const: T;
}
export interface Generic {
opaque: 'Generic is a token type that is going to be replaced with a real type';
}
export interface Unit {
opaque: 'opaque token for empty payload type';
}
export declare type Case<T> = Of<T> | Const<T>;
export interface RecordDict {
readonly [key: string]: Case<unknown>;
}
export declare type SingleDataCase = Of<[Unit]> | Const<unknown> | Of<[unknown]>;
export interface SingleDataRecordDict {
readonly [key: string]: SingleDataCase;
}
export interface ForbidDefault {
default?: never;
}
export declare type ForbidReservedProps = {
readonly if?: never;
readonly match?: never;
readonly matchWith?: never;
readonly T?: never;
} & ForbidDefault;
export declare type RequiredRecordType = RecordDict & ForbidReservedProps;
export declare type SingleDataRecordType = SingleDataRecordDict & ForbidReservedProps;
export interface Of<T> {
_opaque: T;
}
export interface Types {
(unit: null): Of<[Unit]>;
<T = void>(): Of<[T]>;
(g: Generic): Of<[Generic]>;
<T>(val: T): Const<T>;
<T1, T2>(): Of<[T1, T2]>;
<T1, T2, T3>(): Of<[T1, T2, T3]>;
}
export declare const of: Types;
export interface UnionVal<Record> {
readonly _opaqueToken: Record;
}
export interface UnionValG<P, Record> {
readonly _opaqueToken: Record;
readonly _type: P;
}
export declare type GenericValType<Type, Val> = Val extends UnionValG<infer _Type, infer Rec> ? UnionValG<Type, Rec> : never;
export declare type Constructors<Record> = {
[T in keyof Record]: CreatorFunc<Record[T], UnionVal<Record>>;
};
export declare type ConstructorsG<Record> = {
[K in keyof Record]: CreatorFuncG<Record[K], Record>;
};
export declare type Cases<Record, Result> = {
[T in keyof Record]: MatchCaseFunc<Record[T], Result>;
};
export declare type CasesG<Record, Result, P> = {
[K in keyof Record]: MatchCaseFuncG<Record[K], Result, P>;
};
export declare type CreatorFunc<K, UVal> = K extends Of<infer A> ? A extends [void] ? () => UVal : A extends [Unit] ? UVal : A extends any[] ? (...p: A) => UVal : never : K extends Const<unknown> ? () => UVal : never;
export declare type CreatorFuncG<K, Rec> = K extends Of<infer A> ? A extends [void] ? <P = never>() => UnionValG<P, Rec> : A extends [Unit] ? <P = never>() => UnionValG<P, Rec> : A extends [Generic] ? <P>(val: P) => UnionValG<P, Rec> : A extends any[] ? <P = never>(...p: A) => UnionValG<P, Rec> : never : K extends Const<unknown> ? <P = never>() => UnionValG<P, Rec> : never;
export declare type MatchCaseFunc<K, Res> = K extends Of<infer A> ? A extends [void] ? () => Res : A extends [Unit] ? () => Res : A extends any[] ? (...p: A) => Res : never : K extends Const<infer C> ? (c: C) => Res : never;
export declare type MatchCaseFuncG<K, Res, P> = K extends Of<infer A> ? A extends [void] ? () => Res : A extends [Unit] ? () => Res : A extends [Generic] ? (val: P) => Res : A extends any[] ? (...p: A) => Res : never : K extends Const<infer C> ? (c: C) => Res : never;
export declare type MatchCases<Record, Result> = (Cases<Record, Result> & ForbidDefault) | (Partial<Cases<Record, Result>> & {
default: (val: UnionVal<Record>) => Result;
});
export declare type MatchCasesG<Rec, Result, P> = (CasesG<Rec, Result, P> & ForbidDefault) | (Partial<CasesG<Rec, Result, P>> & {
default: (val: UnionValG<P, Rec>) => Result;
});
export interface MatchFunc<Record> {
<Result>(cases: MatchCases<Record, Result>): (val: UnionVal<Record>) => Result;
<Result>(val: UnionVal<Record>, cases: MatchCases<Record, Result>): Result;
}
export interface MatchFuncG<Record> {
<Result, P>(cases: MatchCasesG<Record, Result, P>): (val: UnionValG<P, Record>) => Result;
<Result, P>(val: UnionValG<P, Record>, cases: MatchCasesG<Record, Result, P>): Result;
}
export declare type UnpackFunc<K, Rec> = K extends Of<infer A> ? A extends [void] ? {
<R>(val: UnionVal<Rec>, f: () => R): R | undefined;
<R>(val: UnionVal<Rec>, f: () => R, els: (v: UnionVal<Rec>) => R): R;
} : A extends [Unit] ? {
<R>(val: UnionVal<Rec>, f: () => R): R | undefined;
<R>(val: UnionVal<Rec>, f: () => R, els: (v: UnionVal<Rec>) => R): R;
} : A extends any[] ? {
<R>(val: UnionVal<Rec>, f: (...p: A) => R): R | undefined;
<R>(val: UnionVal<Rec>, f: (...p: A) => R, els: (v: UnionVal<Rec>) => R): R;
} : never : K extends Const<infer С> ? {
<R>(val: UnionVal<Rec>, f: (с: С) => R): R | undefined;
<R>(val: UnionVal<Rec>, f: (с: С) => R, els: (v: UnionVal<Rec>) => R): R;
} : never;
export declare type UnpackFuncG<K, Rec> = K extends Of<infer A> ? A extends [void] ? {
<R, P>(val: UnionValG<P, Rec>, f: () => R): R | undefined;
<R, P>(val: UnionValG<P, Rec>, f: () => R, els: (v: UnionValG<P, Rec>) => R): R;
} : A extends [Unit] ? {
<R, P>(val: UnionValG<P, Rec>, f: () => R): R | undefined;
<R, P>(val: UnionValG<P, Rec>, f: () => R, els: (v: UnionValG<P, Rec>) => R): R;
} : A extends [Generic] ? {
<R, P>(val: UnionValG<P, Rec>, f: (val: P) => R): R | undefined;
<R, P>(val: UnionValG<P, Rec>, f: (val: P) => R, els: (v: UnionValG<P, Rec>) => R): R;
} : A extends any[] ? {
<R, P>(val: UnionValG<P, Rec>, f: (...p: A) => R): R | undefined;
<R, P>(val: UnionValG<P, Rec>, f: (...p: A) => R, els: (v: UnionValG<P, Rec>) => R): R;
} : never : K extends Const<infer С> ? {
<R, P>(val: UnionValG<P, Rec>, f: (с: С) => R): R | undefined;
<R, P>(val: UnionValG<P, Rec>, f: (с: С) => R, els: (v: UnionValG<P, Rec>) => R): R;
} : never;
export declare type Unpack<Rec> = {
[K in keyof Rec]: UnpackFunc<Rec[K], Rec>;
};
export declare type UnpackG<Rec> = {
[K in keyof Rec]: UnpackFuncG<Rec[K], Rec>;
};
declare type UnionDesc<Rec> = {
if: Unpack<Rec>;
T: UnionVal<Rec>;
match: MatchFunc<Rec>;
matchWith: Rec extends SingleDataRecordType ? <Other extends SingleDataRecordType, Result>(other: UnionDesc<Other>, matchObj: MatchCasesForTwo<Rec, Other, Result>) => (a: UnionVal<Rec>, b: UnionVal<Other>) => Result : never;
} & Constructors<Rec>;
export declare type UnionObj<Rec> = UnionDesc<Rec>;
export declare type GenericUnionDesc<Rec> = {
match: MatchFuncG<Rec>;
if: UnpackG<Rec>;
T: UnionValG<unknown, Rec>;
} & ConstructorsG<Rec>;
declare type TypeOfLeg<Leg> = Leg extends Of<infer A> ? A extends [void] | [Unit] ? void : A extends [infer Value] ? Value : A extends Const<infer C> ? C : never : never;
export declare type MatchCaseFuncTwo<LegA, LegB, Res> = (a: TypeOfLeg<LegA>, b: TypeOfLeg<LegB>) => Res;
export declare type CasesTwo<RecordA, RecordB, Result> = {
[KA in keyof RecordA]?: {
[KB in keyof RecordB]?: MatchCaseFuncTwo<RecordA[KA], RecordB[KB], Result>;
};
};
export declare type MatchCasesForTwo<RecordA, RecordB, Result> = CasesTwo<RecordA, RecordB, Result> & {
default: (a: UnionVal<RecordA>, b: UnionVal<RecordB>) => Result;
};
export interface UnionFunc {
<R extends RequiredRecordType>(record: R): UnionDesc<R>;
<R extends RequiredRecordType>(ctor: (g: Generic) => R): GenericUnionDesc<R>;
}
export declare const Union: UnionFunc;
export {};