最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - TypeScript generic type for "pick" function (result object values types) - Stack Overflow

programmeradmin1浏览0评论

Have problem writing type for pick function. Everything works fine while picking only one key or several keys with values of same type. But if I'm trying to pick few keys and their values are different types - I will get an error. Not really sure where I made a mistake.

Thank you for your time.

export interface Mapper<T = any, R = any> {
  (arg: T): R;
}


export function pick<O, T extends keyof O>(keys: T[], obj?: O): { [K in T]: O[T] };

export function pick<T>(keys: T[], obj?: never): Mapper;

export function pick<O, T extends keyof O>(keys: T[], obj?: O) {
  const picker: Mapper<O, { [K in T]: O[T] }> = _obj =>
    keys.reduce((acc, key) => {
      if (key in _obj) {
        acc[key] = _obj[key];
      }
      return acc;
    }, {} as O);

  return obj ? picker(obj) : picker;
}

const obj = { someKey: 'value', otherKey: 42, moreKey: ['array value'] };

const newObj = pick(['otherKey'], obj);
//OK. TS type for newObj is {otherKey: number}

const n: number = newObj.otherKey;
// OK

const otherNewObj = pick(['otherKey', 'someKey'], obj);
//no really OK. TS type for otherNewObj is {otherKey: number | string, someKey: number | string}

const m: number = otherNewObj.someKey;
// Error. Type string | number is not assignable to the number

Have problem writing type for pick function. Everything works fine while picking only one key or several keys with values of same type. But if I'm trying to pick few keys and their values are different types - I will get an error. Not really sure where I made a mistake.

Thank you for your time.

export interface Mapper<T = any, R = any> {
  (arg: T): R;
}


export function pick<O, T extends keyof O>(keys: T[], obj?: O): { [K in T]: O[T] };

export function pick<T>(keys: T[], obj?: never): Mapper;

export function pick<O, T extends keyof O>(keys: T[], obj?: O) {
  const picker: Mapper<O, { [K in T]: O[T] }> = _obj =>
    keys.reduce((acc, key) => {
      if (key in _obj) {
        acc[key] = _obj[key];
      }
      return acc;
    }, {} as O);

  return obj ? picker(obj) : picker;
}

const obj = { someKey: 'value', otherKey: 42, moreKey: ['array value'] };

const newObj = pick(['otherKey'], obj);
//OK. TS type for newObj is {otherKey: number}

const n: number = newObj.otherKey;
// OK

const otherNewObj = pick(['otherKey', 'someKey'], obj);
//no really OK. TS type for otherNewObj is {otherKey: number | string, someKey: number | string}

const m: number = otherNewObj.someKey;
// Error. Type string | number is not assignable to the number
Share Improve this question asked Jan 27, 2020 at 15:58 DeltaBravoDeltaBravo 1351 gold badge2 silver badges5 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

You have an error in your mapped type you probably want to use O[K] instead of O[T] so you end up with { [K in T]: O[K] }. You want the type for each key K, not the type of all properties in the T union.

Also I would use Pick since Pick is homomorphic and would preserve modifiers such as readonly and optional.

Also obj?: never probably does not do what you want it to do, anything is assignable to never, you are better off omitting the parameter in that overload:

export function pick<O, T extends keyof O>(keys: T[], obj?: O): Pick<O, T>;
export function pick<T>(keys: T[]): Mapper;
export function pick<O, T extends keyof O>(keys: T[], obj?: O) {
    //....
}

Playground Link

发布评论

评论列表(0)

  1. 暂无评论