Opaque Type Aliases
Opaque type aliases are type aliases that hide their underlying type outside of the file in which they are defined.
1opaque type ID = string;When to use this
Use opaque types over regular type aliases when you need to enforce abstraction boundaries across module boundaries — for example, preventing callers from treating an ID as a plain string. Use the optional supertype constraint when consumers need partial access (e.g. reading an ID as a string but not creating one from a string).
Opaque Type Alias Syntax
Opaque type aliases are created using the words opaque type followed by its
name, an equals sign =, and a type definition.
opaque type Alias = Type;
You can optionally add a subtyping constraint to an opaque type alias by adding
a colon : and a type after the name.
opaque type Alias: SuperType = Type;
Any type can appear as the super type or type of an opaque type alias.
1opaque type StringAlias = string;2opaque type ObjectAlias = {3 property: string,4 method(): number,5};6opaque type UnionAlias = 1 | 2 | 3;7opaque type AliasAlias: ObjectAlias = ObjectAlias;8opaque type VeryOpaque: AliasAlias = ObjectAlias;Opaque Type Alias Type Checking
Within the Defining File
When in the same file the alias is defined, opaque type aliases behave exactly as regular type aliases do.
1opaque type NumberAlias = number;2
30 as NumberAlias;4
5function add(x: NumberAlias, y: NumberAlias): NumberAlias {6 return x + y;7}8function toNumberAlias(x: number): NumberAlias { return x; }9function toNumber(x: NumberAlias): number { return x; }Outside the Defining File
When importing an opaque type alias, it behaves like a nominal type, hiding its underlying type.
exports.js
export opaque type NumberAlias = number;
imports.js
1// In imports.js, NumberAlias is opaque — its underlying type (number) is hidden2declare opaque type NumberAlias;3
40 as NumberAlias; // Error: 0 is not a NumberAlias!incompatible-typeCannot cast 0 to NumberAlias because number [1] is incompatible with NumberAlias [2].5
6function convert(x: NumberAlias): number {7 return x; // Error: x is not a number!incompatible-typeCannot return x because NumberAlias [1] is incompatible with number [2].8}Subtyping Constraints
When you add a subtyping constraint to an opaque type alias, we allow the opaque type to be used as the super type when outside of the defining file.
exports.js
1export opaque type ID: string = string;imports.js
1// In imports.js, ID is opaque with a supertype constraint of string2declare opaque type ID: string;3
4function formatID(x: ID): string {5 return "ID: " + x; // OK! IDs are strings.6}7
8function toID(x: string): ID {9 return x; // Error: strings are not IDs.incompatible-typeCannot return x because string [1] is incompatible with ID [2].10}When you create an opaque type alias with a subtyping constraint, the type in the type position must be a subtype of the type in the super type position.
1opaque type Bad: string = number; // Error: number is not a subtype of stringincompatible-typenumber [1] is incompatible with string [2].2opaque type Good: {x: string, ...} = {x: string, y: number};Generics
Opaque type aliases can also have their own generics, and they work exactly as generics do in regular type aliases
1opaque type MyObject<A, B, C>: {foo: A, bar: B, ...} = {2 foo: A,3 bar: B,4 baz: C,5};6
7const val: MyObject<number, boolean, string> = {8 foo: 1,9 bar: true,10 baz: 'three',11};Library Definitions
You can also declare opaque type aliases in libdefs. There, you omit the underlying type, but may still optionally include a super type.
1declare opaque type Foo;2declare opaque type PositiveNumber: number;See Also
- Type Aliases — regular (transparent) type aliases
- Nominal & Structural Typing — opaque types are nominally typed, unlike regular type aliases
- Generics — opaque type aliases can be parameterized with generics
- Library Definitions — declaring opaque types in library definition files