-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathnominal.ts
50 lines (44 loc) · 1.49 KB
/
nominal.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import {Func} from "./util";
const name: unique symbol = Symbol('name');
/**
* Grants nominal typing in a structural space by describing a
* (false) unique symbolic property.
*/
export type Nominal<T, Name> = T & {[name]: Name};
type AnonymousObject<T> = {
[P in keyof T]: Anonymous<T[P]>
}
type AnonymousTuple<T extends ReadonlyArray<any>> = {
[P in keyof T]: T[P] extends Nominal<infer TValue, any> ? Anonymous<TValue> : T[P]
};
type AnonymousFunction<T> = T extends (...args: infer TArgs) => infer TR ? (...anonArgs: AnonymousTuple<TArgs>) => Anonymous<TR> : T;
type primitive = number | string | symbol | boolean;
/**
* Anonymize a nominal type. Take an object that requires it's specific ids
* and return one that doesn't. Great for taking in inputs. Most action creators
* could go from Anonymous<T> => T
*/
export type Anonymous<T> =
T extends Nominal<infer TValue, any>
? (
TValue extends ReadonlyArray<any> ? AnonymousTuple<TValue> :
TValue extends Func ? AnonymousFunction<TValue> :
TValue extends primitive ? TValue :
TValue extends object ? AnonymousObject<TValue> :
T
)
:
T extends ReadonlyArray<any>
? AnonymousTuple<T>
:
T extends (...args: any[]) => any // needs to be before object, will technically count as one. in this else-if chain.
? AnonymousFunction<T>
:
T extends primitive
? T
:
T extends object
? AnonymousObject<T>
:
never
;