Opaque type aliases are type aliases that do not allow access to their
underlying type outside of the file in which they are defined.
1
|
opaque type ID = string;
|
{"value":"opaque type ID = string;\n","tokens":[{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":1,"start":12,"end":14},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":15,"end":16},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":17,"end":23},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":23,"end":24}],"errors":[]}
Opaque type aliases, like regular type aliases, may be used anywhere a type can
be used.
1
2
3
4
5
6
7
|
opaque type ID = string;
function identity(x: ID): ID {
return x;
}
export type {ID};
|
{"value":"// @flow\nopaque type ID = string;\n\nfunction identity(x: ID): ID {\n return x;\n}\nexport type {ID};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":2,"start":9,"end":15},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":16,"end":20},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":2,"start":21,"end":23},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":24,"end":25},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":26,"end":32},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":32,"end":33},{"type":"T_FUNCTION","context":"normal","value":"function","line":4,"start":35,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"identity","line":4,"start":44,"end":52},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":4,"start":53,"end":54},{"type":"T_COLON","context":"type","value":":","line":4,"start":54,"end":55},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":4,"start":56,"end":58},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":58,"end":59},{"type":"T_COLON","context":"type","value":":","line":4,"start":59,"end":60},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":4,"start":61,"end":63},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":64,"end":65},{"type":"T_RETURN","context":"normal","value":"return","line":5,"start":68,"end":74},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":5,"start":75,"end":76},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":76,"end":77},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":78,"end":79},{"type":"T_EXPORT","context":"normal","value":"export","line":7,"start":80,"end":86},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":87,"end":91},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":92,"end":93},{"type":"T_IDENTIFIER","context":"normal","value":"ID","line":7,"start":93,"end":95},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":95,"end":96},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":96,"end":97}],"errors":[]}
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.
1
|
opaque type Alias = Type;
|
{"value":"opaque type Alias = Type;\n","tokens":[{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_IDENTIFIER","context":"type","value":"Alias","line":1,"start":12,"end":17},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":1,"start":20,"end":24},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":24,"end":25}],"errors":[]}
You can optionally add a subtyping constraint to an opaque type alias by adding
a colon :
and a type after the name.
1
|
opaque type Alias: SuperType = Type;
|
{"value":"opaque type Alias: SuperType = Type;\n","tokens":[{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_IDENTIFIER","context":"type","value":"Alias","line":1,"start":12,"end":17},{"type":"T_COLON","context":"type","value":":","line":1,"start":17,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"SuperType","line":1,"start":19,"end":28},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":1,"start":31,"end":35},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":35,"end":36}],"errors":[]}
Any type can appear as the super type or type of an opaque type alias.
1
2
3
4
5
6
7
8
|
opaque type StringAlias = string;
opaque type ObjectAlias = {
property: string,
method(): number,
};
opaque type UnionAlias = 1 | 2 | 3;
opaque type AliasAlias: ObjectAlias = ObjectAlias;
opaque type VeryOpaque: AliasAlias = ObjectAlias;
|
{"value":"opaque type StringAlias = string;\nopaque type ObjectAlias = {\n property: string,\n method(): number,\n};\nopaque type UnionAlias = 1 | 2 | 3;\nopaque type AliasAlias: ObjectAlias = ObjectAlias;\nopaque type VeryOpaque: AliasAlias = ObjectAlias;\n","tokens":[{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_IDENTIFIER","context":"type","value":"StringAlias","line":1,"start":12,"end":23},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":24,"end":25},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":26,"end":32},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":32,"end":33},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":2,"start":34,"end":40},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":41,"end":45},{"type":"T_IDENTIFIER","context":"type","value":"ObjectAlias","line":2,"start":46,"end":57},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":58,"end":59},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":3,"start":64,"end":72},{"type":"T_COLON","context":"type","value":":","line":3,"start":72,"end":73},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":74,"end":80},{"type":"T_COMMA","context":"type","value":",","line":3,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":4,"start":84,"end":90},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":90,"end":91},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":91,"end":92},{"type":"T_COLON","context":"type","value":":","line":4,"start":92,"end":93},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":94,"end":100},{"type":"T_COMMA","context":"type","value":",","line":4,"start":100,"end":101},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":102,"end":103},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":103,"end":104},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":6,"start":105,"end":111},{"type":"T_TYPE","context":"normal","value":"type","line":6,"start":112,"end":116},{"type":"T_IDENTIFIER","context":"type","value":"UnionAlias","line":6,"start":117,"end":127},{"type":"T_ASSIGN","context":"type","value":"=","line":6,"start":128,"end":129},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"1","line":6,"start":130,"end":131},{"type":"T_BIT_OR","context":"type","value":"|","line":6,"start":132,"end":133},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"2","line":6,"start":134,"end":135},{"type":"T_BIT_OR","context":"type","value":"|","line":6,"start":136,"end":137},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"3","line":6,"start":138,"end":139},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":139,"end":140},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":7,"start":141,"end":147},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":148,"end":152},{"type":"T_IDENTIFIER","context":"type","value":"AliasAlias","line":7,"start":153,"end":163},{"type":"T_COLON","context":"type","value":":","line":7,"start":163,"end":164},{"type":"T_IDENTIFIER","context":"type","value":"ObjectAlias","line":7,"start":165,"end":176},{"type":"T_ASSIGN","context":"type","value":"=","line":7,"start":177,"end":178},{"type":"T_IDENTIFIER","context":"type","value":"ObjectAlias","line":7,"start":179,"end":190},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":190,"end":191},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":8,"start":192,"end":198},{"type":"T_TYPE","context":"normal","value":"type","line":8,"start":199,"end":203},{"type":"T_IDENTIFIER","context":"type","value":"VeryOpaque","line":8,"start":204,"end":214},{"type":"T_COLON","context":"type","value":":","line":8,"start":214,"end":215},{"type":"T_IDENTIFIER","context":"type","value":"AliasAlias","line":8,"start":216,"end":226},{"type":"T_ASSIGN","context":"type","value":"=","line":8,"start":227,"end":228},{"type":"T_IDENTIFIER","context":"type","value":"ObjectAlias","line":8,"start":229,"end":240},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":240,"end":241}],"errors":[]}
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.
1
2
3
4
5
6
7
8
9
10
|
opaque type NumberAlias = number;
(0: NumberAlias);
function add(x: NumberAlias, y: NumberAlias): NumberAlias {
return x + y;
}
function toNumberAlias(x: number): NumberAlias { return x; }
function toNumber(x: NumberAlias): number { return x; }
|
{"value":"//@flow\nopaque type NumberAlias = number;\n\n(0: NumberAlias);\n\nfunction add(x: NumberAlias, y: NumberAlias): NumberAlias {\n return x + y;\n}\nfunction toNumberAlias(x: number): NumberAlias { return x; }\nfunction toNumber(x: NumberAlias): number { return x; }\n","tokens":[{"type":"Line","context":"comment","value":"//@flow","line":1,"start":0,"end":7},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":2,"start":8,"end":14},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":15,"end":19},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":2,"start":20,"end":31},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":32,"end":33},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":34,"end":40},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":40,"end":41},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":43,"end":44},{"type":"T_NUMBER","context":"normal","value":"0","line":4,"start":44,"end":45},{"type":"T_COLON","context":"type","value":":","line":4,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":4,"start":47,"end":58},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":58,"end":59},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":59,"end":60},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":62,"end":70},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":6,"start":71,"end":74},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":6,"start":75,"end":76},{"type":"T_COLON","context":"type","value":":","line":6,"start":76,"end":77},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":6,"start":78,"end":89},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":6,"start":91,"end":92},{"type":"T_COLON","context":"type","value":":","line":6,"start":92,"end":93},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":6,"start":94,"end":105},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":105,"end":106},{"type":"T_COLON","context":"type","value":":","line":6,"start":106,"end":107},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":6,"start":108,"end":119},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":120,"end":121},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":126,"end":132},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":7,"start":133,"end":134},{"type":"T_PLUS","context":"normal","value":"+","line":7,"start":135,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":7,"start":137,"end":138},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":138,"end":139},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":140,"end":141},{"type":"T_FUNCTION","context":"normal","value":"function","line":9,"start":142,"end":150},{"type":"T_IDENTIFIER","context":"normal","value":"toNumberAlias","line":9,"start":151,"end":164},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":164,"end":165},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":9,"start":165,"end":166},{"type":"T_COLON","context":"type","value":":","line":9,"start":166,"end":167},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":9,"start":168,"end":174},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":174,"end":175},{"type":"T_COLON","context":"type","value":":","line":9,"start":175,"end":176},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":9,"start":177,"end":188},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":189,"end":190},{"type":"T_RETURN","context":"normal","value":"return","line":9,"start":191,"end":197},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":9,"start":198,"end":199},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":199,"end":200},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":201,"end":202},{"type":"T_FUNCTION","context":"normal","value":"function","line":10,"start":203,"end":211},{"type":"T_IDENTIFIER","context":"normal","value":"toNumber","line":10,"start":212,"end":220},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":220,"end":221},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":10,"start":221,"end":222},{"type":"T_COLON","context":"type","value":":","line":10,"start":222,"end":223},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":10,"start":224,"end":235},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":235,"end":236},{"type":"T_COLON","context":"type","value":":","line":10,"start":236,"end":237},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":10,"start":238,"end":244},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":245,"end":246},{"type":"T_RETURN","context":"normal","value":"return","line":10,"start":247,"end":253},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":10,"start":254,"end":255},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":255,"end":256},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":257,"end":258}],"errors":[]}
Outside the Defining File
When importing an opaque type alias, it behaves like a
nominal type, hiding its
underlying type.
exports.js
1
|
export opaque type NumberAlias = number;
|
{"value":"export opaque type NumberAlias = number;\n","tokens":[{"type":"T_EXPORT","context":"normal","value":"export","line":1,"start":0,"end":6},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":7,"end":13},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":14,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":1,"start":19,"end":30},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":31,"end":32},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":33,"end":39},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":39,"end":40}],"errors":[]}
imports.js
1
2
3
4
5
6
7
|
import type {NumberAlias} from './exports';
(0: NumberAlias)
function convert(x: NumberAlias): number {
return x;
}
|
{"value":"import type {NumberAlias} from './exports';\n\n(0: NumberAlias) // Error: 0 is not a NumberAlias!\n\nfunction convert(x: NumberAlias): number {\n return x; // Error: x is not a number!\n}\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":12,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":1,"start":13,"end":24},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":26,"end":30},{"type":"T_STRING","context":"normal","value":"'./exports'","line":1,"start":31,"end":42},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":42,"end":43},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":45,"end":46},{"type":"T_NUMBER","context":"normal","value":"0","line":3,"start":46,"end":47},{"type":"T_COLON","context":"type","value":":","line":3,"start":47,"end":48},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":3,"start":49,"end":60},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":60,"end":61},{"type":"Line","context":"comment","value":"// Error: 0 is not a NumberAlias!","line":3,"start":62,"end":95},{"type":"T_FUNCTION","context":"normal","value":"function","line":5,"start":97,"end":105},{"type":"T_IDENTIFIER","context":"normal","value":"convert","line":5,"start":106,"end":113},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":113,"end":114},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":5,"start":114,"end":115},{"type":"T_COLON","context":"type","value":":","line":5,"start":115,"end":116},{"type":"T_IDENTIFIER","context":"type","value":"NumberAlias","line":5,"start":117,"end":128},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":128,"end":129},{"type":"T_COLON","context":"type","value":":","line":5,"start":129,"end":130},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":131,"end":137},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":138,"end":139},{"type":"T_RETURN","context":"normal","value":"return","line":6,"start":142,"end":148},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":6,"start":149,"end":150},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":150,"end":151},{"type":"Line","context":"comment","value":"// Error: x is not a number!","line":6,"start":152,"end":180},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":181,"end":182}],"errors":[]}
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
1
|
export opaque type ID: string = string;
|
{"value":"export opaque type ID: string = string;\n","tokens":[{"type":"T_EXPORT","context":"normal","value":"export","line":1,"start":0,"end":6},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":7,"end":13},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":14,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":1,"start":19,"end":21},{"type":"T_COLON","context":"type","value":":","line":1,"start":21,"end":22},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":23,"end":29},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":30,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":32,"end":38},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":38,"end":39}],"errors":[]}
imports.js
1
2
3
4
5
6
7
8
9
|
import type {ID} from './exports';
function formatID(x: ID): string {
return "ID: " + x;
}
function toID(x: string): ID {
return x;
}
|
{"value":"import type {ID} from './exports';\n\nfunction formatID(x: ID): string {\n return \"ID: \" + x; // Ok! IDs are strings.\n}\n\nfunction toID(x: string): ID {\n return x; // Error: strings are not IDs.\n}\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":12,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":1,"start":13,"end":15},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":17,"end":21},{"type":"T_STRING","context":"normal","value":"'./exports'","line":1,"start":22,"end":33},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":33,"end":34},{"type":"T_FUNCTION","context":"normal","value":"function","line":3,"start":36,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"formatID","line":3,"start":45,"end":53},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":54,"end":55},{"type":"T_COLON","context":"type","value":":","line":3,"start":55,"end":56},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":3,"start":57,"end":59},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":59,"end":60},{"type":"T_COLON","context":"type","value":":","line":3,"start":60,"end":61},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":62,"end":68},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":69,"end":70},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":75,"end":81},{"type":"T_STRING","context":"normal","value":"\"ID: \"","line":4,"start":82,"end":88},{"type":"T_PLUS","context":"normal","value":"+","line":4,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":4,"start":91,"end":92},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":92,"end":93},{"type":"Line","context":"comment","value":"// Ok! IDs are strings.","line":4,"start":94,"end":117},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":118,"end":119},{"type":"T_FUNCTION","context":"normal","value":"function","line":7,"start":121,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"toID","line":7,"start":130,"end":134},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":134,"end":135},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":7,"start":135,"end":136},{"type":"T_COLON","context":"type","value":":","line":7,"start":136,"end":137},{"type":"T_STRING_TYPE","context":"type","value":"string","line":7,"start":138,"end":144},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":144,"end":145},{"type":"T_COLON","context":"type","value":":","line":7,"start":145,"end":146},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":7,"start":147,"end":149},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":150,"end":151},{"type":"T_RETURN","context":"normal","value":"return","line":8,"start":156,"end":162},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":8,"start":163,"end":164},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":164,"end":165},{"type":"Line","context":"comment","value":"// Error: strings are not IDs.","line":8,"start":166,"end":196},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":197,"end":198}],"errors":[]}
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.
1
2
3
|
opaque type Bad: string = number;
opaque type Good: {x: string} = {x: string, y: number};
|
number [1] is incompatible with string [2]. [incompatible-type]
{"value":"//@flow\nopaque type Bad: string = number; // Error: number is not a subtype of string\nopaque type Good: {x: string} = {x: string, y: number};\n","tokens":[{"type":"Line","context":"comment","value":"//@flow","line":1,"start":0,"end":7},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":2,"start":8,"end":14},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":15,"end":19},{"type":"T_IDENTIFIER","context":"type","value":"Bad","line":2,"start":20,"end":23},{"type":"T_COLON","context":"type","value":":","line":2,"start":23,"end":24},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":25,"end":31},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":32,"end":33},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":34,"end":40},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":40,"end":41},{"type":"Line","context":"comment","value":"// Error: number is not a subtype of string","line":2,"start":42,"end":85},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":3,"start":86,"end":92},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":93,"end":97},{"type":"T_IDENTIFIER","context":"type","value":"Good","line":3,"start":98,"end":102},{"type":"T_COLON","context":"type","value":":","line":3,"start":102,"end":103},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":104,"end":105},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":105,"end":106},{"type":"T_COLON","context":"type","value":":","line":3,"start":106,"end":107},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":108,"end":114},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":114,"end":115},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":116,"end":117},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":118,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":119,"end":120},{"type":"T_COLON","context":"type","value":":","line":3,"start":120,"end":121},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":122,"end":128},{"type":"T_COMMA","context":"type","value":",","line":3,"start":128,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":3,"start":130,"end":131},{"type":"T_COLON","context":"type","value":":","line":3,"start":131,"end":132},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":133,"end":139},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":139,"end":140},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":140,"end":141}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"number [1] is incompatible with string [2]. [incompatible-type]","context":"opaque type Bad: string = number; // Error: number is not a subtype of string","source":"-","start":{"line":2,"column":27,"offset":34},"end":{"line":2,"column":32,"offset":40}}],"operation":null}]}
Generics
Opaque type aliases can also have their own generics,
and they work exactly as generics do in regular type aliases
1
2
3
4
5
6
7
8
9
10
11
12
|
opaque type MyObject<A, B, C>: { foo: A, bar: B } = {
foo: A,
bar: B,
baz: C,
};
var val: MyObject<number, boolean, string> = {
foo: 1,
bar: true,
baz: 'three',
};
|
{"value":"// @flow\nopaque type MyObject<A, B, C>: { foo: A, bar: B } = {\n foo: A,\n bar: B,\n baz: C,\n};\n\nvar val: MyObject<number, boolean, string> = {\n foo: 1,\n bar: true,\n baz: 'three',\n};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":2,"start":9,"end":15},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":16,"end":20},{"type":"T_IDENTIFIER","context":"type","value":"MyObject","line":2,"start":21,"end":29},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":30,"end":31},{"type":"T_COMMA","context":"type","value":",","line":2,"start":31,"end":32},{"type":"T_IDENTIFIER","context":"type","value":"B","line":2,"start":33,"end":34},{"type":"T_COMMA","context":"type","value":",","line":2,"start":34,"end":35},{"type":"T_IDENTIFIER","context":"type","value":"C","line":2,"start":36,"end":37},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":37,"end":38},{"type":"T_COLON","context":"type","value":":","line":2,"start":38,"end":39},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":42,"end":45},{"type":"T_COLON","context":"type","value":":","line":2,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":47,"end":48},{"type":"T_COMMA","context":"type","value":",","line":2,"start":48,"end":49},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":2,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":2,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"type","value":"B","line":2,"start":55,"end":56},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":57,"end":58},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":59,"end":60},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":65,"end":68},{"type":"T_COLON","context":"type","value":":","line":3,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"type","value":"A","line":3,"start":70,"end":71},{"type":"T_COMMA","context":"type","value":",","line":3,"start":71,"end":72},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":4,"start":75,"end":78},{"type":"T_COLON","context":"type","value":":","line":4,"start":78,"end":79},{"type":"T_IDENTIFIER","context":"type","value":"B","line":4,"start":80,"end":81},{"type":"T_COMMA","context":"type","value":",","line":4,"start":81,"end":82},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":5,"start":85,"end":88},{"type":"T_COLON","context":"type","value":":","line":5,"start":88,"end":89},{"type":"T_IDENTIFIER","context":"type","value":"C","line":5,"start":90,"end":91},{"type":"T_COMMA","context":"type","value":",","line":5,"start":91,"end":92},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":93,"end":94},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":94,"end":95},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":97,"end":100},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":8,"start":101,"end":104},{"type":"T_COLON","context":"type","value":":","line":8,"start":104,"end":105},{"type":"T_IDENTIFIER","context":"type","value":"MyObject","line":8,"start":106,"end":114},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":114,"end":115},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":8,"start":115,"end":121},{"type":"T_COMMA","context":"type","value":",","line":8,"start":121,"end":122},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":8,"start":123,"end":130},{"type":"T_COMMA","context":"type","value":",","line":8,"start":130,"end":131},{"type":"T_STRING_TYPE","context":"type","value":"string","line":8,"start":132,"end":138},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":138,"end":139},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":140,"end":141},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":142,"end":143},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":9,"start":146,"end":149},{"type":"T_COLON","context":"normal","value":":","line":9,"start":149,"end":150},{"type":"T_NUMBER","context":"normal","value":"1","line":9,"start":151,"end":152},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":152,"end":153},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":10,"start":156,"end":159},{"type":"T_COLON","context":"normal","value":":","line":10,"start":159,"end":160},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":161,"end":165},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":165,"end":166},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":11,"start":169,"end":172},{"type":"T_COLON","context":"normal","value":":","line":11,"start":172,"end":173},{"type":"T_STRING","context":"normal","value":"'three'","line":11,"start":174,"end":181},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":181,"end":182},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":183,"end":184},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":184,"end":185}],"errors":[]}
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.
1
2
|
declare opaque type Foo;
declare opaque type PositiveNumber: number;
|
{"value":"declare opaque type Foo;\ndeclare opaque type PositiveNumber: number;\n","tokens":[{"type":"T_DECLARE","context":"normal","value":"declare","line":1,"start":0,"end":7},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":1,"start":8,"end":14},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":15,"end":19},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":1,"start":20,"end":23},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":23,"end":24},{"type":"T_DECLARE","context":"normal","value":"declare","line":2,"start":25,"end":32},{"type":"T_OPAQUE","context":"normal","value":"opaque","line":2,"start":33,"end":39},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":40,"end":44},{"type":"T_IDENTIFIER","context":"type","value":"PositiveNumber","line":2,"start":45,"end":59},{"type":"T_COLON","context":"type","value":":","line":2,"start":59,"end":60},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":61,"end":67},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":67,"end":68}],"errors":[]}