Шаблоны сопоставления
Шаблоны сопоставления задают условие, которому должно соответствовать значение, и новые переменные, которые извлекаются (как при деструктуризации).
Шаблоны примитивных значений
Шаблоны примитивных значений включают строковые литералы (например, 'light'), числовые литералы (например, 42), литералы BigInt (например, 10n), логические литералы (например, true), null и undefined.
Можно использовать переменные (например, name) или обращения к свойствам (например, Status.Active), которые имеют тип литерального типа или элемента перечисления Flow. В шаблонах сопоставления нельзя использовать переменные с общим типом, например string или number, — они должны иметь литеральный тип, например 'light'. Чтобы присвоить строковому значению литеральный тип, можно добавить аннотацию типа или as const. Вычисляемые свойства в шаблонах сопоставления допускают только литералы, например foo['bar'] или foo[2].
Идентификатор _ специально обрабатывается в «шаблонах-универсальных шаблонах». Если нужно сопоставить значение с таким именем, сначала переименуйте его. Чтобы создать новую переменную, см. раздел «Шаблоны объявления переменных» ниже — для этого используется const x.
Можно использовать числовой литерал с префиксом + или -, а также литералы BigInt с префиксом - (использование + для BigInt приводит к ошибке в JS). +0 и -0 не допускаются (с точки зрения типов Flow не различает их и 0). Для NaN предусмотрена особая обработка, поскольку NaN === NaN всегда равно false. Сопоставление выполняется с помощью Number.isNaN.
Другие типы выражений не поддерживаются. Чтобы сопоставить произвольное выражение (имеющее литеральный тип), сначала присвойте его переменной, а затем сопоставьте значение этой переменной.
Пример:
const Status = {active: 'active', paused: 'paused'} as const;
declare const ranks: [10, 20];
declare const fallbackLabel: 'unknown';
declare const value: unknown;
match (value) {
0 => {}
'archived' => {}
null => {}
-1 => {}
fallbackLabel => {} // a literal-typed variable
Status.active => {} // a property access
Status['paused'] => {} // a computed string member
ranks[0] => {} // a computed number member
_ => {}
} Flow никогда не выдаст ошибку «неиспользуемый шаблон» для шаблона undefined, поэтому его можно добавлять всегда, даже если входной тип не содержит void. Это позволяет учесть случаи, когда Flow вычисляет тип, не на 100% соответствующий значению во время выполнения (например, при обращении по индексу к элементу массива за пределами его длины).
Во время выполнения эти проверки выполняются с помощью оператора строгого равенства ===.
Шаблоны-универсальные шаблоны
Шаблоны-универсальные шаблоны, состоящие из одного символа подчёркивания _, соответствуют любому значению. Если нужно сопоставить значение переменной с именем _, сначала присвойте ей другое имя.
Если часть входного типа невозможно сопоставить исчерпывающим образом (например, string), то в match потребуется универсальный шаблон.
Пример:
declare const username: string;
match (username) {
_ => {}
} Шаблоны объявления переменных
Шаблоны объявления переменных, такие как const name, берут значение из соответствующей позиции и присваивают его новой переменной. При проверке условия они ведут себя как универсальный шаблон и соответствуют любому значению.
Пример:
declare const input: string;
const greeting = match (input) {
const name => `Hello, ${name}!`,
}; Хотя среда выполнения также поддерживает переменные let, сейчас они приводят к ошибке типа; разрешены только переменные const. Если у вас есть сценарий использования, расскажите о нём команде. var не поддерживается.
Шаблоны объектов
Шаблоны объектов сопоставляют объекты с той же структурой. Например, шаблон {type: 'light', num: 42} соответствует объектам, у которых свойство type имеет значение 'light', а свойство num — значение 42. Если у объекта есть дополнительные свойства, не указанные в шаблоне, или его тип является неточным, шаблон также должен быть неточным. Для этого используйте ..., например {type: 'light', num: 42, ...}.
Как и при деструктуризации, вложенный в шаблон объекта шаблон объявления переменной создаёт новую переменную со значением этого свойства. Например, match (arg) { {prop: const x} => x } инициализирует x значением arg.prop. Чтобы собрать остальные собственные свойства объекта, можно использовать остаток объекта: {foo: 1, ...const rest}.
Запись {name} неоднозначна: она может означать {name: name} (сопоставление со значением переменной name) или {name: const name} (извлечение значения свойства в новую переменную с именем name), поэтому она не допускается. Для сокращённой записи создания новых переменных можно использовать {const name} вместо {name: const name}.
Если для свойства используется универсальный шаблон или шаблон объявления переменной, например {foo: _} или {foo: const x}, проверяется, что это свойство содержится в объекте (проверяются собственные и унаследованные свойства).
При проверке объектов с необязательными свойствами для исчерпывающей проверки необходимо включить шаблон, не содержащий необязательные свойства. Например, для {name: string, age?: number}, если сначала сопоставить значение с шаблоном {name: _, age: _}, также потребуется шаблон {name: _, ...}, чтобы обработать случаи, когда свойство age отсутствует.
Имена свойств могут быть идентификаторами (например, foo: pattern), строковыми литералами (например, 'foo': <pattern>) или числовыми литералами (например, 2: <pattern>). Повторяющиеся ключи объекта запрещены, а BigInt не поддерживается в качестве ключей объекта (Flow пока их не поддерживает).
Пример:
declare const event:
| {kind: 'click', x: number, y: number}
| {message: string}
| {kind: 'scroll', delta: number};
const summary = match (event) {
{kind: 'click', x: const x, y: const y} => `click at ${x},${y}`,
{const message} => message,
{kind: 'scroll', ...const rest} => `scroll ${rest.delta}`,
}; Геттеры также не поддерживаются: во время выполнения они будут вызываться несколько раз — по одному разу для каждой выполняемой проверки условия.
Шаблоны экземпляров
Шаблоны экземпляров сопоставляют экземпляры класса. Шаблон состоит из имени класса, за которым следует шаблон объекта. Например, Point {x: 1, ...} сопоставляет значения, являющиеся экземплярами Point, у которых свойство x равно 1. Во время выполнения это эквивалентно проверке p instanceof Point && p.x === 1.
Часть шаблона объекта работает как обычный шаблон объекта: можно извлекать свойства с помощью шаблонов объявления переменных, использовать сокращённую запись {const x} и собирать остальные свойства с помощью остатка объекта.
Экземпляры всегда являются неточными, поскольку класс можно расширить дополнительными свойствами, поэтому шаблон необходимо завершать с помощью ....
Пример:
class Point {
x: number;
y: number;
}
declare const p: Point;
const sum = match (p) {
Point {const x, const y, ...} => x + y,
}; Если не указать ..., Flow выдаст ошибку, поскольку у экземпляра могут быть дополнительные свойства:
class Point {
x: number;
y: number;
}
declare const p: Point;
match (p) {
Point {const x, const y} => {} // ERROR: instances are inexact, so `...` is required
} Каждый класс в объединении можно сопоставить отдельным шаблоном, и тип в теле будет сужен до этого класса:
class Shape {
area(): number {
return 0;
}
}
class Circle extends Shape {
radius: number;
}
class Square extends Shape {
side: number;
}
declare const shape: Circle | Square;
const size = match (shape) {
Circle {const radius, ...} => radius, // matches `Circle` instances
Square {const side, ...} => side, // matches `Square` instances
}; Сопоставление следует по цепочке прототипов, как instanceof: шаблон с именем суперкласса соответствует экземплярам любого его подкласса.
class Shape {
area(): number {
return 0;
}
}
class Circle extends Shape {
radius: number;
}
class Square extends Shape {
side: number;
}
declare const shape: Circle | Square;
const out = match (shape) {
Shape {...} => shape.area(), // a superclass pattern matches any subclass instance
}; Шаблоны объектов являются структурными и также соответствуют экземплярам, поэтому {const x, const y, ...} соответствует Point. Шаблон экземпляра, напротив, соответствует только экземплярам указанного класса и никогда не соответствует обычному объекту.
class Point {
x: number;
y: number;
}
declare const p: Point;
const sum = match (p) {
{const x, const y, ...} => x + y, // an object pattern matches the instance
}; Конструктор должен ссылаться на один класс. Использование типа, интерфейса или другого значения приводит к ошибке.
class Point {
x: number;
y: number;
}
declare const p: Point;
declare const NotAClass: string;
match (p) {
NotAClass {...} => {} // ERROR: constructor must reference a single class
_ => {}
} Шаблоны экземпляров доступны по умолчанию начиная с Flow v0.317.
Шаблоны массивов
Шаблоны массивов сопоставляют значения кортежей и массивов. Например, шаблон ['small', true] соответствует массивам длины 2, элементы которых соответствуют элементам шаблона. Чтобы проверка длины была менее строгой, можно сделать шаблон неточным с помощью .... Например, ['small', true, ...] будет соответствовать массивам длины >= 2, первые элементы которых соответствуют элементам шаблона. С помощью [...] можно сопоставить любой массив или кортеж.
Как и при деструктуризации, вложенный в шаблон массива шаблон объявления переменной создаёт новую переменную со значением соответствующего элемента. Например, match (arg) { [const x] => x } инициализирует x значением arg[0]. В конце шаблона можно использовать остаток массива, чтобы собрать оставшиеся элементы и проверить, что длина массива не меньше длины шаблона. Например: [1, 2, ...const rest].
Шаблоны массивов соответствуют значениям, для которых выполняется Array.isArray, поэтому они не соответствуют объектам, похожим на массивы, но не являющимся массивами, и не соответствуют итерируемым объектам. Если нужно сопоставить такие значения с помощью шаблонов массивов, сначала используйте для них Array.from.
Пример:
declare const color: [0, 0, 0] | [255, 0, 0, 1] | [0, 0, 255, 0.5];
const css = match (color) {
[0, 0, 0] => 'black',
[255, 0, 0, ...] => 'opaque red',
[0, 0, 255, ...const rest] => `blue at ${rest[0]} alpha`,
}; Шаблоны «или»
Шаблоны «или» позволяют объединять несколько шаблонов с помощью |. Например, 'active' | 'paused' будет соответствовать любому из строковых литералов.
Шаблоны объявления переменных внутри шаблонов «или» пока не поддерживаются.
Пример:
declare const reply: 'yes' | 'yeah' | 'no' | 'nope';
const answer = match (reply) {
'yes' | 'yeah' => true,
'no' | 'nope' => false,
}; Шаблоны «как»
Шаблоны «как» выполняют сопоставление с шаблоном и создают новую переменную. Например, [_, _] as pair сначала сопоставит любые массивы длины 2, а затем присвоит это значение новой переменной с именем pair.
Пример:
declare const result: ['ok', number] | ['error', string];
const handled = match (result) {
['ok', _] as success => success,
['error', _] as failure => failure,
}; См. также
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/match/patterns