Created
October 6, 2022 16:19
-
-
Save willmtemple/a8c2b808fce434cad90cfe03579130b7 to your computer and use it in GitHub Desktop.
Type-space OData stuff
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
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