Home > Blockchain >  How to set default value to param that has type keyof (from an explicit type) in Typescript
How to set default value to param that has type keyof (from an explicit type) in Typescript

Time:10-23

I am having trouble in Typescript passing default values to a function similar to pick from lodash.

The function accepts an object of known (non-generic) interface and a set of keys to pick and return from the object.

Regular (no default params) declaration of the function works properly, however, I do not seem able to set an array as a default value for the parameter that selects the properties to pick.

interface Person {
    name: string;
    age: number;
    address: string;
    phone: string;
}

const defaultProps = ['name', 'age'] as const;


function pick<T extends keyof Person>(obj: Person, props: ReadonlyArray<T> = defaultProps): Pick<Person, T> {    
    return props.reduce((res, prop) => {
        res[prop] = obj[prop];
        return res;
    }, {} as Pick<Person,T>);
}

const testPerson: Person = {
    name: 'mitsos',
    age: 33,
    address: 'GRC',
    phone: '000'
};

If you remove the default value = defaultProps it compiles successfully and the returned type is also correct from an example call such as: const testPick = pick(testPerson, ['name']);

However, setting the default value produces the following error:

Type 'readonly ["name", "age"]' is not assignable to type 'readonly T[]'.
  Type '"name" | "age"' is not assignable to type 'T'.
    '"name" | "age"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'keyof Person'.
      Type '"name"' is not assignable to type 'T'.
        '"name"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'keyof Person'.

How can I successfully pass the default values to the props param?

Typescript Playground link here

CodePudding user response:

You can overload pick function:

interface Person {
    name: string;
    age: number;
    address: string;
    phone: string;
}

const defaultProps = ['name', 'age'] as const;

type DefaultProps = typeof defaultProps;

function pick(obj: Person): Pick<Person, DefaultProps[number]>
function pick<Prop extends keyof Person, Props extends ReadonlyArray<Prop>>(obj: Person, props: Props): Pick<Person, Props[number]>
function pick<T extends keyof Person>(obj: Person, props = defaultProps) {
    return props.reduce((res, prop) => ({
        ...res,
        [prop]: obj[prop]
    }), {} as Pick<Person, T>);
}

const testPerson = {
    name: 'mitsos',
    age: 33,
    address: 'GRC',
    phone: '000'
};

const result = pick(testPerson) //  Pick<Person, "name" | "age">
const result2 = pick(testPerson, ['phone']) // Pick<Person, "phone">
const result3 = pick(testPerson, ['abc']) // expected error

Playground

You can find more advanced pick typings in my article and other answers: First , second, third

CodePudding user response:

Something like that? Updated TS Playground.

const defaultProps: ReadonlyArray<keyof Person> = ['name', 'age'] as const;

function pick<T extends keyof Person>(obj: Person, props: ReadonlyArray<T> = (defaultProps as ReadonlyArray<T>)): Pick<Person, T> {   

UPDATED:

TS Playground.

  • Related