How to define an opaque type in TypeScript?

后端 未结 1 854
半阙折子戏
半阙折子戏 2020-12-11 00:49

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 fu

相关标签:
1条回答
  • 2020-12-11 01:44

    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.

    There's this long standing issue tracking nominal typings additions to 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' };
    

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

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

    Branded types

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

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

    0 讨论(0)
提交回复
热议问题