Created
August 25, 2020 13:51
-
-
Save webstrand/f8a67999c8cbfcf318beb5a024143ff0 to your computer and use it in GitHub Desktop.
incomplete work on some defined-ness assertions
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
| type Obj = { | |
| id?: string; | |
| name?: string; | |
| child: undefined | { | |
| id?: string; | |
| } | |
| parent: Obj | undefined; | |
| } | |
| // --------- assertAllDefined | |
| // not what you asked for, but I wrote it so you get it. | |
| type RecursiveDefined<T extends { [key: string]: any }> = { | |
| [P in keyof T]-?: _RecursiveDefined<T[P]> | |
| } | |
| type _RecursiveDefined<T> = T extends { [key: string]: any } ? RecursiveDefined<T> : T extends undefined ? never : T; | |
| declare function assertAllDefined<T extends { [key: string]: any }>(obj: T): asserts obj is RecursiveDefined<T>; | |
| declare const obj1: Obj; | |
| assertAllDefined(obj1); | |
| obj1.child.id // string | |
| obj1.parent.child.id // string | |
| obj1.parent.parent.child.id // string | |
| // etc... | |
| // -------- assertPathDefined | |
| // not what you asked for, but I wrote it so you get it. | |
| type Pick1<T, K1 extends keyof T> = { [_ in K1]: T[K1] } | |
| type Pick2<T, K1 extends keyof T, K2 extends keyof T[K1]> = { [_ in K1]: { [_ in K2]: T[K1][K2] } } | |
| type Pick3<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]> = { [_ in K1]: { [_ in K2]: { [_ in K3]: T[K1][K2][K3] } } } | |
| type Pick4<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]> = { [_ in K1]: { [_ in K2]: { [_ in K3]: { [_ in K4]: T[K1][K2][K3][K4] } } } } | |
| declare function assertPathDefined<T extends { [key: string]: any }, K1 extends keyof T>(obj: T, k1: K1): asserts obj is T & Pick1<RecursiveDefined<T>, K1>; | |
| declare function assertPathDefined<T extends { [_ in K1]?: { [key: string]: any } }, K1 extends string, K2 extends keyof RecursiveDefined<T>[K1]>(obj: T, k1: K1, k2: K2): asserts obj is T & Pick2<RecursiveDefined<T>, K1, K2>; | |
| declare function assertPathDefined<T extends { [_ in K1]?: { [_ in K2]?: { [key: string]: any } } }, K1 extends string, K2 extends keyof RecursiveDefined<T>[K1], K3 extends keyof RecursiveDefined<T>[K1][K2]>(obj: T, k1: K1, k2: K2, k3: K3): asserts obj is T & Pick3<RecursiveDefined<T>, K1, K2, K3>; | |
| declare function assertPathDefined<T extends { [_ in K1]?: { [_ in K2]?: { [_ in K3]?: { [key: string]: any} } } }, K1 extends string, K2 extends keyof RecursiveDefined<T>[K1], K3 extends keyof RecursiveDefined<T>[K1][K2], K4 extends keyof RecursiveDefined<T>[K1][K2][K3]>(obj: T, k1: K1, k2: K2, k3: K3, k4: K4): asserts obj is T & Pick4<RecursiveDefined<T>, K1, K2, K3, K4>; | |
| declare const obj2: Obj; | |
| assertPathDefined(obj2, "id"); | |
| obj2.id // string; | |
| obj2.name // string | undefined | |
| // @ts-expect-error | |
| obj2.child.id // should fail | |
| assertPathDefined(obj2, "child", "id"); | |
| obj2.child.id // string; | |
| // ------- assertNotNull | |
| type Paths<T extends { [key: string]: any }> = keyof T | (keyof T extends infer U ? U extends keyof T ? _Paths<T[U], U> : never : never); | |
| type _Paths<T, U extends PropertyKey> = T extends { [key: string]: any } ? { [_ in U]: Paths<T> } : never; | |
| declare function testfn<T extends Paths<U>, U extends { [key: string]: any }>(u: U, t: T): typeof t; | |
| const xxx = testfn({} as Obj, { child: "id" }); | |
| type UnsafePick<T, U> = | |
| T extends { [key: string]: any } ? | |
| U extends { [key: string]: any } ? | |
| { [P in keyof U]: P extends keyof T ? UnsafePick<T[P], U[P]> : never } | |
| : U extends keyof T ? { [_ in U]: Exclude<T[U], undefined> } : never | |
| : never; | |
| declare function assertNotNull<T extends { [key: string]: any }, U extends Paths<T>>(obj: T, path: U): U; | |
| declare const obj3: Obj; | |
| const x = assertNotNull(obj3, { child: "id" }); | |
| obj3.child.id; | |
| declare const obj: Obj; | |
| assertNotNull(obj, "id", "name", { child: "id" }) // would love "child.id" but I know that's not possible | |
| obj.id // not undefined | |
| obj.name // not undefined | |
| obj.child // not undefined | |
| obj.child.id // not undefined |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment