Часто задаваемые вопросы
Я проверил, что foo.bar не null, но Flow всё ещё считает, что оно таковым является. Почему это происходит и как это исправить?
Flow не отслеживает побочные эффекты, поэтому любой вызов функции потенциально может обнулить вашу проверку. Это называется недействительностью уточнения.
Пример (https://flow.org/try):
// @flow
type Param = {
bar: ?string,
}
function myFunc(foo: Param): string {
if (foo.bar) {
console.log("checked!");
return foo.bar; // Flow errors. If you remove the console.log, it works
}
return "default string";
}
Вы можете обойти эту проблему, сохраняя проверяемые значения в локальных переменных:
// @flow
type Param = {
bar: ?string,
}
function myFunc(foo: Param): string {
if (foo.bar) {
const bar = foo.bar;
console.log("checked!");
return bar; // Ok!
}
return "default string";
}
Я проверил, что мой объект имеет тип A, так почему Flow всё ещё считает его A | B?
Недействительность уточнения также может произойти с разъединёнными объединениями. Любой вызов функции аннулирует любое уточнение.
Пример (https://flow.org/try):
// @flow
type Response =
| { type: 'success', value: string }
| { type: 'error', error: Error };
const handleResponse = (response: Response) => {
if (response.type === 'success') {
setTimeout(() => {
console.log(`${response.value} 1`)
}, 1000);
}
};
В данном случае обходным путём было бы извлечь интересующую вас часть значения или переместить проверку if внутри вызова setTimeout.
Пример (https://flow.org/try):
// @flow
type Response =
| { type: 'success', value: string }
| { type: 'error', error: Error };
const handleResponse = (response: Response) => {
if (response.type === 'success') {
const value = response.value
setTimeout(() => {
console.log(`${value} 1`)
}, 1000);
}
};
Я внутри замыкания, и Flow игнорирует проверку if, которая утверждает, что foo.bar определён. Почему?
В предыдущем разделе мы показали, как уточнение теряется после вызова функции. То же самое происходит внутри замыканий, так как Flow не отслеживает, как ваше значение может измениться до вызова замыкания.
Пример (https://flow.org/try):
// @flow
type Person = {age: ?number}
const people = [{age: 12}, {age: 18}, {age: 24}];
const oldPerson: Person = {age: 70};
if (oldPerson.age) {
people.forEach(person => {
console.log(`The person is ${person.age} and the old one is ${oldPerson.age}`);
})
}
Решение здесь состоит в перемещении проверки if в forEach, или присвоении age промежуточной переменной.
Пример (https://flow.org/try):
// @flow
type Person = {age: ?number}
const people = [{age: 12}, {age: 18}, {age: 24}];
const oldPerson: Person = {age: 70};
if (oldPerson.age) {
const age = oldPerson.age;
people.forEach(person => {
console.log(`The person is ${person.age} and the old one is ${age}`);
})
}
Но Flow должен понимать, что эта функция не может аннулировать это уточнение, правильно?
Flow не является полным, поэтому он не может идеально проверять весь код. Вместо этого Flow делает консервативные предположения, чтобы быть надёжным.
Почему я не могу использовать функцию в моём операторе if для проверки типа свойства?
Flow не отслеживает уточнения, сделанные в отдельных вызовах функций.
Пример (https://flow.org/try)
// @flow const add = (first: number, second: number) => first + second; const val: string | number = ... const isNumber = (valueToRefine: ?number) => typeof valueToRefine === 'number'; if (isNumber(val)) add(val, 2);
Однако, Flow имеет предикатных функций, которые могут выполнять эти проверки через %checks.
Пример (https://flow.org/try)
// @flow const add = (first: number, second: number) => first + second; const val: string | number = ... const isNumber = (valueToRefine: ?number): %checks => typeof valueToRefine === 'number'; if (isNumber(val)) add(val, 2);
Почему я не могу передать Array<string> в функцию, которая принимает Array<string | number>
Аргумент функции позволяет string значениям в своём массиве, но в данном случае Flow предотвращает получение исходным массивом number. Внутри функции вы сможете добавить number в массив аргумента, что приведет к тому, что тип исходного массива больше не будет точным. Вы можете исправить эту ошибку, изменив тип аргумента на $ReadOnlyArray<string | number>. Это предотвращает добавление чего-либо в массив внутри тела функции, позволяя ему принимать более узкие типы.
Например, это не сработает:
// @flow
const fn = (arr: Array<string | number>) => {
// arr.push(123) NOTE! Array<string> passed in and after this it would also include numbers if allowed
return arr;
};
const arr: Array<string> = ['abc'];
fn(arr); // Error!
но с $ReadOnlyArray вы можете достичь того, что вы искали:
// @flow
const fn = (arr: $ReadOnlyArray<string | number>) => {
// arr.push(321) NOTE! Since you are using $ReadOnlyArray<...> you cannot push anything to it
return arr;
};
const arr: Array<string> = ['abc'];
fn(arr);
Пример (https://flow.org/try)
Почему я не могу передать { a: string } в функцию, которая принимает { a: string | number }
Аргумент функции допускает string значения в своём поле, но в этом случае Flow предотвращает запись number в исходный объект. Внутри тела функции вы можете изменить объект таким образом, что свойству a будет присвоено number, что сделает тип исходного объекта неточным. Вы можете исправить эту ошибку, сделав свойство ковариативным (только для чтения): { +a: string | number }. Это предотвращает запись в свойство внутри тела функции, что делает безопасным передачу более ограниченных типов в функцию.
Например, это не сработает:
// @flow
const fn = (obj: {| a: string | number |}) => {
// obj.a = 123;
return obj;
};
const object: {| a: string |} = {a: 'str' };
fn(object); // Error!
но с ковариативным свойством вы можете достичь того, что искали:
// @flow
const fn = (obj: {| +a: string | number |}) => {
// obj.a = 123 NOTE! Since you are using covariant {| +a: string | number |}, you can't mutate it
return obj;
};
const object: {| a: string |} = { a: 'str' };
fn(object);
Пример (https://flow.org/try)
Почему я не могу уточнить объединение объектов?
Существует две потенциальных причины:
- Вы используете неточные объекты.
- Вы деструктурируете объект. При деструктуризации Flow теряет отслеживание свойств объекта.
Нерабочий пример:
/* @flow */
type Action =
| {type: 'A', payload: string}
| {type: 'B', payload: number};
// Not OK
const fn = ({type, payload}: Action) => {
switch (type) {
case 'A': return payload.length;
case 'B': return payload + 10;
}
}
Рабочий пример:
/* @flow */
type Action =
| {type: 'A', payload: string}
| {type: 'B', payload: number};
// OK
const fn = (action: Action) => {
switch (action.type) {
case 'A': return action.payload.length;
case 'B': return action.payload + 10;
}
}
Второй пример:
- Нерабочий: https://flow.org/try
- Рабочий: https://flow.org/try
У меня ошибка «Отсутствует аннотация типа». Откуда она взялась?
Flow требует аннотации типов на границах модулей, чтобы обеспечить масштабируемость. Чтобы узнать больше об этом, ознакомьтесь с нашей статьёй в блоге на эту тему.
Наиболее распространённый случай — это экспорт функции или React-компонента. Flow требует аннотирования входов. Например, в этом примере, Flow будет жаловаться:
export const add = a => a + 1;
Решение заключается в добавлении типов к параметрам add.
Пример (https://flow.org/try):
export const add = (a: number) => a + 1;
Чтобы узнать, как аннотировать экспортируемые React-компоненты, ознакомьтесь с нашей документацией по HOC.
Существуют и другие случаи, когда это происходит, и они могут быть сложнее для понимания. Вы получите ошибку, похожую на Missing type annotation for U Например, вы написали этот код:
const array = ['a', 'b'] export const genericArray = array.map(a => a)
Здесь Flow будет жаловаться на export, требуя аннотации типа. Flow хочет, чтобы вы аннотировали экспорты, возвращаемые обобщённой функцией. Тип Array.prototype.map равен map<U>(callbackfn: (value: T, index: number, array: Array<T>) => U, thisArg?: any): Array<U>. <U> соответствует тому, что называется обобщённым типом, чтобы подчеркнуть связь типа функции, передаваемой в map, с типом массива.
Понимание логики обобщений может быть полезным, но для того, чтобы сделать ваши типы действительными, нужно помочь Flow понять тип genericArray.
Вы можете сделать это, добавив явное аргумент типа:
const array = ['a', 'b']; export const genericArray = array.map<string>(a => a);END_OF_DOCUMENT_MARKER
или, добавив аннотацию к экспортированной константе (https://flow.org/try):
const array = ['a', 'b'] export const genericArray: Array<string> = array.map(a => a)
Flow не понимает типы моего высшего порядка компонента React, как я могу ему помочь?
Настройка типов HOC может быть сложной. Хотя вы можете следовать документации по этому вопросу, иногда проще задать тип возвращаемого компонента.
Например, в этом примере, мы не задаём тип HOC (setType), а тип компонента, созданного с помощью него, Button. Для этого мы используем тип React.ComponentType.
// @flow
import * as React from 'react';
const setType = BaseComponent => props => <BaseComponent {...props} type="button" />;
const GenericButton = ({type, children}) => <button type={type} onClick={() => console.log('clicked')}>{children}</button>;
const Button: React.ComponentType<{children: React.Node}> = setType(GenericButton);
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/faq