如何强制方法参数为无属性超类型的子类型?
如何强制函数参数仅属于无属性超类型的子类型
问题背景
我们有一系列用于更新操作的类型,示例如下:
type DateUpdateObj = { newDate: Date } type LengthUpdateObj = { newLength: number, unit: 'm'|'cm' } // 后续可扩展更多同类型的 ...UpdateObj 结构
需要实现一个函数,仅接受上述这类更新子类型作为参数,拒绝其他任意类型。
首次尝试的问题
参考Java/Kotlin的标记接口思路,尝试用私有属性类作为标记:
class UpdateObj { private readonly _: never } // 作为标记接口 type MyUpdateObj = { foo: string } & UpdateObj // 新的更新对象类型 function send<T extends UpdateObj>(updateObj: T) {} // 仅接受UpdateObj类型
测试时出现不符合预期的情况:
send("") // 符合预期,编译失败 send(new Date()) // 符合预期,编译失败 send({ foo: 'hurray' } as MyUpdateObj) // 符合预期,编译通过 const myUpdate: MyUpdateObj = { foo: "err" } // 预期编译通过但实际失败,因缺少私有属性_,且类型断言会破坏源码兼容性 send(myUpdate)
可行解决方案:品牌类型标记
利用TypeScript的unique symbol实现品牌类型,既不需要添加实际运行时属性,又能在编译时完成类型约束:
// 定义全局唯一的品牌标记符号 const UpdateBrand: unique symbol = Symbol('update-brand'); // 标记接口,仅包含可选的品牌属性(无需实例化时添加) interface UpdateObj { [UpdateBrand]?: never; } // 所有更新类型交叉该标记接口 type DateUpdateObj = { newDate: Date } & UpdateObj; type LengthUpdateObj = { newLength: number, unit: 'm'|'cm' } & UpdateObj; type MyUpdateObj = { foo: string } & UpdateObj; // 函数约束为仅接受带标记的类型 function send<T extends UpdateObj>(updateObj: T) {}
测试验证
send("") // 编译失败,符合预期 send(new Date()) // 编译失败,符合预期 const myUpdate: MyUpdateObj = { foo: "success" } // 编译通过,无需额外属性 send(myUpdate) // 编译通过,符合预期
方案说明
unique symbol确保标记唯一,避免其他类型意外满足约束- 可选的品牌属性
[UpdateBrand]?: never,让类型实例化时无需添加任何额外属性,仅在编译时做类型校验 - 所有需要被允许的更新类型只需交叉
UpdateObj,就能被send函数接受,其他未标记类型会被编译拦截
内容的提问来源于stack exchange,提问作者Chriss
相关产品推荐
相关产品推荐

