How to have TS infer a callback in a generic builder function?
Try declare Record<K, number>
as second generic type.
declare function makeObj<K extends string, T extends Record<K, number> = Record<K, number>>( builder: (ref: (k: K) => number) => T): Tconst obj1 = makeObj(() => ({ x: 1, y: 2 }));const obj2 = makeObj(ref => ({ x: 1, y: ref("invalid key, only x or y") }));const obj3 = makeObj(ref => ({ x: 1, y: ref("x") }));
Limitation
Well, as @kaya3 commented below.This resolution can only infer the return type.It still cannot find the invalid key unless explicitly set generic type.
// error will shown when given explicit generic typeconst obj2 = makeObj<'x' | 'y'>(ref => ({x: 1, y: ref("invalid key, only x or y")}));
type Ref<Obj> = { ref<Key extends string & keyof Obj> (k:`${Key}`):Obj[Key]};declare function makeObject<Type>(obj: Type): Type & Ref<Type>;const person = makeObject({ firstName: "Saoirse", lastName: "Ronan", age: 26});person.ref('firstName') // (method) ref<"firstName">(k: "firstName"): string// Argument of type '"firstNme"' is not assignable to // parameter of type '"firstName" | "lastName" | "age"'.ts(2345)person.ref('firstNme') // ERRORperson.ref('age') // (method) ref<"age">(k: "age"): number
Reference "String Unions in Types"
Unfortunately you can't access ref
when you are declaring the object in the call to makeObject
. Fortunately, the native Javascript approximation is pretty good anyway.