有没有一种方法可以键入一个函数,该函数可以在保持类型安全性的情况下(不进行类型转换)从Typescript中的对象中按名称删除属性?

时间:2018-12-17 20:49:57

标签: typescript

紧随this great answer to a related question之后,我们得到ObjectDiff(出于以下目的,我将其重命名为ObjectExclude

我有一个接受一个对象的函数,以及一个我想从该对象的克隆中删除的属性列表。我如何告诉Typescript这里发生了什么?该类型应为传入的对象,然后减去其后传入键的属性。

type ObjectExclude<T, U> = Pick<T, Exclude<keyof T, keyof U>>;

function removeProps<T extends object, U extends keyof T>(
  obj: T,
  ...propNames: U[]
): SomeType { // What type should this actually be?
  const objClone = { ...obj };
  propNames.forEach(p => delete objClone[p]);
  return objClone;
}

SomeType似乎是可以派生的东西,但我总是最终只是进行类型转换。

例如,如何在不进行手动类型转换的情况下告诉打字稿下面的b是类型{one: number, two: string}

const a = {one: 1, two: '2', three: true};
const b = removeProps(a, 'three'); // TS should just know the type here.

将代码看作一个了解JS或TS的人,很明显b的类型

interface B {one: number; two: string}

我希望TS也知道这一点,而无需告诉它。

2 个答案:

答案 0 :(得分:0)

Typescript可以根据您在其余参数中使用的输入字符串为您构造一个对象类型。以下removeProps的实现与上面的完全相同。仅类型定义已更改。

我们构造Z类型的唯一目的是将其排除在T类型之外。

function removeProps<T extends object, K extends keyof T, Z = {[P in K]: any}>(
  obj: T,
  ...propNames: K[]
): ObjectExclude<T, Z> {
  const objClone = { ...obj };
  propNames.forEach(p => delete objClone[p]);
  return objClone;
}

现在在您的代码中(从TS 3.2.2开始,但是我认为这一直可以回溯到2.8),您将获得以下信息:

const a = {one: 1, two: '2', three: true};
const b = removeProps(a, 'three');
// Type for b: Pick<{one: number, two: string, three: boolean}, "one" | "two">

这基本上意味着类型与对象a相同,但只包含属性"one""two"

  

注意:从技术上讲Z的类型为{[P in K]: T[K]},但是any可以在上面工作,因为它从未以任何方式使用:我们只关心属性,而不关心值,的Z


更新:

@ titian-cernicova-dragomir在下面指出,甚至不需要整个Z类型。所以说真的,您要做的就是

export function removeProps<T extends object, U extends keyof T>(
  obj: T,
  ...propNames: U[]
): Pick<T, Exclude<keyof T, U>> {
  const objClone = { ...obj };
  propNames.forEach(p => delete objClone[p]);
  return objClone;
}

这只是通过直接使用构成其的类型而放弃了ObjectExclude类型。

答案 1 :(得分:0)

我相信您可以使用Partial<T> 作为返回类型