【问题标题】:Typescript: Type '{}' is not assignable to type 'Pick<T, K>', how do I type javascript `pick` function correctly?Typescript:类型“{}”不可分配给类型“Pick<T,K>”,如何正确键入 javascript `pick` 函数?
【发布时间】:2019-08-03 21:14:16
【问题描述】:

我想打一个叫做pick的函数,我知道typescript内置了Pick&lt;T, K&gt;,现在我想实现实际的源代码使用部分,但是我卡住了。

这个函数的作用是根据给定的字符串将pick提供object的属性并返回new object

用法是这样的:

const data = { name: 'Jason', age: 18, isMember: true };
const pickedData = pick(data, ['name', 'isMember']);

console.log(pickedData)
// => { name: 'Jason', isMember: true } 

我的pick函数的javascript实现without typescript是这样的:

function pick(obj, properties) {
  let result = {}

  properties.forEach(property => {
    result[property] = obj[property];
  });

  return result;
}

这里是typescript 版本,但它没有编译:

function pick<T, K extends keyof T>(obj: T, properties: K[]): Pick<T, K> {
  let result: Pick<T, K> = {}; // Typescript yells: Type '{}' is not assignable to type 'Pick<T, K>'.

  properties.forEach(property => {
    result[property] = obj[property];
  });

  return result;
}

我的 VSCode 截图:

但是,有一个解决办法,不知道是不是good的解决办法,就是加as Pick&lt;T, K&gt;

function pick<T, K extends keyof T>(obj: T, properties: K[]): Pick<T, K> {
  let result: Pick<T, K> = {} as Pick<T, K> ; // Typescript now satisfy with this.

  properties.forEach(property => {
    result[property] = obj[property];
  });

  return result;
}

但是,当使用这个pick 函数时,typescript 不会显示拾取的对象属性,而是显示不必要的信息:

它告诉我这个:

// typescript shows me, not what I want.
const pickedData: Pick<{
    name: string;
    age: number;
    isMember: boolean;
}, "name" | "isMember">

typescript 有什么方法可以告诉我这个吗?

// This is what I want typescript to show me.
const pickedData: {
  name: string;
  isMember: boolean;
}

【问题讨论】:

  • 提香的答案就是答案。正确的?提香的回答经常是。但是,如果您在这篇文章中看到了类似的内容(如果略有不同,可能不太复杂),您可能想知道像 Partial&lt;Pick&lt;T, K&gt;&gt; 这样简单的事情可能会有所帮助

标签: javascript typescript generics types


【解决方案1】:

第一个问题是{} 不应分配给Pick&lt;T, K&gt;,因为Pick&lt;T, K&gt; 可能具有必需的属性。在某些情况下,类型断言是使事情正常工作的唯一方法,这就是其中之一。 Typescript 不知道您打算稍后分配这些属性,您拥有该信息并以类型断言的形式将其提供给编译器,基本上是说,放松,我知道这不是真的 Pick&lt;T, K&gt; 但我很快就会做到这一点。

您问题的第二部分与其说是功能性的,不如说是Pick&lt;{ name: string; age: number; isMember: boolean;, "name" | "isMember"&gt; 在结构上等同于{ name: string; isMember: boolean; }。有时语言服务会扩展这些类型,有时则不会。这是最近的 issue 描述了这种确切的行为。

您可以使用更复杂的类型(与{} 相交的标识映射类型)强制扩展该类型:

type Id<T extends object> = {} & { [P in keyof T]: T[P] }
function pick<T, K extends keyof T>(obj: T, properties: K[]): Id<Pick<T, K>> {
  let result: Pick<T, K> = {} as Pick<T, K> ; // Typescript now satisfy with this.

  properties.forEach(property => {
    result[property] = obj[property];
  });

  return result;
}

const data = { name: 'Jason', age: 18, isMember: true };
const pickedData = pick(data, ['name', 'isMember']);

console.log(pickedData)

作为一个警告,不能保证Id 总是被扩展,我只能说在当前版本中Id 似乎总是强制扩展映射类型。

【讨论】:

    猜你喜欢
    • 2021-05-26
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    • 2021-12-26
    • 2018-09-19
    • 2020-05-13
    • 2021-11-16
    • 1970-01-01
    相关资源
    最近更新 更多