Search code examples
typescripttypescript-typings

Strong types for a function getting a value from object


I have a function for getting value by key from an object that allows me to see suggestions of possible keys in record when using it. The function infers types from it's arguments.

function get<T extends Record<string, any>, K1 extends keyof T>(
    record: T | undefined,
    key1: K1
): T[K1] | undefined

const foo = { bar: 1, baz: 2 }
get(foo, 'bar') // 1, get<{ a: number, b: number }>

Is it possible to convert it to point-free style, keep the strong types and don't lose suggestions? Something similiar to this.

function get<T extends Record<string, any>, K1 extends keyof T>(key1: K1):
    (record: T | undefined) => T[K1] | undefined

const foo = { bar: 1, baz: 2 }
get('bar')(foo) // 1, get<{ a: number, b: number }>

Obviously this doesn't work. I tried multiple variants of the pointfree function, but I couldn't get it to work. I looked at ramda implementation of function prop, which works pointfree, but it doesn't provide suggestions as it allows any string as key.

type prop = <P extends string>(key: P) => <T>(obj: Record<P, T>) => T

const foo = { bar: 1, baz: 2 }
prop('bar')(foo) // 1, prop: <"bar">(key: "bar") => <T>(obj: Record<"bar", T>) => T

EDIT:

Just to be clear I know I wouldn't be able get suggestions without specifying the record first.

prop('...') // no suggestions here
prop('...')(foo) // now I want suggestions

Solution

  • I highly suspect that this is impossible as the order of the dependencies is reversed. If the object were to be supplied first that would not be an issue.

    Right now you would have to specify the type of the object manually via the generics, e.g.:

    const get = <T extends Record<K1, any>, K1 extends keyof T>(key1: K1) =>
        (record: T) => record[key1];
    
    const foo = { bar: 1, baz: 2 }
    get<typeof foo, keyof typeof foo>('bar')(foo)