Skip to content

Instantly share code, notes, and snippets.

@willmtemple
Created October 6, 2022 16:19
Show Gist options
  • Save willmtemple/a8c2b808fce434cad90cfe03579130b7 to your computer and use it in GitHub Desktop.
Save willmtemple/a8c2b808fce434cad90cfe03579130b7 to your computer and use it in GitHub Desktop.
Type-space OData stuff
interface Foo {
a: string;
b: number;
bar: Bar;
}
interface Bar {
a: number;
b: string;
baz: Baz;
}
interface Baz {
test: symbol;
test2: "a"
}
// type DecMap = [0, 0, 1, 2, 3, 4, 5, 6, 7, 8];
// type DepthLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
// type Dec<T extends DepthLevel> = DecMap[T]
/**
* This little operator converts a union `T1 | T2 | ... | TN` to an intersection `T1 & T2 & ... & TN`.
*
* It works by first distributing the members of the union into a union of functions, where the members of the union are mapped into parameters of the functions in the union.
*
* Then, by trying to `infer` the type of the parameter, contravariance requires the inferred type to be an _intersection_ of all parameters, thereby converting the original union into an intersection.
*
* @hidden
* @internal
*/
type UnionToIntersection<U> = (U extends unknown ? (_: U) => unknown : never) extends (_: infer I) => unknown ? I : never;
type ODataPaths<T extends object, /* Depth extends DepthLevel = 9> = Depth extends 0
? never
: */ > = {
[K in Exclude<keyof T, symbol | number>]: T[K] extends object
? ODataPaths<T[K]/*, Dec<Depth>*/> extends infer NextPaths extends string
? K | `${K}/${NextPaths}`
: K
: K
}[Exclude<keyof T, symbol | number>];
declare const _1: ODataPaths<Foo>;
interface Client<T extends object> {
search<Paths extends ODataPaths<T>>(...fields: Paths[]): ODataPick<T, Paths>;
}
declare const c: Client<Foo>;
type ODataPick<T extends object, Paths extends ODataPaths<T>> = UnionToIntersection<
Paths extends `${infer Fragment extends Exclude<keyof T, symbol | number>}/${infer NextPaths}`
? ( T[Fragment] extends object ? (NextPaths extends ODataPaths<T[Fragment]> ? ({ [K in Fragment]: ODataPick<T[Fragment], NextPaths>}) : never) : never) : Paths extends keyof T ? { [K in Paths]: T[K] }
: never
> & {};
// ^ do not remove, thx
// Inferred type: ODataPick<Foo, "a" | "b" | "bar/baz/test2">
const _2 = c.search("bar/baz/test2", "b", "a");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment