TypeScript参数化类型编译通过是编译器bug还是程序合理?
问题原因
TypeScript 采用结构类型系统判断类型兼容性,而非标称类型系统:只要两个类型的实例结构完全一致,TS 就会认为二者互相兼容。
你代码里的泛型参数T仅在构造函数的入参中使用,没有被作为实例的属性保留,因此RegularString<"^...$">和RegularString<"^.$">的实例结构完全相同:都包含一个私有value字符串属性和一个toString方法,TS 自然不会判定赋值类型错误。
修复方案
只需要将泛型参数T绑定到实例属性上,让不同的T产生不同的实例结构即可,常见两种实现:
方案1:显式存储正则规则到实例
class RegularString<T extends string> { // 新增pattern属性,类型绑定为T constructor(private readonly value: string, private readonly pattern: T) { if(!new RegExp(pattern).test(value)) throw new Error("Given value does not conform to regex " + pattern + ". found: " + value) } toString() { return this.value } } const strLengthThree: RegularString<"^...$"> = new RegularString<"^...$">("ABC", "^...$") const strLengthOne: RegularString<"^.$"> = strLengthThree // 此时就会触发预期的类型错误
方案2:使用幻影类型标记(不需要实际存储值)
如果不想对外暴露正则规则,也可以加一个仅用于类型区分的私有幻影属性,不需要赋值,不占用运行时空间:
class RegularString<T extends string> { // 幻影属性,仅用于类型标记 private readonly _typeMarker?: T constructor(private readonly value: string, regexPatternConstraint: T) { if(!new RegExp(regexPatternConstraint).test(value)) throw new Error("Given value does not conform to regex " + regexPatternConstraint + ". found: " + value) } toString() { return this.value } } const strLengthThree: RegularString<"^...$"> = new RegularString<"^...$">("ABC", "^...$") const strLengthOne: RegularString<"^.$"> = strLengthThree // 同样会触发预期的类型错误
内容的提问来源于stack exchange,提问作者Dejas
相关产品推荐
相关产品推荐

