首页 > 解决方案 > TypeScript 中是否可以使用编译时标记的数字?

问题描述

例如,我想创建一个Port类型和一个Seconds类型,它们都是围绕number类型的薄包装器,因此两者都不能分配给另一个。

let httpPort: Port = 80;
let oneMinute: Seconds = 60;

httpPort = oneMinute; // type error
oneMinute = httpPort; // type error

标签: typescripttypes

解决方案


我们可以通过unique symbol. 具体来说,具有声明类型的字段unique symbol永远不会与另一个同名字段比较兼容,除非它来自同一个声明。所以我们可以用伪造的唯一符号标记我们的数字,使它们无与伦比。

type Port = number & { readonly __tag: unique symbol };
type Seconds = number & { readonly __tag: unique symbol };

// Note that we do have to explicitly cast the numbers, as we're
// technically lying to the type system to get this behavior.
let httpPort = 80 as Port;
let oneMinute = 60 as Seconds;

现在httpPortoneMinute是互不相容的。如果我们尝试比较它们,我们会得到类似的结果。

file.ts:8:1 - error TS2322: Type 'Seconds' is not assignable to type 'Port'.
  Type 'Seconds' is not assignable to type '{ readonly __tag: unique symbol; }'.
    Types of property '__tag' are incompatible.
      Type 'typeof __tag' is not assignable to type 'typeof __tag'. Two different types with this name exist, but they are unrelated.

8 httpPort = oneMinute; // type error

不幸的是,我们仍然可以做一些无意义的事情,比如添加两个端口或一个端口或第二个端口,因为 Typescript 很乐意将它们中的任何一个向上转换为number(毕竟这就是交集类型的工作方式),但至少我们不能传递一个Port给期望 a 的函数Seconds,或错误地分配变量。

请注意,您尝试执行的操作通常被称为“新类型模式”,newtype位于 Haskell 中的关键字之后(正是为了这个目的而存在)。特别是,您可以在此页面上阅读有关它在 Typescript 中的使用的更多信息(我也在这里学习了这个小技巧)。


推荐阅读