如何声明无法赋值给索引签名类型的完全Opaque type?
解决TypeScript完全不透明类型被索引签名类型兼容的问题
通常我们会用以下方式声明完全不透明类型(非品牌类型):
declare const sym: unique symbol; type O = { [sym]: true };
但这种类型存在一个问题:它会被带有字符串索引签名的类型(比如Record<string, number>)兼容,示例如下:
type T = { [key: string]: number }; const o: O = {} as O; const t: T = o; // 无报错,不符合仅O自身可赋值的预期
原因在于TypeScript的结构类型系统中,带字符串索引的类型兼容空对象,而O的实例可以赋值给空对象,进而被T兼容。
要实现O仅能赋值给自己和any,可以采用以下几种声明方式:
方式一:添加冲突的never类型字符串索引
给O添加一个字符串索引签名,类型设为never,这样任何带字符串索引的类型(只要其索引值类型不是never)都无法兼容O:
declare const sym: unique symbol; type O = { [sym]: true; [key: string]: never; // 新增字符串索引,类型为never }; type T = { [key: string]: number }; const o: O = {} as O; const t: T = o; // 报错,符合预期
这种方式下,O只能赋值给自己或者any,其他类型要么不匹配symbol属性,要么索引签名类型冲突。
方式二:交叉unknown类型的品牌
将O与unknown交叉,利用unknown的严格特性,让它无法赋值给除自身和any外的其他类型:
declare const sym: unique symbol; type O = unknown & { [sym]: true }; type T = { [key: string]: number }; const o: O = {} as O; const t: T = o; // 报错,符合预期
这种方式下,O因为交叉了unknown,除自身和any外,无法赋值给包括空对象在内的任何其他类型。
内容的提问来源于stack exchange,提问作者Yevgeniy P
相关产品推荐
相关产品推荐

