如何在TypeScript中定义不透明类型?

时间:2019-06-24 12:52:40

标签: typescript

如果我没记错的话,在C ++中,您可以定义这样的不透明类型...

class Foo;

...并像手柄一样使用它声明函数签名时...

void printFoo(const Foo& foo);

然后,应用程序代码可能会使用对Foo的引用或对Foo的指针,而不会看到Foo的实际定义。

TypeScript中是否有类似内容-您将如何定义不透明类型?

我的问题是,如果我定义这个……

interface Foo {};

...然后可以与其他类似类型自由互换。有成语吗?

1 个答案:

答案 0 :(得分:2)

这是因为TypeScript类型系统是“结构的”,所以具有相同形状的任何两个类型将可以彼此分配-与“标称”相对,引入“ Foo”这样的新名称会使不可分配为相同形状的Bar类型,反之亦然。

long standing issue跟踪TS的名义类型添加。

TS中不透明类型的一种常见近似方法是使用唯一标记使两种类型在结构上有所不同:

// opaque type module:
export type EUR = { readonly _tag: 'EUR' };
export function eur(value: number): EUR {
  return value as any;
}
export function addEuros(a: EUR, b: EUR): EUR {
  return ((a as any) + (b as any)) as any;
}

// usage from other modules:
const result: EUR = addEuros(eur(1), eur(10)); // OK
const c = eur(1) + eur(10) // Error: Operator '+' cannot be applied to types 'EUR' and 'EUR'.

更好的是,可以使用唯一的符号对标签进行编码,以确保永远不会访问和使用该标签:

declare const tag: unique symbol;
export type EUR = { readonly [tag]: 'EUR' };

请注意,这些表示形式在运行时没有任何作用,唯一的开销是调用eur构造函数。

newtype-ts提供了通用工具,用于定义和使用行为与上面的示例类似的类型的值。

品牌类型

另一个典型的用例是仅将不可分配性保持在一个方向上,即处理可分配给EUR的{​​{1}}类型:

number

这可以通过所谓的“品牌类型”获得:

declare const a: EUR;
const b: number = a; // OK

例如查看此usage in the io-ts library.