1+ import { Maybe } from "./maybe" ;
2+ import { Option } from "./option"
3+
4+ export type EnumInstance < T > = T extends TranslateTypeToStatic < infer U > ? ( TranslateTypeToInstance < U > & InstanceType < T > ) : never ;
5+
6+ export const Default = Symbol ( "Default" ) ;
7+
8+ interface BaseInstance < T extends { [ key : string ] : { new ( ...args : any ) : any } } > {
9+ when < K extends keyof T , RT > ( object : ( ...args : ConstructorParameters < T [ K ] > ) => TranslateTypeToWellDefined < T , K > , handler : ( instance : InstanceType < T [ K ] > ) => RT ) : RT extends Promise < infer U > ? Maybe < U > : Option < RT > ;
10+ asyncWhen < K extends keyof T , RT > ( object : ( ...args : ConstructorParameters < T [ K ] > ) => TranslateTypeToWellDefined < T , K > , handler : ( instance : InstanceType < T [ K ] > ) => Promise < RT > ) : Maybe < RT > ;
11+ match < K extends keyof T , H extends ( {
12+ [ key in keyof T ] ?: ( instance : InstanceType < T [ key ] > ) => any
13+ } & Record < typeof Default , ( ) => any > | {
14+ [ key in keyof T ] : ( instance : InstanceType < T [ key ] > ) => any
15+ } ) > ( handlers : H ) : ReturnType < Exclude < H [ keyof H ] , undefined > > ;
16+ }
17+
18+ type TranslateTypeToStatic < T extends { [ key : string ] : { new ( ...args : any ) : any } } > = {
19+ [ K in keyof T ] : < V extends { new ( ...args : any ) : any } > ( this : V , ...args : ConstructorParameters < T [ K ] > ) => TranslateTypeToWellDefined < T , K > & InstanceType < V > ;
20+ } & X < T > ;
21+
22+ interface X < T extends { [ key : string ] : { new ( ...args : any ) : any } } > {
23+ new ( current : keyof T , value : InstanceType < T [ keyof T ] > ) : TranslateTypeToInstance < T > ;
24+ }
25+
26+ type TranslateTypeToInstance < T extends { [ key : string ] : { new ( ...args : any ) : any } } > = BaseInstance < T > & {
27+ [ K in keyof T as `is${Capitalize < string & K > } `] : ( a : TranslateTypeToInstance < T > ) => a is TranslateTypeToWellDefined < T , K > ;
28+ }
29+
30+ type TranslateTypeToWellDefined < T extends { [ key : string ] : { new ( ...args : any ) : any } } , K extends keyof T > = TranslateTypeToInstance < T > & {
31+ [ key in K ] : InstanceType < T [ key ] > ;
32+ }
33+
34+ export function Enum < T extends { [ key : string ] : { new ( ...args : any ) : any } } > ( classObject : T ) {
35+ const base = class < K extends keyof T > {
36+ [ Symbol . toStringTag ] ( ) { return name }
37+
38+ constructor (
39+ public current : K ,
40+ public value : InstanceType < T [ K ] > ,
41+ ) { }
42+
43+ match < RT > ( handlers : { [ key in keyof T ] ?: ( instance : InstanceType < T [ key ] > ) => RT } & ( Record < typeof Default , ( ) => RT > | { } ) ) : RT {
44+ const handler = handlers [ this . current ] ;
45+
46+ if ( handler ) {
47+ return handler ( this . value ) ;
48+ }
49+
50+ if ( handlers [ Default as any ] !== undefined ) {
51+ return ( handlers [ Default as any ] as any ) ( ) ;
52+ }
53+
54+ throw new Error ( `PANIC! No handler for ${ String ( this . current ) } !` ) ;
55+ }
56+ } ;
57+
58+ const baseAny = base as any ;
59+
60+ const keys = Object . keys ( classObject ) as ( keyof T ) [ ] ;
61+
62+ const map : any = { } ;
63+
64+ for ( const key of keys ) {
65+ classObject [ key ] . prototype [ Symbol . toStringTag ] = ( ) => key ;
66+
67+ const stringKey = String ( key ) ;
68+ const capitalized = stringKey . charAt ( 0 ) . toUpperCase ( ) + stringKey . slice ( 1 ) ;
69+
70+ baseAny . prototype [ `is${ capitalized } ` ] = function ( a : TranslateTypeToInstance < T > ) : a is TranslateTypeToWellDefined < T , keyof T > {
71+ return ( a as any ) . current === key ;
72+ }
73+
74+ Object . defineProperty ( baseAny . prototype , stringKey , {
75+ get ( ) {
76+ if ( this . current !== key ) {
77+ throw new Error ( `Tried to access ${ String ( key ) } when current is ${ this . current } ` ) ;
78+ }
79+
80+ return this . value ;
81+ }
82+ } ) ;
83+
84+ map [ key ] = baseAny [ key ] = function ( ...args : any [ ] ) {
85+ return new this ( key , new classObject [ key ] ( ...args ) ) ;
86+ }
87+ }
88+
89+ baseAny . prototype . when = function when < K1 extends T [ keyof T ] , RT > ( object : K1 , handler : ( instance : InstanceType < K1 > ) => RT ) : Option < RT > {
90+ if ( map [ this . current ] as any === object as any ) {
91+ return Option . some ( handler ( this . value as unknown as InstanceType < K1 > ) ) ;
92+ }
93+
94+ return Option . none ( ) ;
95+ }
96+
97+ baseAny . prototype . asyncWhen = function asyncWhen < K1 extends T [ keyof T ] , RT > ( object : K1 , handler : ( instance : InstanceType < K1 > ) => Promise < RT > ) : Maybe < RT > {
98+ if ( map [ this . current ] as any === object as any ) {
99+ return Maybe . new < RT > ( m => {
100+ handler ( this . value as unknown as InstanceType < K1 > ) . then ( m . fullfill ) . catch ( m . unfullfill ) ;
101+ } ) ;
102+ }
103+
104+ return Maybe . Unfullfilled ( ) ;
105+ }
106+
107+ return baseAny as TranslateTypeToStatic < T > ;
108+ }
0 commit comments