嗨,大家好!这里是道长王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
是否存在。类型否定是一种在编译时捕获可能错误的语法糖,具有与实际实现相匹配的函数签名。
🎉 你觉得怎么样?这篇文章可以给你带来帮助吗?当你处于这个阶段时,你发现什么对你帮助最大?如果你有任何疑问或者想进一步讨论相关话题,请随时发表评论分享您的想法,让其他人从中受益。🚀✨