我正在学习excellent docs的Typescript ,在练习Mapped Types的时候,我遇到了这个错误.

我知道为什么会发生这个错误(解释如下),但由于我是TS的新手,我不知道修复它的推荐方法.

代码如下(下面是the link to the code playground个代码):

// Represents schema of a Db table
type User = {
  id: number;   // might be auto incremented by DB
  name: string; // contains textual name data
};

// Manual work. Define a type that represents schema of "User" and add some metadata
type DBFields = {
  id: { format: "incrementing" };
  name: { type: string; pii: true };
};

type ExtractPII<Type> = {
  [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
};

type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;

// Manual work. Create object of ObjectsNeedingGDPRDeletion type
let deletionMarkers: ObjectsNeedingGDPRDeletion = {
  id: false,
  name: true
};

function handleGDPRDeletionRequest(user: User, deletionMarkers: ObjectsNeedingGDPRDeletion) {
  let cleanedUser: Partial<User> = {};

  for (const field of Object.keys(user)) {
    if (!deletionMarkers[field as keyof User]) {
      cleanedUser[field as keyof User] = user[field as keyof User];
    }
  }

  console.log(cleanedUser);  // Prints: { id: 1 }
}

let user = {
  id: 1,
  name: "John Doe",
};

handleGDPRDeletionRequest(user, deletionMarkers);  // { id: 1 }

**Please correct me if this explanation is not accurate**

错误发生在线路上

cleanedUser[field as keyof User] = user[field as keyof User];

因为cleanedUser被宣布为Partial<User>.Partial<User>意味着User的所有属性都是可选的,并且可能是undefined.

当我们试图将user[field as keyof User]赋值给这undefined个可能的属性时,TypeScrip抛出这个错误.


我试着询问了ChatGPT和BingAI,他们提出了一些老生常谈的解决方案,比如将cleanedUser声明为any,这是我不想做的.

所以我转向SO寻求适当的解决方案.

推荐答案

TL;DR答案

将该函数更改为

function handleGDPRDeletionRequest(user: User, deletionMarkers: ObjectsNeedingGDPRDeletion) {

  const cleanedUser: Partial<User> = Object.fromEntries(
    Object.entries(user).filter(([userKey]) => !deletionMarkers[userKey as keyof User])
  );

  console.log(cleanedUser);  // Prints: { id: 1 }
}

为什么它能解决这个问题?

它不是try 将类型脚本标记为不兼容的赋值,而是构造一个干净的新对象,该对象类型脚本可以自信地推断其类型.

要完全理解这一点,请阅读下面的解释部分.


问题解释

错误发生在第行

cleanedUser[field as keyof User] = user[field as keyof User];

在此赋值过程中,TS无法理解cleanedUser[field]user[field]对应于来自一个特定键的samespecific数据类型.

因此,TS将只允许与all种可能的密钥类型cleanedUser兼容的值.

让我们快速回忆一下cleanedUseruser的类型

 

cleanedUser的所有可能的密钥类型为

  1. number | undefined
  2. string | undefined

与上述两种类型兼容的类型为undefined.这就是TS在这里允许的.

因此出现错误:

Type 'string | number' is not assignable to type 'undefined'.
  Type 'string' is not assignable to type 'undefined'.

为什么错误有2行?

第1行显示了错误,其中包括完整的类型.在第2行中,它 Select string,只是因为它是并集类型的第一个组成部分,以突出显示整个类型string | number中与左侧不兼容的一个特定部分的更具体的示例(undefined).

在这种情况下,第二行实际上并没有提供任何额外的信息,甚至可能有一点误导,因为stringnumber都与左侧的undefined不兼容-string不是唯一的问题.然而,在许多其他的排版错误中,对排版慢慢地分解大字体以突出不兼容的部分是非常有帮助的.


解的解释

  const cleanedUser: Partial<User> = Object.fromEntries(
    Object.entries(user).filter(([userKey]) => !deletionMarkers[userKey as keyof User])
  );
  • Object.entries(user):将user对象转换为[key, value]元组的array.相当于[['id', 1], ['name', 'John Doe']].
  • .filter(([userKey]) => !deletionMarkers[userKey as keyof User]):这将应用来自数组原型的Filter方法,该方法遍历数组的每个元素,并且只包括所提供的回调函数为其返回True的那些元素. [userKey] destructures传递给它的元组并从每个[key,Value]元组中获取第一个元素.
  • Object.fromEntries(...):该函数本质上与Object.entries(...)相反.它接收一个可迭代的条目(例如,一个数组),其中每个条目都是一个具有键-值对的数组,并将其转换回一个对象.所以,Object.fromEntries([['id', 1]])等于{ id: 1 }.

参考

Typescript相关问答推荐

如何根据参数的值缩小函数内的签名范围?

如何获取数组所有可能的索引作为数字联合类型?

TypScript ' NoInfer '类型未按预期工作

VS代码1.88.0中未出现自动导入建议

用泛型类型覆盖父类函数导致类型y中的Property x不能赋给基类型Parent中的相同属性."'''''' "

如果请求是流,如何等待所有响应块(Axios)

如何缩小变量访问的对象属性范围?

如何按不能保证的功能过滤按键?

Cypress页面对象模型模式.扩展Elements属性

为什么Typescript不能推断类型?

布局组件中的Angular 17命名路由出口

TypeScrip:强制使用动态密钥

ANGLE订阅要么不更新价值,要么不识别价值变化

TYPE FOR ARRAY,其中每个泛型元素是单独的键类型对,然后在该数组上循环

如何避免从引用<;字符串&>到引用<;字符串|未定义&>;的不安全赋值?

有没有办法在Reaction组件中动态导入打字脚本`type`?

使用RXJS获取数据的3种不同REST API

Typescript 子类继承的方法允许参数中未定义的键

const nav: typechecking = useNavigation() 和 const nav = useNavigation() 之间有什么区别?

是否可以从 TypeScript 的类型推断中排除this?