Typescript:类型“{}”不可分配给类型“Pick<T, K>”,如何正确键入 javascript `pick` 函数?

Jos*_*ang 2 javascript generics types typescript

我想输入一个名为 的函数pick,我知道 typescript 内置了Pick<T, K>,现在我想实现实际的源代码使用部分,但我被卡住了。

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

用法是这样的:

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

console.log(pickedData)
// => { name: 'Jason', isMember: true } 
Run Code Online (Sandbox Code Playgroud)

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

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

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

  return result;
}
Run Code Online (Sandbox Code Playgroud)

这是typescript版本,但未编译:

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

console.log(pickedData)
// => { name: 'Jason', isMember: true } 
Run Code Online (Sandbox Code Playgroud)

我的 VSCode 截图:

在此输入图像描述

但是,有一个解决方案,我不知道这是否是一个good解决方案,那就是添加as Pick<T, K>

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

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

  return result;
}
Run Code Online (Sandbox Code Playgroud)

但是,使用此pick函数时,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;
}
Run Code Online (Sandbox Code Playgroud)

有什么方法typescript可以向我展示这一点吗?

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;
}

Run Code Online (Sandbox Code Playgroud)

Tit*_*mir 5

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

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

您可以使用更复杂的类型强制扩展类型(与 交集的恒等映射类型{}):

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)
Run Code Online (Sandbox Code Playgroud)

正如警告一样,不能保证总是Id会扩展,我只能说在当前版本中似乎Id总是强制扩展映射类型。