An important attribute of every type system is whether they are structural or
nominal, they can even be mixed within a single type system. So it’s important
to know the difference.
A type is something like a string, a boolean, an object, or a class. They have
names and they have structures. Primitives like strings or booleans have a very
simple structure and only go by one name.
More complex types like object or classes have more complex structures. They
each get their own name even if they sometimes have the same structure overall.
A static type checker uses either the names or the structure of the types in
order to compare them against other types. Checking against the name is nominal
typing and checking against the structure is structural typing.
Nominal typing
Languages like C++, Java, and Swift have primarily nominal type systems.
1
2
3
4
|
class Foo { method(input: string) { } }
class Bar { method(input: string) { } }
let foo: Foo = new Bar();
|
{"value":"class Foo { method(input: string) { /* ... */ } }\nclass Bar { method(input: string) { /* ... */ } }\n\nlet foo: Foo = new Bar(); // Error!\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_LCURLY","context":"normal","value":"{","line":1,"start":10,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":1,"start":12,"end":18},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":1,"start":19,"end":24},{"type":"T_COLON","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_RPAREN","context":"normal","value":")","line":1,"start":32,"end":33},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":34,"end":35},{"type":"Block","context":"comment","value":"/* ... */","line":1,"start":36,"end":45},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":46,"end":47},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":48,"end":49},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":50,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":2,"start":56,"end":59},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":2,"start":62,"end":68},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":2,"start":69,"end":74},{"type":"T_COLON","context":"type","value":":","line":2,"start":74,"end":75},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":76,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":82,"end":83},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":84,"end":85},{"type":"Block","context":"comment","value":"/* ... */","line":2,"start":86,"end":95},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":96,"end":97},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":98,"end":99},{"type":"T_LET","context":"normal","value":"let","line":4,"start":101,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":105,"end":108},{"type":"T_COLON","context":"type","value":":","line":4,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":4,"start":110,"end":113},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":114,"end":115},{"type":"T_NEW","context":"normal","value":"new","line":4,"start":116,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":4,"start":120,"end":123},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":123,"end":124},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":124,"end":125},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":125,"end":126},{"type":"Line","context":"comment","value":"// Error!","line":4,"start":127,"end":136}],"errors":[]}
Here you can see a pseudo-example of a nominal type system erroring out when
you’re trying to put a Bar
where a Foo
is required because they have
different names.
Structural typing
Languages like OCaml, Haskell, and Elm have primarily structural type systems.
1
2
3
4
|
class Foo { method(input: string) { } }
class Bar { method(input: string) { } }
let foo: Foo = new Bar();
|
{"value":"class Foo { method(input: string) { /* ... */ } }\nclass Bar { method(input: string) { /* ... */ } }\n\nlet foo: Foo = new Bar(); // Works!\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_LCURLY","context":"normal","value":"{","line":1,"start":10,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":1,"start":12,"end":18},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":1,"start":19,"end":24},{"type":"T_COLON","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_RPAREN","context":"normal","value":")","line":1,"start":32,"end":33},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":34,"end":35},{"type":"Block","context":"comment","value":"/* ... */","line":1,"start":36,"end":45},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":46,"end":47},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":48,"end":49},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":50,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":2,"start":56,"end":59},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":2,"start":62,"end":68},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":2,"start":69,"end":74},{"type":"T_COLON","context":"type","value":":","line":2,"start":74,"end":75},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":76,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":82,"end":83},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":84,"end":85},{"type":"Block","context":"comment","value":"/* ... */","line":2,"start":86,"end":95},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":96,"end":97},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":98,"end":99},{"type":"T_LET","context":"normal","value":"let","line":4,"start":101,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":105,"end":108},{"type":"T_COLON","context":"type","value":":","line":4,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":4,"start":110,"end":113},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":114,"end":115},{"type":"T_NEW","context":"normal","value":"new","line":4,"start":116,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":4,"start":120,"end":123},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":123,"end":124},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":124,"end":125},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":125,"end":126},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":127,"end":136}],"errors":[]}
Here you can see a pseudo-example of a structural type system passing when
you’re trying to put a Bar where a Foo
is required because their structure is
exactly the same.
But as soon as you change the shape it will start to cause errors.
1
2
3
4
|
class Foo { method(input: string) { } }
class Bar { method(input: number) { } }
let foo: Foo = new Bar();
|
{"value":"class Foo { method(input: string) { /* ... */ } }\nclass Bar { method(input: number) { /* ... */ } }\n\nlet foo: Foo = new Bar(); // Error!\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_LCURLY","context":"normal","value":"{","line":1,"start":10,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":1,"start":12,"end":18},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":1,"start":19,"end":24},{"type":"T_COLON","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_RPAREN","context":"normal","value":")","line":1,"start":32,"end":33},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":34,"end":35},{"type":"Block","context":"comment","value":"/* ... */","line":1,"start":36,"end":45},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":46,"end":47},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":48,"end":49},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":50,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":2,"start":56,"end":59},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":2,"start":62,"end":68},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":2,"start":69,"end":74},{"type":"T_COLON","context":"type","value":":","line":2,"start":74,"end":75},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":76,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":82,"end":83},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":84,"end":85},{"type":"Block","context":"comment","value":"/* ... */","line":2,"start":86,"end":95},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":96,"end":97},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":98,"end":99},{"type":"T_LET","context":"normal","value":"let","line":4,"start":101,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":105,"end":108},{"type":"T_COLON","context":"type","value":":","line":4,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":4,"start":110,"end":113},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":114,"end":115},{"type":"T_NEW","context":"normal","value":"new","line":4,"start":116,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":4,"start":120,"end":123},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":123,"end":124},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":124,"end":125},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":125,"end":126},{"type":"Line","context":"comment","value":"// Error!","line":4,"start":127,"end":136}],"errors":[]}
It can get a little bit more complicated than this.
We’ve demonstrated both nominal and structure typing of classes, but there are
also other complex types like objects and functions which can also be either
nominal or structural. Even further, they can be different within the same type
system (most of the languages listed before has features of both).
For example, Flow uses structural typing for objects and functions, but nominal
typing for classes.
Functions are structurally typed
When comparing a function type with a function it must have the same structure
in order to be considered valid.
1
2
3
4
|
type FuncType = (input: string) => void;
function func(input: string) { }
let test: FuncType = func;
|
{"value":"// @flow\ntype FuncType = (input: string) => void;\nfunction func(input: string) { /* ... */ }\nlet test: FuncType = func; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"FuncType","line":2,"start":14,"end":22},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":23,"end":24},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":2,"start":26,"end":31},{"type":"T_COLON","context":"type","value":":","line":2,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":33,"end":39},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":39,"end":40},{"type":"T_ARROW","context":"type","value":"=>","line":2,"start":41,"end":43},{"type":"T_VOID_TYPE","context":"type","value":"void","line":2,"start":44,"end":48},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":48,"end":49},{"type":"T_FUNCTION","context":"normal","value":"function","line":3,"start":50,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"func","line":3,"start":59,"end":63},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":63,"end":64},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":3,"start":64,"end":69},{"type":"T_COLON","context":"type","value":":","line":3,"start":69,"end":70},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":71,"end":77},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":77,"end":78},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":79,"end":80},{"type":"Block","context":"comment","value":"/* ... */","line":3,"start":81,"end":90},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":91,"end":92},{"type":"T_LET","context":"normal","value":"let","line":4,"start":93,"end":96},{"type":"T_IDENTIFIER","context":"normal","value":"test","line":4,"start":97,"end":101},{"type":"T_COLON","context":"type","value":":","line":4,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"type","value":"FuncType","line":4,"start":103,"end":111},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":112,"end":113},{"type":"T_IDENTIFIER","context":"normal","value":"func","line":4,"start":114,"end":118},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":118,"end":119},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":120,"end":129}],"errors":[]}
Objects are structurally typed
When comparing an object type with an object it must have the same structure
in order to be considered valid.
1
2
3
|
type ObjType = { property: string };
let obj = { property: "value" };
let test: ObjType = obj;
|
{"value":"type ObjType = { property: string };\nlet obj = { property: \"value\" };\nlet test: ObjType = obj;\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"ObjType","line":1,"start":5,"end":12},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":13,"end":14},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":1,"start":17,"end":25},{"type":"T_COLON","context":"type","value":":","line":1,"start":25,"end":26},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":27,"end":33},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":34,"end":35},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":35,"end":36},{"type":"T_LET","context":"normal","value":"let","line":2,"start":37,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":41,"end":44},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":45,"end":46},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":47,"end":48},{"type":"T_IDENTIFIER","context":"normal","value":"property","line":2,"start":49,"end":57},{"type":"T_COLON","context":"normal","value":":","line":2,"start":57,"end":58},{"type":"T_STRING","context":"normal","value":"\"value\"","line":2,"start":59,"end":66},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":67,"end":68},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":68,"end":69},{"type":"T_LET","context":"normal","value":"let","line":3,"start":70,"end":73},{"type":"T_IDENTIFIER","context":"normal","value":"test","line":3,"start":74,"end":78},{"type":"T_COLON","context":"type","value":":","line":3,"start":78,"end":79},{"type":"T_IDENTIFIER","context":"type","value":"ObjType","line":3,"start":80,"end":87},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":88,"end":89},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":90,"end":93},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":93,"end":94}],"errors":[]}
Classes are nominally typed
When you have two classes with the same structure, they still are not
considered equivalent because Flow uses nominal typing for classes.
1
2
3
4
|
class Foo { method(input: string) { } }
class Bar { method(input: string) { } }
let test: Foo = new Bar();
|
Cannot assign `new Bar()` to `test` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]
{"value":"// @flow\nclass Foo { method(input: string) { /* ... */ } }\nclass Bar { method(input: string) { /* ... */ } }\nlet test: 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":"method","line":2,"start":21,"end":27},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":2,"start":28,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":35,"end":41},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":41,"end":42},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":43,"end":44},{"type":"Block","context":"comment","value":"/* ... */","line":2,"start":45,"end":54},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":55,"end":56},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":57,"end":58},{"type":"T_CLASS","context":"normal","value":"class","line":3,"start":59,"end":64},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":3,"start":65,"end":68},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":69,"end":70},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":3,"start":71,"end":77},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":77,"end":78},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":3,"start":78,"end":83},{"type":"T_COLON","context":"type","value":":","line":3,"start":83,"end":84},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":85,"end":91},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":91,"end":92},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":93,"end":94},{"type":"Block","context":"comment","value":"/* ... */","line":3,"start":95,"end":104},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":105,"end":106},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":107,"end":108},{"type":"T_LET","context":"normal","value":"let","line":4,"start":109,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"test","line":4,"start":113,"end":117},{"type":"T_COLON","context":"type","value":":","line":4,"start":117,"end":118},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":4,"start":119,"end":122},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":123,"end":124},{"type":"T_NEW","context":"normal","value":"new","line":4,"start":125,"end":128},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":4,"start":129,"end":132},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":132,"end":133},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":133,"end":134},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":134,"end":135},{"type":"Line","context":"comment","value":"// Error!","line":4,"start":136,"end":145}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `new Bar()` to `test` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]","context":"let test: Foo = new Bar(); // Error!","source":"-","start":{"line":4,"column":17,"offset":125},"end":{"line":4,"column":25,"offset":134}}],"operation":null}]}
If you wanted to use a class structurally you could do that using an interface:
1
2
3
4
5
6
7
8
9
|
interface Interface {
method(value: string): void;
};
class Foo { method(input: string) { } }
class Bar { method(input: string) { } }
let test: Interface = new Foo();
let test: Interface = new Bar();
|
{"value":"interface Interface {\n method(value: string): void;\n};\n\nclass Foo { method(input: string) { /* ... */ } }\nclass Bar { method(input: string) { /* ... */ } }\n\nlet test: Interface = new Foo(); // Okay.\nlet test: Interface = new Bar(); // Okay.\n","tokens":[{"type":"T_INTERFACE","context":"normal","value":"interface","line":1,"start":0,"end":9},{"type":"T_IDENTIFIER","context":"type","value":"Interface","line":1,"start":10,"end":19},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":2,"start":24,"end":30},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":30,"end":31},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":31,"end":36},{"type":"T_COLON","context":"type","value":":","line":2,"start":36,"end":37},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":38,"end":44},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":44,"end":45},{"type":"T_COLON","context":"type","value":":","line":2,"start":45,"end":46},{"type":"T_VOID_TYPE","context":"type","value":"void","line":2,"start":47,"end":51},{"type":"T_SEMICOLON","context":"type","value":";","line":2,"start":51,"end":52},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":53,"end":54},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":54,"end":55},{"type":"T_CLASS","context":"normal","value":"class","line":5,"start":57,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":5,"start":63,"end":66},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":5,"start":69,"end":75},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":5,"start":76,"end":81},{"type":"T_COLON","context":"type","value":":","line":5,"start":81,"end":82},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":83,"end":89},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":89,"end":90},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":91,"end":92},{"type":"Block","context":"comment","value":"/* ... */","line":5,"start":93,"end":102},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":103,"end":104},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":105,"end":106},{"type":"T_CLASS","context":"normal","value":"class","line":6,"start":107,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":6,"start":113,"end":116},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":117,"end":118},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":6,"start":119,"end":125},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"input","line":6,"start":126,"end":131},{"type":"T_COLON","context":"type","value":":","line":6,"start":131,"end":132},{"type":"T_STRING_TYPE","context":"type","value":"string","line":6,"start":133,"end":139},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":139,"end":140},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":141,"end":142},{"type":"Block","context":"comment","value":"/* ... */","line":6,"start":143,"end":152},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":153,"end":154},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":155,"end":156},{"type":"T_LET","context":"normal","value":"let","line":8,"start":158,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"test","line":8,"start":162,"end":166},{"type":"T_COLON","context":"type","value":":","line":8,"start":166,"end":167},{"type":"T_IDENTIFIER","context":"type","value":"Interface","line":8,"start":168,"end":177},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":178,"end":179},{"type":"T_NEW","context":"normal","value":"new","line":8,"start":180,"end":183},{"type":"T_IDENTIFIER","context":"normal","value":"Foo","line":8,"start":184,"end":187},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":187,"end":188},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":188,"end":189},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":189,"end":190},{"type":"Line","context":"comment","value":"// Okay.","line":8,"start":191,"end":199},{"type":"T_LET","context":"normal","value":"let","line":9,"start":200,"end":203},{"type":"T_IDENTIFIER","context":"normal","value":"test","line":9,"start":204,"end":208},{"type":"T_COLON","context":"type","value":":","line":9,"start":208,"end":209},{"type":"T_IDENTIFIER","context":"type","value":"Interface","line":9,"start":210,"end":219},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":220,"end":221},{"type":"T_NEW","context":"normal","value":"new","line":9,"start":222,"end":225},{"type":"T_IDENTIFIER","context":"normal","value":"Bar","line":9,"start":226,"end":229},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":229,"end":230},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":230,"end":231},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":231,"end":232},{"type":"Line","context":"comment","value":"// Okay.","line":9,"start":233,"end":241}],"errors":[]}
Mixing nominal and structural typing
The design decision in Flow around mixing nominal and structural typing was
chosen based on how objects, functions, and classes are already used in
JavaScript.
The JavaScript language is a bunch of object-oriented ideas and functional
ideas mixed together. Developer’s usage of JavaScript tends to be mixed as
well. Classes (or constructor functions) being the more object-oriented side
and functions (as lambdas) and objects tend to be more on the functional side,
developers use both simultaneously.
When someone writes a class, they are declaring a thing. This thing might
have the same structure as something else but they still serve different
purposes. Imagine two component classes that both have render()
methods,
these components could still have totally different purposes, but in a
structural type system they’d be considered exactly the same.
Flow chooses what is natural for JavaScript, and should behave the way you
expect it to.