Непрозрачные псевдонимы типов
Непрозрачные псевдонимы типов — это псевдонимы типов, которые скрывают базовый тип за пределами файла, в котором они определены.
opaque type ID = string;
Непрозрачные типы доступны только в Flow — в TypeScript нет встроенного аналога. Распространённый идиоматический подход в TS — «брендированные типы» (пересечение с типизированным приватным символом свойством), то есть шаблон на пользовательском уровне, а не функция языка. Обеспечиваемая им граница слабее, чем абстракция с областью видимости файла в Flow: бренды TS реализуются на пользовательском уровне и могут быть подделаны с помощью приведения as, а также допускают структурное создание, если ключ бренда доступен или является строковым, а не приватным unique symbol. Полное сравнение см. в разделе Непрозрачные типы в Flow.
Когда это использовать
Используйте непрозрачные типы вместо обычных псевдонимов типов, когда нужно обеспечить границы абстракции между модулями — например, запретить вызывающему коду рассматривать ID как обычный string. Обычный type ID = string прозрачен на границах файлов, поэтому любой потребитель может обращаться с ID в точности как с string; opaque type ID = string сохраняет эту прозрачность внутри файла, где он определён, но скрывает базовый тип на границе модуля.
exports.js
export type TransparentID = string;
export opaque type OpaqueID = string;
export function makeOpaqueID(s: string): OpaqueID { return s; } imports.js
// The importer's view: `TransparentID` is still `string`; `OpaqueID` is nominal.
declare type TransparentID = string;
declare opaque type OpaqueID;
declare function makeOpaqueID(s: string): OpaqueID;
const a: TransparentID = "abc"; // Works — transparent alias is interchangeable with string
const b: string = a; // Works — round-trips with no boundary
const oid: OpaqueID = makeOpaqueID("abc"); // Works — the only way to obtain an OpaqueID
const c: OpaqueID = "abc"; // Error — outside the defining file, string is not an OpaqueID
const d: string = oid; // Error — outside the defining file, OpaqueID is not a string Используйте opaque type, когда вызывающие стороны должны получать значение только через функцию-конструктор, контролируемую определяющим модулем, — например, для идентификаторов, очищенных строк или единиц измерения. Оставьте type, если псевдоним нужен только для документации и вас устраивает, что потребители будут обращаться с ним как с его базовым представлением. Используйте необязательное ограничение супертипа, если потребителям нужен частичный доступ (например, возможность читать ID как string, но не создавать его из string).
Синтаксис непрозрачных псевдонимов типов
Непрозрачные псевдонимы типов создаются с помощью ключевых слов opaque type, за которыми следуют имя псевдонима, знак равенства = и определение типа.
opaque type Alias = Type;
К непрозрачному псевдониму типа можно добавить необязательное ограничение подтипизации: после имени укажите двоеточие : и тип.
opaque type Alias: SuperType = Type;
В качестве супертипа или типа непрозрачного псевдонима типа можно использовать любой тип.
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; Проверка типов непрозрачных псевдонимов типов
Внутри файла, где тип определён
В том же файле, где определён псевдоним, непрозрачные псевдонимы типов ведут себя точно так же, как обычные псевдонимы типов.
opaque type NumberAlias = number;
0 as 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; } За пределами файла, где тип определён
При импорте непрозрачный псевдоним типа ведёт себя как номинальный тип, скрывая свой базовый тип.
exports.js
export opaque type NumberAlias = number;
imports.js
// In imports.js, NumberAlias is opaque — its underlying type (number) is hidden
declare opaque type NumberAlias;
0 as NumberAlias; // Error: 0 is not a NumberAlias!
function convert(x: NumberAlias): number {
return x; // Error: x is not a number!
} Ограничения подтипизации
Если добавить к непрозрачному псевдониму типа ограничение подтипизации, за пределами файла, где он определён, непрозрачный тип можно использовать как супертип.
exports.js
export opaque type ID: string = string;
imports.js
// In imports.js, ID is opaque with a supertype constraint of string
declare opaque type ID: string;
function formatID(x: ID): string {
return "ID: " + x; // OK! IDs are strings.
}
function toID(x: string): ID {
return x; // Error: strings are not IDs.
} При создании непрозрачного псевдонима типа с ограничением подтипизации тип в позиции типа должен быть подтипом типа в позиции супертипа.
opaque type Bad: string = number; // Error: number is not a subtype of string
opaque type Good: {x: string, ...} = {x: string, y: number}; Обобщённые типы
У непрозрачных псевдонимов типов также могут быть собственные параметры типа, которые работают точно так же, как параметры типа у обычных псевдонимов типов
opaque type MyObject<A, B, C>: {foo: A, bar: B, ...} = {
foo: A,
bar: B,
baz: C,
};
const val: MyObject<number, boolean, string> = {
foo: 1,
bar: true,
baz: 'three',
}; Определения библиотек
Непрозрачные псевдонимы типов также можно объявлять в определениях библиотек. В этом случае базовый тип не указывается, но супертип при желании можно добавить.
declare opaque type Foo; declare opaque type PositiveNumber: number;
См. также
- Псевдонимы типов — обычные (прозрачные) псевдонимы типов
- Номинальная и структурная типизация — непрозрачные типы типизируются номинально, в отличие от обычных псевдонимов типов
- Обобщённые типы — непрозрачные псевдонимы типов можно параметризовать
- Определения библиотек — объявление непрозрачных типов в файлах определений библиотек
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/types/opaque-types