如何在 TypeScript 中定义不透明类型? [英] How to define an opaque type in TypeScript?

查看:20
本文介绍了如何在 TypeScript 中定义不透明类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

If I recall correctly, in C++ you can define an an opaque type like this ...

class Foo;

... 并将其用作句柄,例如声明函数签名时...

... and use it like a handle e.g. when declaring function signatures ...

void printFoo(const Foo& foo);

然后应用程序代码可能会在没有看到 Foo 的实际定义的情况下使用对 Foo 的引用或对 Foo 的指针.

Application code might then work with references-to-Foo or pointers-to-Foo without seeing the actual definition of Foo.

TypeScript 中是否有类似的东西——你会如何定义不透明类型?

Is there anything similar in TypeScript -- how would you define an opaque type?

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

My problem is that if I define this ...

interface Foo {};

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

... then that's freely interchangeable with other similar types. Is there an idiom?

推荐答案

那是因为 TypeScript 类型系统是结构化的",所以任何具有相同形状的两种类型都可以相互赋值——而不是结构化".名义上的",其中引入像 Foo 这样的新名称将使其不可分配给相同形状的 Bar 类型,反之亦然.

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.

有这个长期存在的问题跟踪对 TS 的名义类型添加.

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:

// 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:

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

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

Note that these representation don't have any effect at runtime, the only overhead is calling the eur constructor.

newtype-ts 提供通用实用程序来定义和使用行为类似于我的类型的值上面的例子.

newtype-ts provides generic utilities for defining and using values of types that behave similar to my examples above.

品牌类型

另一个典型用例是仅在一个方向上保持不可分配性,即处理可分配给 numberEUR 类型:

Another typical use case is to keep the non-assignability only in one direction, i.e. deal with an EUR type which is assignable to number:

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' };

例如参见

中的使用code>io-ts 库.

See for instance this usage in the io-ts library.

这篇关于如何在 TypeScript 中定义不透明类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆