在 Typescript 中执行名义类型的最强方法?

Sho*_*hou 4 generics newtype typescript nominal-typing opaque-types

我见过许多不同的方法来在 Typescript 中执行名义类型,但它们似乎都在某些方面存在不足。我希望保留所有这些属性:

  1. 必须有清晰的(不一定简洁,但如果是的话加分)编译器错误消息来传达哪些不透明类型,例如Type 'GBP' is not assignable to type 'JPY'.
  2. 必须真正唯一,以避免意外匹配相似的不透明类型,即没有__tag__键,必须使用unique symbol.
  3. 必须能够拥有安全的泛型函数,采用共享相同底层基元类型的不透明类型,例如<A>(Opaque<number, A>) => Opaque<number, A>

语法清晰的界面会带来更多加分,但我知道这是主观的。

Sho*_*hou 6

这是我发现的最好的方法:

namespace Unique {
  export declare const Newtype: unique symbol
  export declare const JPY: unique symbol
  export declare const GBP: unique symbol
}

type Newtype<A, B extends symbol> = A & { readonly [Unique.Newtype]: B }

type JPY = Newtype<number, typeof Unique.JPY>
type GBP = Newtype<number, typeof Unique.GBP>

const test: <A extends symbol>(a: Newtype<number, A>, b: Newtype<number, A>) => Newtype<number, A>
  = (a, b) => a + b as any // massage the type checker a bit

// fails
test(10 as GBP, 10)
test(10 as GBP, 10 as JPY)

// passes
test(10 as GBP, 10 as GBP)
test(10 as JPY, 10 as JPY)
Run Code Online (Sandbox Code Playgroud)
  1. 成立,但这里没有奖励积分,因为您最终会收到一些包含文件路径的非常令人讨厌的错误消息(实时示例,请参阅“错误”)Newtype<number, typeof import("file:///input").JPY>:。我希望有一种涉及接口扩展或类似的方法来使这个更干净。

  2. 成立,因为 和Unique.Newtype都是Unique.JPYs unique symbol

  3. 之所以成立,是因为我们可以使用 的结构Newtype来确保类型肯定是Newtype,因为它是根据Unique.Newtypewhich 来定义的unique symbol