如何用TypeScript类型防止透传变量与其他类型意外比较?
问题:如何用TypeScript类型防止透传变量被意外比较?
前提
我有一个仅在应用中透传的变量,从不修改或比较。它来自某API字段,将原封不动通过另一API字段返回给服务器。
需求
希望定义一种TypeScript类型,实现以下效果:
- 禁止该类型变量与其他类型(包括原始类型)进行比较操作
- 允许该类型变量在指定的透传函数间传递
- 禁止直接用普通值赋值给该类型的参数
示例代码(期望行为)
type MyUnknownPassthroughServerType = string; function getSomethingFromTheServer(): MyUnknownPassthroughServerType { return 'test' as MyUnknownPassthroughServerType; } function writeSomethingToTheServer(payload: MyUnknownPassthroughServerType): void {} const serverVar = getSomethingFromTheServer(); if (serverVar === 'test') { // <- 期望此处出现编译错误 // 执行操作 } if (serverVar === 1) { // <- 期望此处出现编译错误 // 执行操作 } writeSomethingToTheServer(serverVar); // <- 此操作应被允许 writeSomethingToTheServer('test') // <- 期望此处出现编译错误
现有方案的问题
之前用type MyUnknownPassthroughServerType = {neverUsed: boolean};这类无重叠类型,虽然能实现隔离,但丢失了原始类型的特性,透传时可能需要额外处理,不够优雅。
更优解决方案:品牌类型(Branded Types)
TypeScript的品牌类型可以完美解决这个问题——它在保留原始类型底层值的同时,给类型添加一个“品牌”标识,让它与普通原始类型不兼容,从而阻止意外的比较和赋值。
实现方式1:对象字面量品牌
// 定义品牌类型:基于string,添加唯一标识字段 type MyUnknownPassthroughServerType = string & { __brand: 'MyUnknownPassthroughServerType' }; function getSomethingFromTheServer(): MyUnknownPassthroughServerType { // 只有通过类型断言才能创建该类型的实例 return 'test' as MyUnknownPassthroughServerType; } function writeSomethingToTheServer(payload: MyUnknownPassthroughServerType): void {} const serverVar = getSomethingFromTheServer(); if (serverVar === 'test') { // ❌ 编译错误:类型不兼容 // 执行操作 } if (serverVar === 1) { // ❌ 编译错误:类型不兼容 // 执行操作 } writeSomethingToTheServer(serverVar); // ✅ 正常通过 writeSomethingToTheServer('test') // ❌ 编译错误:普通string无法赋值给品牌类型
实现方式2:unique symbol品牌(更安全)
如果需要避免不同品牌类型之间的混淆,可以用unique symbol定义品牌:
// 声明唯一符号作为品牌标识 declare const MyPassthroughBrand: unique symbol; type MyUnknownPassthroughServerType = string & { [MyPassthroughBrand]: never }; // 后续用法和上面一致 function getSomethingFromTheServer(): MyUnknownPassthroughServerType { return 'test' as MyUnknownPassthroughServerType; }
方案优势
- 保留原始类型的底层值(比如这里是string),透传时不需要额外转换
- 强制只能通过指定的入口函数创建该类型实例,避免随意赋值
- 编译阶段直接阻止意外的比较操作,比ESLint规则更可靠
ESLint规则补充(可选)
如果需要额外的代码检查,可以使用@typescript-eslint/restrict-plus-operands规则限制类型不兼容的操作,但品牌类型已经能在编译阶段解决核心问题,通常无需额外ESLint规则。
内容的提问来源于stack exchange,提问作者lentschi
相关产品推荐
相关产品推荐

