Classes in Flow are nominally typed. This means that when you have two separate
classes you cannot use one in place of the other even when they have the same
exact properties and methods.
1
2
3
4
5
6
7
8
9
10
11
|
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
const foo: Foo = new Bar();
|
Cannot assign `new Bar()` to `foo` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]
{"value":"// @flow\nclass Foo {\n serialize() { return '[Foo]'; }\n}\n\nclass Bar {\n serialize() { return '[Bar]'; }\n}\n\n// $ExpectError\nconst foo: Foo = new Bar(); // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":2,"start":15,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":3,"start":23,"end":32},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":32,"end":33},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":33,"end":34},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":35,"end":36},{"type":"T_RETURN","context":"normal","value":"return","line":3,"start":37,"end":43},{"type":"T_STRING","context":"normal","value":"'[Foo]'","line":3,"start":44,"end":51},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":51,"end":52},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":53,"end":54},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":55,"end":56},{"type":"T_CLASS","context":"normal","value":"class","line":6,"start":58,"end":63},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":6,"start":64,"end":67},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":7,"start":72,"end":81},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":81,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":82,"end":83},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":84,"end":85},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":86,"end":92},{"type":"T_STRING","context":"normal","value":"'[Bar]'","line":7,"start":93,"end":100},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":100,"end":101},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":102,"end":103},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":104,"end":105},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":107,"end":122},{"type":"T_CONST","context":"normal","value":"const","line":11,"start":123,"end":128},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":11,"start":129,"end":132},{"type":"T_COLON","context":"type","value":":","line":11,"start":132,"end":133},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":11,"start":134,"end":137},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":138,"end":139},{"type":"T_NEW","context":"normal","value":"new","line":11,"start":140,"end":143},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":11,"start":144,"end":147},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":147,"end":148},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":148,"end":149},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":149,"end":150},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":151,"end":160}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `new Bar()` to `foo` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]","context":"const foo: Foo = new Bar(); // Error!","source":"-","start":{"line":11,"column":18,"offset":140},"end":{"line":11,"column":26,"offset":149}}],"operation":null}]}
Instead, you can use interface
in order to declare the structure of the class
that you are expecting.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
interface Serializable {
serialize(): string;
}
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
const foo: Serializable = new Foo();
const bar: Serializable = new Bar();
|
{"value":"// @flow\ninterface Serializable {\n serialize(): string;\n}\n\nclass Foo {\n serialize() { return '[Foo]'; }\n}\n\nclass Bar {\n serialize() { return '[Bar]'; }\n}\n\nconst foo: Serializable = new Foo(); // Works!\nconst bar: Serializable = new Bar(); // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_INTERFACE","context":"normal","value":"interface","line":2,"start":9,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"Serializable","line":2,"start":19,"end":31},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":3,"start":36,"end":45},{"type":"T_LPAREN","context":"type","value":"(","line":3,"start":45,"end":46},{"type":"T_RPAREN","context":"type","value":")","line":3,"start":46,"end":47},{"type":"T_COLON","context":"type","value":":","line":3,"start":47,"end":48},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":49,"end":55},{"type":"T_SEMICOLON","context":"type","value":";","line":3,"start":55,"end":56},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":57,"end":58},{"type":"T_CLASS","context":"normal","value":"class","line":6,"start":60,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":6,"start":66,"end":69},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":70,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":7,"start":74,"end":83},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":83,"end":84},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":84,"end":85},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":86,"end":87},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":88,"end":94},{"type":"T_STRING","context":"normal","value":"'[Foo]'","line":7,"start":95,"end":102},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":102,"end":103},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":104,"end":105},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":106,"end":107},{"type":"T_CLASS","context":"normal","value":"class","line":10,"start":109,"end":114},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":10,"start":115,"end":118},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":119,"end":120},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":11,"start":123,"end":132},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":132,"end":133},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":133,"end":134},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":135,"end":136},{"type":"T_RETURN","context":"normal","value":"return","line":11,"start":137,"end":143},{"type":"T_STRING","context":"normal","value":"'[Bar]'","line":11,"start":144,"end":151},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":151,"end":152},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":153,"end":154},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":155,"end":156},{"type":"T_CONST","context":"normal","value":"const","line":14,"start":158,"end":163},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":14,"start":164,"end":167},{"type":"T_COLON","context":"type","value":":","line":14,"start":167,"end":168},{"type":"T_IDENTIFIER","context":"type","value":"Serializable","line":14,"start":169,"end":181},{"type":"T_ASSIGN","context":"normal","value":"=","line":14,"start":182,"end":183},{"type":"T_NEW","context":"normal","value":"new","line":14,"start":184,"end":187},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":14,"start":188,"end":191},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":191,"end":192},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":192,"end":193},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":193,"end":194},{"type":"Line","context":"comment","value":"// Works!","line":14,"start":195,"end":204},{"type":"T_CONST","context":"normal","value":"const","line":15,"start":205,"end":210},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":15,"start":211,"end":214},{"type":"T_COLON","context":"type","value":":","line":15,"start":214,"end":215},{"type":"T_IDENTIFIER","context":"type","value":"Serializable","line":15,"start":216,"end":228},{"type":"T_ASSIGN","context":"normal","value":"=","line":15,"start":229,"end":230},{"type":"T_NEW","context":"normal","value":"new","line":15,"start":231,"end":234},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":15,"start":235,"end":238},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":238,"end":239},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":239,"end":240},{"type":"T_SEMICOLON","context":"normal","value":";","line":15,"start":240,"end":241},{"type":"Line","context":"comment","value":"// Works!","line":15,"start":242,"end":251}],"errors":[]}
You can also use implements
to tell Flow that you want the class to match an
interface. This prevents you from making incompatible changes when editing the
class.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
interface Serializable {
serialize(): string;
}
class Foo implements Serializable {
serialize() { return '[Foo]'; }
}
class Bar implements Serializable {
serialize() { return 42; }
}
|
Cannot implement `Serializable` [1] with `Bar` because number [2] is incompatible with string [3] in the return value of property `serialize`. [incompatible-type]
{"value":"// @flow\ninterface Serializable {\n serialize(): string;\n}\n\nclass Foo implements Serializable {\n serialize() { return '[Foo]'; } // Works!\n}\n\nclass Bar implements Serializable {\n // $ExpectError\n serialize() { return 42; } // Error!\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_INTERFACE","context":"normal","value":"interface","line":2,"start":9,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"Serializable","line":2,"start":19,"end":31},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":3,"start":36,"end":45},{"type":"T_LPAREN","context":"type","value":"(","line":3,"start":45,"end":46},{"type":"T_RPAREN","context":"type","value":")","line":3,"start":46,"end":47},{"type":"T_COLON","context":"type","value":":","line":3,"start":47,"end":48},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":49,"end":55},{"type":"T_SEMICOLON","context":"type","value":";","line":3,"start":55,"end":56},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":57,"end":58},{"type":"T_CLASS","context":"normal","value":"class","line":6,"start":60,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":6,"start":66,"end":69},{"type":"T_IMPLEMENTS","context":"normal","value":"implements","line":6,"start":70,"end":80},{"type":"T_IDENTIFIER","context":"type","value":"Serializable","line":6,"start":81,"end":93},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":94,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":7,"start":98,"end":107},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":107,"end":108},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":108,"end":109},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":110,"end":111},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":112,"end":118},{"type":"T_STRING","context":"normal","value":"'[Foo]'","line":7,"start":119,"end":126},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":126,"end":127},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":128,"end":129},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":130,"end":139},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":140,"end":141},{"type":"T_CLASS","context":"normal","value":"class","line":10,"start":143,"end":148},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":10,"start":149,"end":152},{"type":"T_IMPLEMENTS","context":"normal","value":"implements","line":10,"start":153,"end":163},{"type":"T_IDENTIFIER","context":"type","value":"Serializable","line":10,"start":164,"end":176},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":177,"end":178},{"type":"Line","context":"comment","value":"// $ExpectError","line":11,"start":181,"end":196},{"type":"T_IDENTIFIER","context":"normal","value":"serialize","line":12,"start":199,"end":208},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":208,"end":209},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":209,"end":210},{"type":"T_LCURLY","context":"normal","value":"{","line":12,"start":211,"end":212},{"type":"T_RETURN","context":"normal","value":"return","line":12,"start":213,"end":219},{"type":"T_NUMBER","context":"normal","value":"42","line":12,"start":220,"end":222},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":222,"end":223},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":224,"end":225},{"type":"Line","context":"comment","value":"// Error!","line":12,"start":226,"end":235},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":236,"end":237}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot implement `Serializable` [1] with `Bar` because number [2] is incompatible with string [3] in the return value of property `serialize`. [incompatible-type]","context":" serialize() { return 42; } // Error!","source":"-","start":{"line":12,"column":24,"offset":220},"end":{"line":12,"column":25,"offset":222}}],"operation":null}]}
You can also use implements
with multiple interfaces.
1
2
3
|
class Foo implements Bar, Baz {
}
|
{"value":"class Foo implements Bar, Baz {\n // ...\n}\n","tokens":[{"type":"T_CLASS","context":"normal","value":"class","line":1,"start":0,"end":5},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":1,"start":6,"end":9},{"type":"T_IMPLEMENTS","context":"normal","value":"implements","line":1,"start":10,"end":20},{"type":"T_IDENTIFIER","context":"type","value":"Bar","line":1,"start":21,"end":24},{"type":"T_COMMA","context":"normal","value":",","line":1,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"type","value":"Baz","line":1,"start":26,"end":29},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":30,"end":31},{"type":"Line","context":"comment","value":"// ...","line":2,"start":34,"end":40},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":41,"end":42}],"errors":[]}
Interface Syntax
Interfaces are created using the keyword interface
followed by its name and
a block which contains the body of the type definition.
1
2
3
|
interface MyInterface {
}
|
{"value":"interface MyInterface {\n // ...\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"Line","context":"comment","value":"// ...","line":2,"start":26,"end":32},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":33,"end":34}],"errors":[]}
The syntax of the block matches the syntax of object types and has all of the
same features.
Interface Methods
You can add methods to interfaces following the same syntax as object methods.
1
2
3
|
interface MyInterface {
method(value: string): number;
}
|
{"value":"interface MyInterface {\n method(value: string): number;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":2,"start":26,"end":32},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":33,"end":38},{"type":"T_COLON","context":"type","value":":","line":2,"start":38,"end":39},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":40,"end":46},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":46,"end":47},{"type":"T_COLON","context":"type","value":":","line":2,"start":47,"end":48},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":49,"end":55},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":55,"end":56},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":57,"end":58}],"errors":[]}
Interface Properties
You can add properties to interfaces following the same syntax as object
properties.
1
2
3
|
interface MyInterface {
property: string;
}
|
{"value":"interface MyInterface {\n property: string;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":2,"start":26,"end":34},{"type":"T_COLON","context":"type","value":":","line":2,"start":34,"end":35},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":36,"end":42},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":42,"end":43},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":44,"end":45}],"errors":[]}
Interface properties can be optional as well.
1
2
3
|
interface MyInterface {
property?: string;
}
|
{"value":"interface MyInterface {\n property?: string;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":2,"start":26,"end":34},{"type":"T_PLING","context":"type","value":"?","line":2,"start":34,"end":35},{"type":"T_COLON","context":"type","value":":","line":2,"start":35,"end":36},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":37,"end":43},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":43,"end":44},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":45,"end":46}],"errors":[]}
Interfaces as maps
You can create “indexer properties” the same
way as with objects.
1
2
3
|
interface MyInterface {
[key: string]: number;
}
|
{"value":"interface MyInterface {\n [key: string]: number;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"type","value":"key","line":2,"start":27,"end":30},{"type":"T_COLON","context":"type","value":":","line":2,"start":30,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":32,"end":38},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":38,"end":39},{"type":"T_COLON","context":"type","value":":","line":2,"start":39,"end":40},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":41,"end":47},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":47,"end":48},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":49,"end":50}],"errors":[]}
Interface Generics
Interfaces can also have their own generics.
1
2
3
4
|
interface MyInterface<A, B, C> {
property: A;
method(val: B): C;
}
|
{"value":"interface MyInterface<A, B, C> {\n property: A;\n method(val: B): C;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"type","value":"A","line":1,"start":22,"end":23},{"type":"T_COMMA","context":"type","value":",","line":1,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"type","value":"B","line":1,"start":25,"end":26},{"type":"T_COMMA","context":"type","value":",","line":1,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"type","value":"C","line":1,"start":28,"end":29},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":29,"end":30},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":31,"end":32},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":2,"start":35,"end":43},{"type":"T_COLON","context":"type","value":":","line":2,"start":43,"end":44},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":45,"end":46},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":3,"start":50,"end":56},{"type":"T_LPAREN","context":"type","value":"(","line":3,"start":56,"end":57},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":3,"start":57,"end":60},{"type":"T_COLON","context":"type","value":":","line":3,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"type","value":"B","line":3,"start":62,"end":63},{"type":"T_RPAREN","context":"type","value":")","line":3,"start":63,"end":64},{"type":"T_COLON","context":"type","value":":","line":3,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"type","value":"C","line":3,"start":66,"end":67},{"type":"T_SEMICOLON","context":"type","value":";","line":3,"start":67,"end":68},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":69,"end":70}],"errors":[]}
Interface generics are parameterized.
When you use an interface you need to pass parameters for each of its generics.
1
2
3
4
5
6
7
8
9
10
11
12
|
interface MyInterface<A, B, C> {
foo: A;
bar: B;
baz: C;
}
var val: MyInterface<number, boolean, string> = {
foo: 1,
bar: true,
baz: 'three',
};
|
{"value":"// @flow\ninterface MyInterface<A, B, C> {\n foo: A;\n bar: B;\n baz: C;\n}\n\nvar val: MyInterface<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_INTERFACE","context":"normal","value":"interface","line":2,"start":9,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":2,"start":19,"end":30},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":30,"end":31},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":31,"end":32},{"type":"T_COMMA","context":"type","value":",","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"type","value":"B","line":2,"start":34,"end":35},{"type":"T_COMMA","context":"type","value":",","line":2,"start":35,"end":36},{"type":"T_IDENTIFIER","context":"type","value":"C","line":2,"start":37,"end":38},{"type":"T_GREATER_THAN","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":3,"start":44,"end":47},{"type":"T_COLON","context":"type","value":":","line":3,"start":47,"end":48},{"type":"T_IDENTIFIER","context":"type","value":"A","line":3,"start":49,"end":50},{"type":"T_SEMICOLON","context":"type","value":";","line":3,"start":50,"end":51},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":4,"start":54,"end":57},{"type":"T_COLON","context":"type","value":":","line":4,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"type","value":"B","line":4,"start":59,"end":60},{"type":"T_SEMICOLON","context":"type","value":";","line":4,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":5,"start":64,"end":67},{"type":"T_COLON","context":"type","value":":","line":5,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"type","value":"C","line":5,"start":69,"end":70},{"type":"T_SEMICOLON","context":"type","value":";","line":5,"start":70,"end":71},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":72,"end":73},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":75,"end":78},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":8,"start":79,"end":82},{"type":"T_COLON","context":"type","value":":","line":8,"start":82,"end":83},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":8,"start":84,"end":95},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":95,"end":96},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":8,"start":96,"end":102},{"type":"T_COMMA","context":"type","value":",","line":8,"start":102,"end":103},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":8,"start":104,"end":111},{"type":"T_COMMA","context":"type","value":",","line":8,"start":111,"end":112},{"type":"T_STRING_TYPE","context":"type","value":"string","line":8,"start":113,"end":119},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":119,"end":120},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":121,"end":122},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":123,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":9,"start":127,"end":130},{"type":"T_COLON","context":"normal","value":":","line":9,"start":130,"end":131},{"type":"T_NUMBER","context":"normal","value":"1","line":9,"start":132,"end":133},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":133,"end":134},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":10,"start":137,"end":140},{"type":"T_COLON","context":"normal","value":":","line":10,"start":140,"end":141},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":142,"end":146},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":146,"end":147},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":11,"start":150,"end":153},{"type":"T_COLON","context":"normal","value":":","line":11,"start":153,"end":154},{"type":"T_STRING","context":"normal","value":"'three'","line":11,"start":155,"end":162},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":162,"end":163},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":164,"end":165},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":165,"end":166}],"errors":[]}
Interface property variance (read-only and write-only)
Interface properties are invariant by default. But you
can add modifiers to make them covariant (read-only) or contravariant
(write-only).
1
2
3
4
|
interface MyInterface {
+covariant: number;
-contravariant: number;
}
|
{"value":"interface MyInterface {\n +covariant: number; // read-only\n -contravariant: number; // write-only\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"T_PLUS","context":"type","value":"+","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"covariant","line":2,"start":27,"end":36},{"type":"T_COLON","context":"type","value":":","line":2,"start":36,"end":37},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":38,"end":44},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":44,"end":45},{"type":"Line","context":"comment","value":"// read-only","line":2,"start":50,"end":62},{"type":"T_MINUS","context":"type","value":"-","line":3,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"contravariant","line":3,"start":66,"end":79},{"type":"T_COLON","context":"type","value":":","line":3,"start":79,"end":80},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":81,"end":87},{"type":"T_SEMICOLON","context":"type","value":";","line":3,"start":87,"end":88},{"type":"Line","context":"comment","value":"// write-only","line":3,"start":89,"end":102},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":103,"end":104}],"errors":[]}
Covariant (read-only) properties on interfaces
You can make a property covariant by adding a plus symbol +
in front of the
property name.
1
2
3
|
interface MyInterface {
+readOnly: number | string;
}
|
{"value":"interface MyInterface {\n +readOnly: number | string;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"MyInterface","line":1,"start":10,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":22,"end":23},{"type":"T_PLUS","context":"type","value":"+","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"readOnly","line":2,"start":27,"end":35},{"type":"T_COLON","context":"type","value":":","line":2,"start":35,"end":36},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":37,"end":43},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":44,"end":45},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":46,"end":52},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":52,"end":53},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":54,"end":55}],"errors":[]}
This allows you to pass a more specific type in place of that property.
1
2
3
4
5
6
7
|
interface Invariant { property: number | string }
interface Covariant { +readOnly: number | string }
var value1: Invariant = { property: 42 };
var value2: Covariant = { readOnly: 42 };
|
{"value":"// @flow\n// $ExpectError\ninterface Invariant { property: number | string }\ninterface Covariant { +readOnly: number | string }\n\nvar value1: Invariant = { property: 42 }; // Error!\nvar value2: Covariant = { readOnly: 42 }; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// $ExpectError","line":2,"start":9,"end":24},{"type":"T_INTERFACE","context":"normal","value":"interface","line":3,"start":25,"end":34},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":3,"start":35,"end":44},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":3,"start":48,"end":56},{"type":"T_COLON","context":"type","value":":","line":3,"start":56,"end":57},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":58,"end":64},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":65,"end":66},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":67,"end":73},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":74,"end":75},{"type":"T_INTERFACE","context":"normal","value":"interface","line":4,"start":76,"end":85},{"type":"T_IDENTIFIER","context":"type","value":"Covariant","line":4,"start":86,"end":95},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":96,"end":97},{"type":"T_PLUS","context":"type","value":"+","line":4,"start":98,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"readOnly","line":4,"start":99,"end":107},{"type":"T_COLON","context":"type","value":":","line":4,"start":107,"end":108},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":109,"end":115},{"type":"T_BIT_OR","context":"type","value":"|","line":4,"start":116,"end":117},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":118,"end":124},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":125,"end":126},{"type":"T_VAR","context":"normal","value":"var","line":6,"start":128,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"value1","line":6,"start":132,"end":138},{"type":"T_COLON","context":"type","value":":","line":6,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":6,"start":140,"end":149},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":150,"end":151},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":152,"end":153},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":6,"start":154,"end":162},{"type":"T_COLON","context":"normal","value":":","line":6,"start":162,"end":163},{"type":"T_NUMBER","context":"normal","value":"42","line":6,"start":164,"end":166},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":167,"end":168},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":168,"end":169},{"type":"Line","context":"comment","value":"// Error!","line":6,"start":170,"end":179},{"type":"T_VAR","context":"normal","value":"var","line":7,"start":180,"end":183},{"type":"T_IDENTIFIER","context":"normal","value":"value2","line":7,"start":184,"end":190},{"type":"T_COLON","context":"type","value":":","line":7,"start":190,"end":191},{"type":"T_IDENTIFIER","context":"type","value":"Covariant","line":7,"start":192,"end":201},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":202,"end":203},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":204,"end":205},{"type":"T_IDENTIFIER","context":"normal","value":"readOnly","line":7,"start":206,"end":214},{"type":"T_COLON","context":"normal","value":":","line":7,"start":214,"end":215},{"type":"T_NUMBER","context":"normal","value":"42","line":7,"start":216,"end":218},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":219,"end":220},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":220,"end":221},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":222,"end":231}],"errors":[]}
Because of how covariance works, covariant properties also become read-only
when used. Which can be useful over normal properties.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
interface Invariant { property: number | string }
interface Covariant { +readOnly: number | string }
function method1(value: Invariant) {
value.property;
value.property = 3.14;
}
function method2(value: Covariant) {
value.readOnly;
value.readOnly = 3.14;
}
|
Cannot assign `3.14` to `value.readOnly` because property `readOnly` is not writable. [cannot-write]
{"value":"// @flow\ninterface Invariant { property: number | string }\ninterface Covariant { +readOnly: number | string }\n\nfunction method1(value: Invariant) {\n value.property; // Works!\n value.property = 3.14; // Works!\n}\n\nfunction method2(value: Covariant) {\n value.readOnly; // Works!\n // $ExpectError\n value.readOnly = 3.14; // Error!\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_INTERFACE","context":"normal","value":"interface","line":2,"start":9,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":2,"start":19,"end":28},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":2,"start":32,"end":40},{"type":"T_COLON","context":"type","value":":","line":2,"start":40,"end":41},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":42,"end":48},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":49,"end":50},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":51,"end":57},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":58,"end":59},{"type":"T_INTERFACE","context":"normal","value":"interface","line":3,"start":60,"end":69},{"type":"T_IDENTIFIER","context":"type","value":"Covariant","line":3,"start":70,"end":79},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":80,"end":81},{"type":"T_PLUS","context":"type","value":"+","line":3,"start":82,"end":83},{"type":"T_IDENTIFIER","context":"normal","value":"readOnly","line":3,"start":83,"end":91},{"type":"T_COLON","context":"type","value":":","line":3,"start":91,"end":92},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":93,"end":99},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":100,"end":101},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":102,"end":108},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":109,"end":110},{"type":"T_FUNCTION","context":"normal","value":"function","line":5,"start":112,"end":120},{"type":"T_IDENTIFIER","context":"normal","value":"method1","line":5,"start":121,"end":128},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":128,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":129,"end":134},{"type":"T_COLON","context":"type","value":":","line":5,"start":134,"end":135},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":5,"start":136,"end":145},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":145,"end":146},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":147,"end":148},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":151,"end":156},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":6,"start":157,"end":165},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":165,"end":166},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":174,"end":183},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":7,"start":186,"end":191},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":7,"start":192,"end":200},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":201,"end":202},{"type":"T_NUMBER","context":"normal","value":"3.14","line":7,"start":203,"end":207},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":207,"end":208},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":209,"end":218},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":219,"end":220},{"type":"T_FUNCTION","context":"normal","value":"function","line":10,"start":222,"end":230},{"type":"T_IDENTIFIER","context":"normal","value":"method2","line":10,"start":231,"end":238},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":238,"end":239},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":10,"start":239,"end":244},{"type":"T_COLON","context":"type","value":":","line":10,"start":244,"end":245},{"type":"T_IDENTIFIER","context":"type","value":"Covariant","line":10,"start":246,"end":255},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":255,"end":256},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":257,"end":258},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":11,"start":261,"end":266},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":266,"end":267},{"type":"T_IDENTIFIER","context":"normal","value":"readOnly","line":11,"start":267,"end":275},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":275,"end":276},{"type":"Line","context":"comment","value":"// Works!","line":11,"start":284,"end":293},{"type":"Line","context":"comment","value":"// $ExpectError","line":12,"start":296,"end":311},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":13,"start":314,"end":319},{"type":"T_PERIOD","context":"normal","value":".","line":13,"start":319,"end":320},{"type":"T_IDENTIFIER","context":"normal","value":"readOnly","line":13,"start":320,"end":328},{"type":"T_ASSIGN","context":"normal","value":"=","line":13,"start":329,"end":330},{"type":"T_NUMBER","context":"normal","value":"3.14","line":13,"start":331,"end":335},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":335,"end":336},{"type":"Line","context":"comment","value":"// Error!","line":13,"start":337,"end":346},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":347,"end":348}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `3.14` to `value.readOnly` because property `readOnly` is not writable. [cannot-write]","context":" value.readOnly = 3.14; // Error!","source":"-","start":{"line":13,"column":9,"offset":320},"end":{"line":13,"column":16,"offset":328}}],"operation":null}]}
Contravariant (write-only) properties on interfaces
You can make a property contravariant by adding a minus symbol - in front of
the property name.
1
2
3
|
interface InterfaceName {
-writeOnly: number;
}
|
{"value":"interface InterfaceName {\n -writeOnly: number;\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"InterfaceName","line":1,"start":10,"end":23},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":24,"end":25},{"type":"T_MINUS","context":"type","value":"-","line":2,"start":28,"end":29},{"type":"T_IDENTIFIER","context":"normal","value":"writeOnly","line":2,"start":29,"end":38},{"type":"T_COLON","context":"type","value":":","line":2,"start":38,"end":39},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":40,"end":46},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":46,"end":47},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":48,"end":49}],"errors":[]}
This allows you to pass a less specific type in place of that property.
1
2
3
4
5
6
7
8
9
|
interface Invariant { property: number }
interface Contravariant { -writeOnly: number }
var numberOrString = Math.random() > 0.5 ? 42 : 'forty-two';
var value1: Invariant = { property: numberOrString };
var value2: Contravariant = { writeOnly: numberOrString };
|
Cannot assign object literal to `value1` because string [1] is incompatible with number [2] in property `property`. [incompatible-type]
{"value":"// @flow\ninterface Invariant { property: number }\ninterface Contravariant { -writeOnly: number }\n\nvar numberOrString = Math.random() > 0.5 ? 42 : 'forty-two';\n\n// $ExpectError\nvar value1: Invariant = { property: numberOrString }; // Error!\nvar value2: Contravariant = { writeOnly: numberOrString }; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_INTERFACE","context":"normal","value":"interface","line":2,"start":9,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":2,"start":19,"end":28},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":33,"end":34},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":2,"start":36,"end":44},{"type":"T_COLON","context":"type","value":":","line":2,"start":44,"end":45},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":46,"end":52},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":53,"end":54},{"type":"T_INTERFACE","context":"normal","value":"interface","line":3,"start":55,"end":64},{"type":"T_IDENTIFIER","context":"type","value":"Contravariant","line":3,"start":65,"end":78},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":79,"end":80},{"type":"T_MINUS","context":"type","value":"-","line":3,"start":81,"end":82},{"type":"T_IDENTIFIER","context":"normal","value":"writeOnly","line":3,"start":82,"end":91},{"type":"T_COLON","context":"type","value":":","line":3,"start":91,"end":92},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":93,"end":99},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":100,"end":101},{"type":"T_VAR","context":"normal","value":"var","line":5,"start":103,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"numberOrString","line":5,"start":107,"end":121},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":122,"end":123},{"type":"T_IDENTIFIER","context":"normal","value":"Math","line":5,"start":124,"end":128},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":128,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"random","line":5,"start":129,"end":135},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":135,"end":136},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":136,"end":137},{"type":"T_GREATER_THAN","context":"normal","value":">","line":5,"start":138,"end":139},{"type":"T_NUMBER","context":"normal","value":"0.5","line":5,"start":140,"end":143},{"type":"T_PLING","context":"normal","value":"?","line":5,"start":144,"end":145},{"type":"T_NUMBER","context":"normal","value":"42","line":5,"start":146,"end":148},{"type":"T_COLON","context":"normal","value":":","line":5,"start":149,"end":150},{"type":"T_STRING","context":"normal","value":"'forty-two'","line":5,"start":151,"end":162},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":162,"end":163},{"type":"Line","context":"comment","value":"// $ExpectError","line":7,"start":165,"end":180},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":181,"end":184},{"type":"T_IDENTIFIER","context":"normal","value":"value1","line":8,"start":185,"end":191},{"type":"T_COLON","context":"type","value":":","line":8,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":8,"start":193,"end":202},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":207,"end":208},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":209,"end":210},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":8,"start":211,"end":219},{"type":"T_COLON","context":"normal","value":":","line":8,"start":219,"end":220},{"type":"T_IDENTIFIER","context":"normal","value":"numberOrString","line":8,"start":221,"end":235},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":236,"end":237},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":237,"end":238},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":240,"end":249},{"type":"T_VAR","context":"normal","value":"var","line":9,"start":250,"end":253},{"type":"T_IDENTIFIER","context":"normal","value":"value2","line":9,"start":254,"end":260},{"type":"T_COLON","context":"type","value":":","line":9,"start":260,"end":261},{"type":"T_IDENTIFIER","context":"type","value":"Contravariant","line":9,"start":262,"end":275},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":276,"end":277},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":278,"end":279},{"type":"T_IDENTIFIER","context":"normal","value":"writeOnly","line":9,"start":280,"end":289},{"type":"T_COLON","context":"normal","value":":","line":9,"start":289,"end":290},{"type":"T_IDENTIFIER","context":"normal","value":"numberOrString","line":9,"start":291,"end":305},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":306,"end":307},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":307,"end":308},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":309,"end":318}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign object literal to `value1` because string [1] is incompatible with number [2] in property `property`. [incompatible-type]","context":"var value1: Invariant = { property: numberOrString }; // Error!","source":"-","start":{"line":8,"column":41,"offset":221},"end":{"line":8,"column":54,"offset":235}}],"operation":null}]}
Because of how contravariance works, contravariant properties also become
write-only when used. Which can be useful over normal properties.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
interface Invariant { property: number }
interface Contravariant { -writeOnly: number }
function method1(value: Invariant) {
value.property;
value.property = 3.14;
}
function method2(value: Contravariant) {
value.writeOnly;
value.writeOnly = 3.14;
}
|
{"value":"interface Invariant { property: number }\ninterface Contravariant { -writeOnly: number }\n\nfunction method1(value: Invariant) {\n value.property; // Works!\n value.property = 3.14; // Works!\n}\n\nfunction method2(value: Contravariant) {\n // $ExpectError\n value.writeOnly; // Error!\n value.writeOnly = 3.14; // Works!\n}\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":1,"start":10,"end":19},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":1,"start":28,"end":36},{"type":"T_COLON","context":"type","value":":","line":1,"start":36,"end":37},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":38,"end":44},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":45,"end":46},{"type":"T_INTERFACE","context":"normal","value":"interface","line":2,"start":47,"end":56},{"type":"T_IDENTIFIER","context":"type","value":"Contravariant","line":2,"start":57,"end":70},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":71,"end":72},{"type":"T_MINUS","context":"type","value":"-","line":2,"start":73,"end":74},{"type":"T_IDENTIFIER","context":"normal","value":"writeOnly","line":2,"start":74,"end":83},{"type":"T_COLON","context":"type","value":":","line":2,"start":83,"end":84},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":85,"end":91},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":92,"end":93},{"type":"T_FUNCTION","context":"normal","value":"function","line":4,"start":95,"end":103},{"type":"T_IDENTIFIER","context":"normal","value":"method1","line":4,"start":104,"end":111},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":111,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":112,"end":117},{"type":"T_COLON","context":"type","value":":","line":4,"start":117,"end":118},{"type":"T_IDENTIFIER","context":"type","value":"Invariant","line":4,"start":119,"end":128},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":128,"end":129},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":130,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":134,"end":139},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":139,"end":140},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":5,"start":140,"end":148},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":148,"end":149},{"type":"Line","context":"comment","value":"// Works!","line":5,"start":157,"end":166},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":169,"end":174},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":174,"end":175},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":6,"start":175,"end":183},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":184,"end":185},{"type":"T_NUMBER","context":"normal","value":"3.14","line":6,"start":186,"end":190},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":190,"end":191},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":192,"end":201},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":202,"end":203},{"type":"T_FUNCTION","context":"normal","value":"function","line":9,"start":205,"end":213},{"type":"T_IDENTIFIER","context":"normal","value":"method2","line":9,"start":214,"end":221},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":221,"end":222},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":222,"end":227},{"type":"T_COLON","context":"type","value":":","line":9,"start":227,"end":228},{"type":"T_IDENTIFIER","context":"type","value":"Contravariant","line":9,"start":229,"end":242},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":242,"end":243},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":244,"end":245},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":248,"end":263},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":11,"start":266,"end":271},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":271,"end":272},{"type":"T_IDENTIFIER","context":"normal","value":"writeOnly","line":11,"start":272,"end":281},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":281,"end":282},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":290,"end":299},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":12,"start":302,"end":307},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":307,"end":308},{"type":"T_IDENTIFIER","context":"normal","value":"writeOnly","line":12,"start":308,"end":317},{"type":"T_ASSIGN","context":"normal","value":"=","line":12,"start":318,"end":319},{"type":"T_NUMBER","context":"normal","value":"3.14","line":12,"start":320,"end":324},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":324,"end":325},{"type":"Line","context":"comment","value":"// Works!","line":12,"start":326,"end":335},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":336,"end":337}],"errors":[]}