🎖️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 是否存在。类型否定是一种在编译时捕获可能错误的语法糖,具有与实际实现相匹配的函数签名。


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

目录
相关文章
|
Shell Linux 开发工具
哇~真的是你呀!今天是用户操作中的修改属性、密码设置、删除
在Linux系统中,修改属性、密码设置和删除用户都是管理用户和文件系统的常见操作,下面让我们一起来看看。
82 1
|
开发框架 安全 .NET
不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault="Deny"),或者是通过包含 oveoverrideMode....
不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault="Deny"),或者是通过包含 oveoverrideMode....
715 0
不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault="Deny"),或者是通过包含 oveoverrideMode....
设置页面禁止转载
设置页面禁止转载
133 0
设置页面禁止转载
|
JavaScript 前端开发
42、属性的操作
元素对象有一个attributes属性,返回一个类似数组的动态对象,成员是该元素标签的所有属性节点对象,属性的实时变化都会反映在这个节点对象上。其他类型的节点对象,虽然也有attributes属性,但返回的都是null,因此可以把这个属性视为元素对象独有的。
173 0
禁止隐藏 history 命令,禁止执行某条命令
通过环境变量替换某条命令
610 0
基类、接口的应用——表单控件:一次添加、修改一条记录,一次修改多条记录。(上)
好久没发帖子了,又加了不少的功能呀。(图片仅是测试,不代表什么表情。) 本来我也想写一个2007的总结的,但是看到很多人都写了,我就不凑热闹了,写点和代码有关系的吧。 写作原因: 1、在项目里做得最多的操作恐怕就是保存数据了,总是要写一大堆的代码,能不能简单一点呢?2005来了,似乎可以减少一些代码,但是03里怎么办呢? 2、基类、接口、策略模式,好多高手都讨论过了,但是都是理论上的,在实践中如何应用呢?在webform 里面又怎么使用呢? 目的: 1、做一个“控件”来应对各种表单的录入,包括一次保存一条记录、一次保存多条记录。
1024 0
附加属性的不同使用方式
原文:附加属性的不同使用方式     [AttachedPropertyBrowsableForType(typeof(ButtonBase))] public static void SetIsClearTextButtonBehaviorEnabled(Button obj, bool value) { obj.
757 0