Search code examples
javascripttypescripttyping

Pick type by array of string


I have type

type User = {
    firstName: string,
    lastName: string,
    age: string
}

I need a function

const result =  fun<User>(['firstName', 'lastName'])

I need result type is a User with picked firstName and lastName. Is it possible to do with typeScript?


Solution

  • Yes, it is possible. I did not came up with this solution myself so all credits go to "drop-george" guy from github:

    type User = {
        a: number;
        b: number;
        c: number;
    }
    
    type ElementType < T extends ReadonlyArray < unknown > > = T extends ReadonlyArray<
      infer ElementType
    >
      ? ElementType
      : never
    
    function get<T, K extends (keyof T)[]>(v: T, keys: K): Pick<T, ElementType<K>> {
        return v
    }
    
    const user: User = {a: 1, b: 2, c: 3}
    const v = get(user, ['a', 'b'])
    const {a, b, c} = v // ERROR
    const {a: a2, b: b2} = v // OK
    const {a: a3} = v // OK
    get({a: 1, b: 2, c: 3} as User, ['a', 'b', 'otherKey']) // ERROR
    

    Original source

    UPD: You can just use ElementType<K> instead of ElementType<typeof keys>

    UPD2: AFAIK if you want to make something like fun<User>(['firstName', 'lastName']) the only 2 ways are:

    • specify properties you need twice:
    function get<T, K extends (keyof T)[]>(keys: K): Pick<T, ElementType<K>> {
        // return user
    }
    
    const user = get<User, ['firstName', 'lastName']>(['firstName', 'lastName'])
    
    • or make a function that returns another function kinda like this:
    function get<T>(): <K extends (keyof T)[]>(keys: K) => Pick<T, ElementType<K>> {
        return keys => {
            // return user from db
        }
    }
    
    const user = get<User>()(['firstName', 'lastName'])