当无法定义T时,如何键入“ K扩展T的key”?

时间:2019-10-09 19:27:08

标签: typescript

我想向此JS函数添加TypeScript类型:

function getProp(obj, prop) {
  if (!obj) {
    return undefined;
  }

  return obj[prop];
}

如果不需要处理undefined,则可以:

function getProp<T, P extends keyof T>(obj: T, prop: P): T[P] {
  // ...
}

但是,对于undefined,这种对| undefined的幼稚方法不起作用:

function getProp<T, P extends keyof T>(obj: T | undefined, prop: P): T[P] | undefined {
  if (!obj) {
    return undefined;
  }

  return obj[prop];
}

const obj1 = {
  id: 1,
  name: 'obj1',
};
const obj2 = undefined;

getProp(obj1, 'id');
getProp(obj2, 'id');
              ^^^ Argument of type '"id"' is not assignable to parameter of type 'never'.ts(2345)

Playground

最好的输入方式是什么?

2 个答案:

答案 0 :(得分:2)

如果要全力以赴,使getProp()函数接受各种obj参数,然后允许obj为{{1 }},我会做这样的事情:

undefined

这非常复杂,但它可能具有一些令人希望的行为:

function getProp<T,
    P extends T extends any ? (undefined extends T ? keyof any : keyof T) : never
>(
    obj: T,
    prop: P
): T extends any ? (P extends keyof T ? T[P] : undefined) : never;
function getProp<T, P extends keyof T>(obj: T | undefined, prop: P): T[P] | undefined {
    if (typeof obj === "undefined") {
        return undefined;
    }

    return obj[prop];
}

但是对于您所描述的用例,您只想将const obj1 = { id: 1, name: 'obj1', }; const obj2 = undefined; const items = [obj1]; getProp(items.find(i => i.id === 1), "id"); // number | undefined getProp(obj1, "id"); // number getProp(obj2, "who knows"); // undefined getProp(Math.random() < 0.5 ? { a: 1 } : { a: "b" }, "a"); // string | number getProp(Math.random() < 0.5 ? { a: 1 } : { b: 1 }, "z"); // error! "z" not "a" | "b" getProp(Math.random() < 0.5 ? { a: 1 } : { b: 1 }, "a"); // number | undefined 传递给某个特定undefined的{​​{1}},而且您从不打算实际致电obj。在这种情况下,您的“幼稚”方法效果很好:

T | undefined

观察:

T

唯一可能的“坏处”是,getProp(undefined, "something")的所有输出在其类型中都包含function getProp<T, P extends keyof T>(obj: T | undefined, prop: P): T[P] | undefined { if (typeof obj === "undefined") { return undefined; } return obj[prop]; } ,即使您在调用getProp(items.find(i => i.id === 1), "id"); // number | undefined getProp(obj1, "id"); // number | undefined getProp(obj2, "who knows"); // error! getProp(Math.random() < 0.5 ? { a: 1 } : { a: "b" }, "a"); // string | number | undefined getProp(Math.random() < 0.5 ? { a: 1 } : { b: 1 }, "z"); // error! "z" not "a" | "b" getProp(Math.random() < 0.5 ? { a: 1 } : { b: 1 }, "a"); // number | undefined (已知定义的{{ 1}}和已知的getProp()。但这似乎就是您想要的,所以就去了。您基本上回答了自己的问题!

所以,希望能有所帮助。祝你好运!

Link to code

答案 1 :(得分:2)

我认为这是您想要实现的目标:

function getProp<O, P extends O extends undefined ? any : keyof O>(obj: O, prop: P)  {
  if (!obj) {
    return undefined;
  }

  return obj[prop];
}

const obj1 = {
  id: 1,
  name: 'obj1',
};
const obj2 = undefined;

getProp(obj1, 'id');
getProp(obj2, 'id');

因此,如果未定义第一个参数,则将第二个类型设置为any。我不键入return,因为可以很好地推断出它。 Playground