jiangshanmeta.github.io icon indicating copy to clipboard operation
jiangshanmeta.github.io copied to clipboard

TypeScript中为primitive type添加名义类型

Open jiangshanmeta opened this issue 3 years ago • 2 comments

在开发中我们通常会遇到各种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);
}

jiangshanmeta avatar Jan 14 '22 08:01 jiangshanmeta

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

jiangshanmeta avatar Oct 27 '23 14:10 jiangshanmeta

so cool, using an OR | it behaves very good: Screenshot 2023-10-31 at 15 59 55

what, is this the solution for the 10 year old nominal type issue?

TobiasNickel avatar Oct 31 '23 15:10 TobiasNickel