Last active
February 12, 2023 19:57
-
-
Save dedurus/4418484e6272f7f0dd9ce9256bee569d to your computer and use it in GitHub Desktop.
Higher Order Type Level Functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Generic helpers | |
*/ | |
interface Fn { | |
input: unknown; | |
output: unknown; | |
} | |
type Call<fn extends Fn, input> = (fn & { input: input })["output"]; | |
type Stringifiable = string | number | boolean | bigint | null | undefined; | |
/** | |
* Generic Array operations | |
*/ | |
/** | |
* Reduce | |
*/ | |
type Reduce<xs, acc, fn extends Fn> = xs extends [infer first, ...infer rest] | |
? Reduce<rest, Call<fn, { acc: acc; item: first }>, fn> | |
: acc; | |
/** | |
* Define Map in terms of Reduce | |
*/ | |
interface MapFn<fn extends Fn> extends Fn { | |
output: this["input"] extends { | |
acc: infer acc extends any[]; | |
item: infer item; | |
} | |
? [...acc, Call<fn, item>] | |
: never; | |
} | |
type ListMap<xs, fn extends Fn> = Reduce<xs, [], MapFn<fn>>; | |
/** | |
* Define Filter in terms of Reduce | |
*/ | |
interface FilterFn<fn extends Fn> extends Fn { | |
output: this["input"] extends { | |
acc: infer acc extends any[]; | |
item: infer item; | |
} | |
? Call<fn, item> extends true | |
? [...acc, item] | |
: acc | |
: never; | |
} | |
type Filter<xs, fn extends Fn> = Reduce<xs, [], FilterFn<fn>>; | |
/** | |
* Examples | |
*/ | |
interface ToPhrase extends Fn { | |
output: `number is ${Extract<this["input"], string | number | boolean>}`; | |
} | |
type ys = ListMap<[1, 2, 3], ToPhrase>; | |
// ^? type ys = ["number is 1", "number is 2", "number is 3"] | |
type MakeRange<n, acc extends any[] = []> = acc["length"] extends n | |
? acc | |
: MakeRange<n, [...acc, acc["length"]]>; | |
type AddNumbers<a, b> = [...MakeRange<a>, ...MakeRange<b>]["length"]; | |
type StringToNumber<str> = str extends `${infer n extends number}` ? n : never; | |
interface Add extends Fn { | |
output: this["input"] extends { | |
acc: infer acc; | |
item: infer item; | |
} | |
? AddNumbers<acc, item> | |
: never; | |
} | |
interface Join<sep extends string> extends Fn { | |
output: this["input"] extends { | |
acc: infer acc extends Stringifiable; | |
item: infer item extends Stringifiable; | |
} | |
? `${acc}${sep}${item}` | |
: never; | |
} | |
type sum = Reduce<[1, 2, 3, 4], 0, Join<"-">>; | |
// ^? type sum = "0-1-2-3-4" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment