-
Notifications
You must be signed in to change notification settings - Fork 2
/
type-safe.ts
138 lines (122 loc) · 2.96 KB
/
type-safe.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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// A library of functions that run at compile time and runtime.
export namespace Uint {
export function add<A extends number, B extends number>(
a: A,
b: B,
): number extends A
? number
: number extends B
? number
: [...Array.FromLength<A>, ...Array.FromLength<B>]["length"]
export function add<A extends number, B extends number>(a: A, b: B): number {
return a + b
}
export function subtract<A extends number, B extends number>(
a: A,
b: B,
): number extends A
? number
: number extends B
? number
: Array.FromLength<A> extends [...Array.FromLength<B>, ...infer Rest]
? Rest["length"]
: 0
export function subtract<A extends number, B extends number>(
a: A,
b: B,
): number {
return Math.max(0, a - b)
}
type Multiply<
A extends unknown[],
B extends unknown[],
T extends unknown[] = [],
> = B extends [unknown, ...infer Rest] ? Multiply<A, Rest, [...T, ...A]> : T
export function multiply<A extends number, B extends number>(
a: A,
b: B,
): number extends A
? number
: number extends B
? number
: A extends 0
? 0
: B extends 0
? 0
: Multiply<Array.FromLength<A>, Array.FromLength<B>> extends infer U
? U extends { length: infer L extends number }
? L
: never
: never
export function multiply<A extends number, B extends number>(
a: A,
b: B,
): number {
return a * b
}
}
export namespace Array {
export type FromLength<
N extends number,
A extends unknown[] = [],
> = A["length"] extends N ? A : FromLength<N, [...A, undefined]>
export function fromLength<N extends number>(
length: N,
): number extends N ? undefined[] : FromLength<N> {
return globalThis.Array.from({ length }) as any
}
}
export abstract class Function<
// @ts-ignore
in T,
> {
declare input: T
// @ts-ignore
protected abstract x(input: this["input"])
call<U extends T>(
value: U,
): ReturnType<
// @ts-ignore
(this & { readonly input: U })["x"]
> {
return this.x(value)
}
}
export abstract class Type<T> {
abstract is(value: unknown): value is T
when<U, A extends Function<T & U>, B extends Function<U>>(
value: U,
ifMatches: A,
otherwise: U extends B["input"]
? B["input"] extends U
? B
: never
: never,
) {
if (this.is(value)) {
return ifMatches.call(value)
} else {
return otherwise.call(value)
}
}
}
export namespace Type {
export const String = new (class StringType extends Type<string> {
is(value: unknown): value is string {
return typeof value == "string"
}
})()
}
const x = Type.String.when(
Math.random() < 0.5 ? ("abc" as const) : (57 as const),
new (class A extends Function<"abc"> {
protected x(input: this["input"]) {
return { x: input }
}
})(),
new (class B extends Function<"abc"> {
protected x(input: this["input"]) {
return { y: input }
}
})(),
)