Skip to content

Instantly share code, notes, and snippets.

@dzil123
Created July 12, 2023 08:36
Show Gist options
  • Save dzil123/af290d5327ad1a616503676575678096 to your computer and use it in GitHub Desktop.
Save dzil123/af290d5327ad1a616503676575678096 to your computer and use it in GitHub Desktop.
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