Created
July 12, 2023 08:36
-
-
Save dzil123/af290d5327ad1a616503676575678096 to your computer and use it in GitHub Desktop.
This file contains 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
type Head<T extends unknown[]> = ((...args: T) => never) extends ( | |
_: infer R, | |
...args: any | |
) => never | |
? R | |
: never; | |
type Tail<T extends unknown[]> = ((...args: T) => never) extends ( | |
_: any, | |
...args: infer R | |
) => never | |
? R | |
: never; | |
interface Scene { | |
foo: {}; | |
bar: {}; | |
baz: {}; | |
} | |
function useScene(_: Scene) {} | |
{ | |
// Broken solution: | |
type Append<T, X extends T, L extends T[]> = L["length"] extends 0 | |
? [X] | |
: [Head<L> | X, ...Append<T, X, Tail<L>>]; | |
type PowerSet<T, L extends T[]> = L["length"] extends 0 | |
? [] | |
: PowerSet<T, Tail<L>> extends infer X | |
? X extends T[] | |
? [...X, ...Append<T, Head<L>, X>] | |
: never | |
: never; | |
type ForEachPick<T, L extends (keyof T)[]> = L["length"] extends 0 | |
? never | |
: Pick<T, Head<L>> | ForEachPick<T, Tail<L>>; | |
type ScenePowerSet = PowerSet<keyof Scene, ["foo", "bar", "baz"]>; | |
// ^? | |
type SceneBuilder = ForEachPick<Scene, ScenePowerSet>; | |
// ^? | |
let scene: SceneBuilder = { foo: {} }; | |
scene = { ...scene, bar: {} }; | |
scene = { ...scene, baz: {} }; | |
// useScene(scene); | |
} | |
{ | |
// Working solution: | |
type Reverse<L extends unknown[]> = L["length"] extends 0 | |
? [] | |
: [...Reverse<Tail<L>>, Head<L>]; | |
type Builder<T, L extends (keyof T)[]> = Reverse<L> extends (keyof T)[] | |
? BuilderImpl<T, Reverse<L>> | |
: never; | |
type BuilderImpl<T, L extends (keyof T)[]> = L["length"] extends 0 | |
? never | |
: BuilderImpl<T, Tail<L>> | Pick<T, L[number]>; | |
type SceneBuilder = Builder<Scene, ["foo", "bar", "baz"]>; // order must match use | |
// ^? | |
let scene: SceneBuilder = { foo: {} }; | |
scene = { ...scene, bar: {} }; | |
scene = { ...scene, baz: {} }; | |
useScene(scene); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment