jiangshanmeta.github.io
jiangshanmeta.github.io copied to clipboard
TypeScript中为primitive type添加名义类型
在开发中我们通常会遇到各种ID,通常我们会把这些ID的类型标为string或者number。当业务中关联ID比较多的时候,就有可能在需要A的ID的时候传了B的ID,我们过于宽泛的类型标注导致类型系统无法帮助我们找到这个错误。一个思路是使用名义类型。
虽然TypeScript是结构类型而不是名义类型,但是依然可以模拟。其中比较核心的一点就是采用unique symbol,每一个unique symbol都是不同的 ( 有点NaN的味道了 ), 对于primitive type,我们可以使用联合类型为其添加名义类型
type UserId = string & {
readonly __nominal: unique symbol;
};
type OrderId = string & {
readonly __nominal: unique symbol;
};
然后我们就会发现,不通ID类型之间是不能随意赋值的:
const userId = 'userId' as UserId
const orderId:OrderId = userId // 类型系统报错
关于这种写法,我们也可以这么理解,这是一类特殊的字符串(数值),但是用TS的类型系统很难描述,比如 “除了某些特性字符串的字符串” “正数” Negated types
type PositiveNumber = number & {
readonly __nominal: unique symbol;
};
function isPositiveNumber(x: number): x is PositiveNumber {
return x > 0;
}
function onlyHandlePositiveNumber(num: PositiveNumber) {}
const num = 100;
// 类型报错
// onlyHandlePositiveNumber(num)
if (isPositiveNumber(num)) {
onlyHandlePositiveNumber(num);
}
https://github.com/andnp/SimplyTyped/blob/master/src/types/utils.ts#L41 https://github.com/piotrwitek/utility-types/blob/master/src/mapped-types.ts#L537 https://github.com/Microsoft/TypeScript/issues/202
so cool, using an OR |
it behaves very good:
what, is this the solution for the 10 year old nominal type issue?