文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
模拟标称类型系统
类型的重要意义之一是限制了数据的可用操作与实际意义,它是通过类型附带的额外信息来实现的(类似于元数据)。
要在 TypeScript 中实现,其实也只需要为类型额外附加元数据即可,比如 CNY 与 USD,分别附加上它们的单位信息即可,但同时又需要保留原本的信息(即原本的 number 类型)。
declare class TagProtector<T extends string> {
protected __tag__: T;
}
type Nominal<T, U extends string> = T & TagProtector<U>;
type CNY = Nominal<number, 'CNY'>;
type USD = Nominal<number, 'USD'>;
const CNYCount = 100 as CNY;
const USDCount = 100 as USD;
function addCNY(source: CNY, input: CNY) {
return (source + input) as CNY;
}
addCNY(CNYCount, CNYCount);
// 报错了!
addCNY(CNYCount, USDCount);
使用 TagProtector 声明了一个具有 protected 属性的类,使用它来携带额外的信息,并和原本的类型合并到一起,就得到了 Nominal 工具类型。
这一实现方式本质上只在类型层面做了数据的处理,在运行时无法进行进一步的限制。可以从逻辑层面入手进一步确保安全性:
class CNY {
private __tag!: void;
constructor(public value: number) {}
}
class USD {
private __tag!: void;
constructor(public value: number) {}
}
使用方式也要进行变化:
const CNYCount = new CNY(100);
const USDCount = new USD(100);
function addCNY(source: CNY, input: CNY) {
return (source.value + input.value);
}
addCNY(CNYCount, CNYCount);
// 报错了!
addCNY(CNYCount, USDCount);
通过这种方式,在运行时添加更多的检查逻辑,同时在类型层面也得到了保障。
这两种方式的本质都是通过额外属性实现了类型信息的附加,从而使得结构化类型系统将结构一致的两个类型也判断为不可兼容。
将其标记为 private / protected 其实不是必须的,只是为了避免类型信息被错误消费。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论