Уточнения типов
Уточнения — часто используемый аспект многих систем типов. Они так прочно укоренились в том, как мы программируем, и даже в том, как мы думаем, что вы можете их даже не заметить.
В коде ниже значение может быть "A" или "B".
// @flow
function method(value: "A" | "B") {
if (value === "A") {
// value is "A"
}
}
Внутри блока if мы знаем, что значение должно быть "A", потому что только в этом случае условное выражение будет истинным.
Способность статического анализатора типов определить, что значение внутри оператора if должно быть "A", называется уточнением.
Далее мы добавим блок else к нашему оператору if.
// @flow
function method(value: "A" | "B") {
if (value === "A") {
// value is "A"
} else {
// value is "B"
}
}
Внутри блока else мы знаем, что значение должно быть "B", потому что оно может быть только "A" или "B", и мы исключили "A" из возможных значений.
Вы можете расширить это ещё дальше и продолжать уточнять возможные значения:
// @flow
function method(value: "A" | "B" | "C" | "D") {
if (value === "A") {
// value is "A"
} else if (value === "B") {
// value is "B"
} else if (value === "C") {
// value is "C"
} else {
// value is "D"
}
}
Уточнения также могут иметь другие формы, помимо проверки на равенство:
// @flow
function method(value: boolean | Array<string> | Event) {
if (typeof value === "boolean") {
// value is a boolean
} else if (Array.isArray(value)) {
// value is an Array
} else if (value instanceof Event) {
// value is an Event
}
}
Или вы можете уточнять форму объектов.
// @flow
type A = { type: "A" };
type B = { type: "B" };
function method(value: A | B) {
if (value.type === "A") {
// value is A
} else {
// value is B
}
}
Что также относится к вложенным типам внутри объектов.
// @flow
function method(value: { prop?: string }) {
if (value.prop) {
value.prop.charAt(0);
}
}
Недействительность уточнений
Также возможно сделать уточнения недействительными, например:
// @flow
function otherMethod() { /* ... */ }
function method(value: { prop?: string }) {
if (value.prop) {
otherMethod();
// $ExpectError
value.prop.charAt(0);
}
}
Причина в том, что мы не знаем, что otherMethod() не выполнил каких-либо действий со значением. Представьте себе следующую ситуацию:
// @flow
var obj = { prop: "test" };
function otherMethod() {
if (Math.random() > 0.5) {
delete obj.prop;
}
}
function method(value: { prop?: string }) {
if (value.prop) {
otherMethod();
// $ExpectError
value.prop.charAt(0);
}
}
method(obj);
Внутри otherMethod() иногда удаляется prop. Поток не знает, всё ли ещё верно для проверки if (value.prop), поэтому он делает уточнение недействительным.
Существует простой способ обойти эту проблему. Сохраните значение перед вызовом другого метода и используйте сохранённое значение вместо него. Таким образом, вы можете предотвратить аннулирование уточнения.
// @flow
function otherMethod() { /* ... */ }
function method(value: { prop?: string }) {
if (value.prop) {
var prop = value.prop;
otherMethod();
prop.charAt(0);
}
}
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/lang/refinements