简体   繁体   English

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

[英]How to define an opaque type in TypeScript?

If I recall correctly, in C++ you can define an an opaque type like this ...如果我没记错的话,在 C++ 中你可以定义一个像这样的不透明类型......

class Foo;

... and use it like a handle eg when declaring function signatures ... ...并像句柄一样使用它,例如在声明函数签名时...

void printFoo(const Foo& foo);

Application code might then work with references-to-Foo or pointers-to-Foo without seeing the actual definition of Foo.然后,应用程序代码可能会在没有看到 Foo 的实际定义的情况下使用对 Foo 的引用或对 Foo 的指针。

Is there anything similar in TypeScript -- how would you define an opaque type? TypeScript 中是否有类似的东西——你会如何定义一个不透明的类型?

My problem is that if I define this ...我的问题是,如果我定义这个......

interface Foo {};

... then that's freely interchangeable with other similar types. ...然后可以与其他类似类型自由互换。 Is there an idiom?有成语吗?

That is because TypeScript type system is "structural", so any two types with the same shape will be assignable one to each other - as opposed to "nominal", where introducing a new name like Foo would make it non-assignable to a same-shape Bar type, and viceversa.那是因为 TypeScript 类型系统是“结构化的”,因此任何具有相同形状的两种类型都可以相互分配——而不是“名义”,在这种情况下,引入像Foo这样的新名称将使其不可分配给相同的-shape Bar类型,反之亦然。

There's this long standing issue tracking nominal typings additions to TS.跟踪 TS 的名义类型添加是一个长期存在的问题

One common approximation of opaque types in TS is using a unique tag to make any two types structurally different: TS 中 opaque 类型的一种常见近似是使用唯一的标签来使任何两种类型在结构上不同:

// 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'.

Even better, the tag can be encoded with a unique Symbol to make sure it is never accessed and used otherwise:更好的是,标签可以用唯一的 Symbol 进行编码,以确保它永远不会被访问和使用:

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

Note that these representation don't have any effect at runtime, the only overhead is calling the eur constructor.请注意,这些表示在运行时没有任何影响,唯一的开销是调用eur构造函数。

newtype-ts provides generic utilities for defining and using values of types that behave similar to my examples above. newtype-ts提供了通用实用程序,用于定义和使用行为类似于我上面的示例的类型的值。

Branded types品牌类型

Another typical use case is to keep the non-assignability only in one direction, ie deal with an EUR type which is assignable to number :另一个典型用例是仅在一个方向上保持不可分配性,即处理可分配给numberEUR类型:

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

This can be obtained via so called "branded types":这可以通过所谓的“品牌类型”获得:

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

See for instance this usage in the io-ts library.例如,在io-ts库中查看这种用法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM