Skip to main content

Opaque Type Aliases

Opaque type aliases are type aliases that do not allow access to their underlying type outside of the file in which they are defined.

1opaque type ID = string;

Opaque type aliases, like regular type aliases, may be used anywhere a type can be used.

1opaque type ID = string;2
3function identity(x: ID): ID {4  return x;5}6export type {ID};

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

import type {NumberAlias} from './exports';

0 as NumberAlias; // Error: 0 is not a NumberAlias!

function convert(x: NumberAlias): number {
return x; // Error: x is not a number!
}

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

import type {ID} from './exports';

function formatID(x: ID): string {
return "ID: " + x; // Ok! IDs are strings.
}

function toID(x: string): ID {
return x; // Error: strings are not IDs.
}

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 string
2opaque type Good: {x: string, ...} = {x: string, y: number};
1:27-1:32: number [1] is incompatible with string [2]. [incompatible-type]

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
7var 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;