我有一个DeepWriteable
类型和一个品牌号码类型PositiveNumber
。PositiveNumber
扩展了number
,但是DeepWriteable<PositiveNumber>
没有。品牌字符串类型也是如此。这里发生了什么?
type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> };
type PositiveNumber = number & { __brand: 'PositiveNumber' };
type NonEmptyString = string & { __brand: 'NonEmptyString' };
type test1 = PositiveNumber extends number ? true : false;
// TRUE
type test2 = DeepWriteable<PositiveNumber> extends number ? true : false;
// FALSE
type test3 = NonEmptyString extends string ? true : false;
// TRUE
type test4 = DeepWriteable<NonEmptyString> extends string ? true : false;
// FALSE
(BTW,我只询问品牌类型,因为常规number
和string
似乎没有此行为)
type test5 = DeepWriteable<number> extends number ? true : false;
// TRUE
type test6 = DeepWriteable<string> extends string ? true : false;
// TRUE
我已经采取了只添加一个检查DeepWriteable
不Map到number
和string
的工作正常,但我很好奇为什么有必要。
是因为-readonly
转换使它成为一个非同态Map类型吗?我想我也许可以理解为什么string
会发生这种情况,因为length
是readonly
。在number
上有类似的readonly
属性吗?
1条答案
按热度按时间e3bfsja21#
任何标记原语(与类对象类型相交的原语类型)上的mapped type(甚至是homomorphic)都将最终销毁该原语类型。
在microsoft/TypeScript#12447中,规则的、非标记的原语上的同态Map类型是特殊情况,以返回不做任何更改的原语。
但是对于标记原语,您将获得输入类型的所有属性和表观属性的Map,这意味着
DeepWriteable<PositiveNumber>
将为您提供具有所有Number
接口属性以及__brand
属性的结果。正如在相关问题microsoft/TypeScript#35992的评论中提到的:
用一个对象类型标记一个原语是一个你自己承担风险的奋进。基本上你可以这样做,只要类型从来没有经历过简化转换......也就是说,我描述的是当前的行为,而不是对未来行为的任何承诺。......我们确实在内部使用了一些标记,但是当我们遇到奇怪的时候,不要对我们自己提出错误😉
因此,看起来建议是避免对品牌原语进行过多的类型操作,或者至少彻底测试此类操作并调整它们,以处理当类型没有按您希望的方式组合时出现的任何意外问题。