【问题标题】:Strongest way to do nominal types in Typescript?在 Typescript 中做名义类型的最强方法?
【发布时间】:2020-09-03 10:28:42
【问题描述】:
我在 Typescript 中看到了许多不同的方法来处理名义类型,但它们似乎都在某些方面有所不足。我希望保留所有这些属性:
- 必须有明确的(不一定简洁,但如果是的话,可以加分)编译器错误消息传达哪些不透明类型,例如
Type 'GBP' is not assignable to type 'JPY'。
- 必须真正唯一以避免意外匹配类似的不透明类型,即没有
__tag__ 键,必须使用 unique symbol。
- 必须能够让安全的泛型函数采用不透明类型共享相同的底层原始类型,例如
<A>(Opaque<number, A>) => Opaque<number, A>。
语法简洁的界面可以获得更多奖励积分,但我理解这是主观的。
【问题讨论】:
标签:
typescript
generics
newtype
nominal-typing
opaque-types
【解决方案1】:
这是我发现的最好的方法:
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)
保留,但这里没有奖励积分,因为您最终会收到一些包含文件路径的非常讨厌的错误消息(live example,请参阅“错误”):Newtype<number, typeof import("file:///input").JPY>。我希望有一种涉及interface extends 或类似方法的方法可以使这个更干净。
成立,因为Unique.Newtype 和Unique.JPY 都是unique symbols。
-
之所以成立,是因为我们可以使用Newtype 的结构来确保类型肯定是Newtype,因为它是根据Unique.Newtype 定义的,即unique symbol。