Номинальная и структурная типизация
При сравнении типов между собой статическая система проверки типов может использовать либо их названия (номинальная типизация), либо их структуру (структурная типизация), например при проверке, является ли один тип подтипом другого.
TypeScript преимущественно использует структурную типизацию с несколькими узкими исключениями в пользу номинальной, тогда как Flow использует структурную типизацию для обычных объектов и функций, но номинальную — для классов, непрозрачных типов и перечислений Flow. Подробнее см. в разделе Сравнение номинальной типизации в Flow и TypeScript.
Номинальная типизация
В таких языках, как C++, Java и Swift, используются преимущественно номинальные системы типов.
// Pseudo code: nominal system
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let foo: Foo = new Bar(); // Error! В этом примере на псевдокоде номинальная система типов выдаёт ошибку, несмотря на то что у обоих классов есть метод с одинаковыми именем и типом. Это происходит потому, что названия классов (и места их объявления) различаются.
Структурная типизация
В таких языках, как Go и TypeScript, используются преимущественно структурные системы типов.
// Pseudo code: structural system
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let foo: Foo = new Bar(); // Works! В этом примере на псевдокоде структурная система типов позволяет использовать Bar как Foo, поскольку у обоих классов есть методы и поля с одинаковыми именами и типами.
Однако если структура классов различается, структурная система выдаст ошибку:
// Pseudo code
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: number) { /* ... */ } }
let foo: Foo = new Bar(); // Error! Мы рассмотрели номинальную и структурную типизацию классов, но есть и другие сложные типы, например объекты и функции, которые также можно сравнивать номинально или структурно. Кроме того, система типов может сочетать элементы структурной и номинальной типизации.
В Flow
Flow использует структурную типизацию для объектов и функций, но номинальную — для классов.
Функции типизируются структурно
При сравнении типа функции с функцией её структура должна совпадать, чтобы она считалась допустимой.
type FuncType = (input: string) => void;
function func(input: string) { /* ... */ }
let test: FuncType = func; // Works! Объекты типизируются структурно
При сравнении типа объекта с объектом его структура должна совпадать, чтобы он считался допустимым.
type ObjType = {property: string};
let obj = {property: "value"};
let test: ObjType = obj; // Works Классы типизируются номинально
Даже если два класса имеют одинаковую структуру, они всё равно не считаются эквивалентными, поскольку Flow использует для классов номинальную типизацию.
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let test: Foo = new Bar(); // Error! Если вы хотите использовать класс структурно, это можно сделать с помощью интерфейса:
interface Interface {
method(value: string): void;
};
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let test1: Interface = new Foo(); // Works
let test2: Interface = new Bar(); // Works Структурный тип объекта, например {method: (value: string) => void}, здесь не подойдёт вместо интерфейса: типы объектов описывают литералы объектов, а не экземпляры классов. О причинах и способе решения см. раздел Интерфейсы для экземпляров и объектов.
Непрозрачные типы
С помощью непрозрачных типов можно преобразовать псевдоним, который ранее типизировался структурно, в номинальный (за пределами файла, в котором он определён).
// A.js export type MyTypeAlias = string; export opaque type MyOpaqueType = string; const x: MyTypeAlias = "hi"; // Works const y: MyOpaqueType = "hi"; // Works
В другом файле MyTypeAlias остаётся прозрачным, а MyOpaqueType становится номинальным:
// B.js — when imported, type aliases are transparent but opaque types are not type MyTypeAlias = string; declare opaque type MyOpaqueType; const x: MyTypeAlias = "hi"; // Works const y: MyOpaqueType = "hi"; // Error! `MyOpaqueType` is not interchangeable with `string`
Перечисления Flow
Перечисления Flow не позволяют взаимозаменяемо использовать элементы перечисления с одинаковыми значениями, если они принадлежат разным перечислениям.
enum A {
X = "x",
}
enum B {
X = "x",
}
const a: A = B.X; // Error! См. также
- Классы — номинальная типизация: сравнение по имени класса
- Интерфейсы — структурная типизация: допускает классы с одинаковой структурой
- Объекты — структурная типизация: сравнение по структуре
- Псевдонимы непрозрачных типов — номинальная типизация: скрытие базового типа на границах модулей
- Перечисления Flow — номинальная типизация: каждое перечисление является отдельным типом
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/lang/nominal-structural