如何让TypeScript对结构兼容的不同类型赋值触发报错?
让TypeScript拒绝超类型赋值的几种方法
由于TypeScript采用结构类型系统,当user2是user的超集(包含user的所有属性)时,默认允许将user2类型的变量赋值给user类型变量。要触发错误,可以通过以下几种方式实现名义类型的效果(让TypeScript不再只看结构,而是区分类型本身):
方法一:使用类+私有属性
类中的私有成员会被TypeScript的类型检查视为类型的一部分,即使两个类型结构相似,只要包含私有成员,就会被判定为不兼容。
class User { // 仅用于区分类型的私有属性,无需实际使用 private _typeMarker: void; name: string; constructor(name: string) { this.name = name; } } interface User2 { name: string; numberOrNull: number | null; } let usr: User = new User("hey"); let usr2: User2 = { name: "as", numberOrNull: 12 }; usr = usr2; // TypeScript报错:类型"User2"缺少类型"User"中的私有属性"_typeMarker"
方法二:品牌类型(Branded Types)
通过给类型附加一个唯一的符号属性作为"品牌",让TypeScript识别出不同类型的区别。
// 定义品牌类型工具 type Brand<T, BrandName> = T & { readonly [Symbol.toStringTag]: BrandName }; // 给User添加品牌标记 type User = Brand<{ name: string }, "User">; type User2 = { name: string; numberOrNull: number | null }; // 创建User类型变量时必须带上品牌属性(可通过类型断言简化) let usr: User = { name: "hey", [Symbol.toStringTag]: "User" } as User; let usr2: User2 = { name: "as", numberOrNull: 12 }; usr = usr2; // TypeScript报错:类型"User2"中缺少属性"[Symbol.toStringTag]"
方法三:通过泛型约束强制无多余属性
利用泛型和条件类型,限制赋值的类型不能包含User之外的属性:
interface User { name: string } interface User2 { name: string; numberOrNull: number | null } // 辅助函数:仅接受无多余属性的User类型 function strictAssignUser<T extends User>( user: Exclude<keyof T, keyof User> extends never ? T : never ): User { return user; } let usr2: User2 = { name: "as", numberOrNull: 12 }; let usr: User = strictAssignUser(usr2); // TypeScript报错:类型"User2"不能赋值给类型"never"
以上方法中,前两种(类私有属性、品牌类型)是最常用且稳定的方案,能有效让TypeScript拒绝超类型的赋值操作。
内容的提问来源于stack exchange,提问作者Valeri Tonchev
相关产品推荐
相关产品推荐

