🎖️typeScrpt中如何禁止操作某些属性?

简介: 在 TypeScript 中,类型否定允许你创建一个明确排除某些属性的类型。通常情况下,我们定义类型来规定对象必须具备的属性。而类型否定,就是相反的操作:我们规定对象不能有哪些属性,你可以将它视为“保留属性”。

嗨,大家好!这里是道长王jj~ 🎩🧙‍♂️

在 TypeScript 中,类型否定允许你创建一个明确排除某些属性的类型。通常情况下,我们定义类型来规定对象必须具备的属性。而类型否定,就是相反的操作:我们规定对象不能有哪些属性,你可以将它视为“保留属性”。

让我们看一个例子。假设我们有一个通用的 createItem 函数,可以将新的项目插入到 NoSQL 数据库(比如 MongoDB、DynamoDB 等)中。NoSQL 数据库表没有预定义的列模式,这意味着项目及其所有属性都会按原样存储。然而,为了检索这些项目,我们需要至少定义一个属性作为主键(比如,在 DynamoDB 中,这是哈希键)。通常是一个整数或 UUID 形式的 ID,可能由数据库或应用程序定义。

function createItem<TItem extends object>(item: TItem): TItem {
   
  // 数据库设置 ID
  const newItem = db.insert(item);

  return newItem;
}

// 返回带有 id { id: "0d92b425efc9", name: "John", ... } 的对象
const user = createItem({
    name: "John", email: "john@doe.com" });

我们可以使用任何 JavaScript 对象来调用这个函数,并将它存储在我们的 NoSQL 数据库中。但是如果我们传递的对象包含 id 属性会发生什么?

// 会发生什么?
//> 会创建一个新项目吗?
//> 如果已经存在该项目,会覆盖吗?
const user = createItem({
    id: "0d92b425efc9", name: "John", email: "john@doe.com" });

当然,实际发生的情况取决于数据库的设置。可能会创建新项目,也可能会覆盖现有项目,或者如果我们不应该指定外部 ID,甚至可能会引发错误。当然,我们可以简单地在函数中检查 id 属性并自行引发错误来防止这种情况。

function createItem<TItem extends object>(item: TItem): TItem {
   
  if('id' in item) throw new Error("项目不得包含 ID");
  // 数据库设置 ID
  const newItem = db.insert(item);

  return newItem;
}

// 抛出错误
const user = createItem({
    id: "0d92b425efc9", name: "John", email: "john@doe.com" });

现在,让我们更进一步,使用 TypeScript 的泛型和类型来从一开始就防止这种情况发生。我们只是禁止项目包含 id 属性。

type ReservedKeys = {
   
  id: string;
}

function createItem<TItem extends object>(
  item: TItem extends ReservedKeys ? never : TItem
): TItem {
   
  if('id' in item) throw new Error("项目不得包含 ID");
  // 数据库设置 ID
  const newItem = db.insert(item);

  return newItem;
}

在这个示例中,我们定义了一个 ReservedKeys 类型,其中包含我们不希望出现在项目中的属性。在函数签名中,我们使用 TItem extends ReservedKeys 来检查泛型 TItem 是否是 ReservedKeys 类型的子集。如果是,我们将函数的参数类型设置为特殊值 never

让我们回到之前的例子。现在,当我们传递带有 ID 的对象时会发生什么?

// 会发生什么?
//> TypeScript 错误:类型 '{ id: string; name: string; email: string; }' 无法分配给类型 'never'
const user = createItem({
    id: "0d92b425efc9", name: "John", email: "john@doe.com" });

TypeScript 报错,指出我们传递给函数的对象与预期的类型不匹配。

当然,我们永远不应该仅仅依靠静态类型检查来避免这种错误。实际实现中的运行时检查也应该始终检查属性 id 是否存在。类型否定是一种在编译时捕获可能错误的语法糖,具有与实际实现相匹配的函数签名。


🎉 你觉得怎么样?这篇文章可以给你带来帮助吗?当你处于这个阶段时,你发现什么对你帮助最大?如果你有任何疑问或者想进一步讨论相关话题,请随时发表评论分享您的想法,让其他人从中受益。🚀✨

目录
相关文章
|
编解码 算法 文件存储
浅谈动图文件格式 - GIF
介绍动图的文件格式,及其优劣
2651 0
浅谈动图文件格式 - GIF
|
NoSQL 关系型数据库 数据库
天生一对,当游戏遇上MongoDB
当游戏遇上MongoDB,会碰撞出什么样的火花,本文为您一一道来。MongoDB针对游戏灵活多变需求、一些专有场景-道具自动过期和附近玩家、高可用、高可扩展、回档、滚服、运营数据分析等场景都有非常好的解决方案,可谓是天生一对。
11200 0
|
NoSQL Linux 虚拟化
Linux内核调试方法总结【转】
内核开发比用户空间开发更难的一个因素就是内核调试艰难。内核错误往往会导致系统宕机,很难保留出错时的现场。调试内核的关键在于你的对内核的深刻理解。
2167 0
|
Java API Apache
阿里巴巴开源框架JarsLink
JarsLink是一个基于JAVA的模块化开发框架,它提供在运行时动态加载模块(JAR包)、卸载模块和模块间调用的API,它能够帮助你进行模块化开发,也能帮助你的系统在运行时动态添加新功能,减少编译、打包和部署带来的发布耗时,同时它也是阿里巴巴的开源项目之一 https://github.com/alibaba/jarslink,目前在微贷事业群各团队广泛使用。
14681 0
|
机器学习/深度学习 算法 数据挖掘
【机器学习】解释什么是K-means聚类?
【5月更文挑战第11天】【机器学习】解释什么是K-means聚类?
|
人工智能 移动开发 算法
王道考研操作系统同步与互斥(王道大题详解)(一)
王道考研操作系统同步与互斥(王道大题详解)(一)
508 1
王道考研操作系统同步与互斥(王道大题详解)(一)
|
数据安全/隐私保护 网络架构
计算机网络实验(华为eNSP模拟器)——第七章 远程终端协议(远程登陆)
计算机网络实验(华为eNSP模拟器)——第七章 远程终端协议(远程登陆)
计算机网络实验(华为eNSP模拟器)——第七章 远程终端协议(远程登陆)
|
SQL 关系型数据库 MySQL
MySQL:The used SELECT statements have a different number of columns
执行SQL报错:The used SELECT statements have a different number of columns
1093 0
MySQL:The used SELECT statements have a different number of columns
|
消息中间件 SpringCloudAlibaba Java
SpringCloud 第一代与第二代的区别 | 学习笔记
快速学习 SpringCloud 第一代与第二代的区别
1256 0
SpringCloud 第一代与第二代的区别 | 学习笔记
|
传感器 供应链 数据可视化
【HaaS成功案例】基于HaaS开发框架的智能农业物联网大棚系统
【HaaS成功案例】基于HaaS开发框架的智能农业物联网大棚系统
【HaaS成功案例】基于HaaS开发框架的智能农业物联网大棚系统