Skip to content

Instantly share code, notes, and snippets.

@webstrand
Created August 25, 2020 13:51
Show Gist options
  • Select an option

  • Save webstrand/f8a67999c8cbfcf318beb5a024143ff0 to your computer and use it in GitHub Desktop.

Select an option

Save webstrand/f8a67999c8cbfcf318beb5a024143ff0 to your computer and use it in GitHub Desktop.
incomplete work on some defined-ness assertions
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