Объявления переменных
При объявлении новой переменной можно указать её тип.
В JavaScript есть три способа объявить локальные переменные:
-
var— объявляет переменную, при необходимости присваивая ей значение. (MDN) -
let— объявляет переменную с блочной областью видимости, при необходимости присваивая ей значение. (MDN) -
const— объявляет переменную с блочной областью видимости и присваивает ей значение, которое нельзя изменить повторным присваиванием. (MDN)
В Flow они делятся на две группы:
-
letиvar— переменные, которым можно присваивать новые значения. -
const— переменные, которым нельзя присваивать новые значения.
var varVariable = 1; let letVariable = 1; const constVariable = 1; varVariable = 2; // Works! letVariable = 2; // Works! constVariable = 2; // Error!
const
Поскольку переменной const нельзя присвоить новое значение позднее, всё довольно просто.
Flow может вывести тип из присваиваемого значения или вы можете указать его самостоятельно.
const foo /* : number */ = 1; const bar: number = 2;
var и let
Поскольку переменным var и let можно присваивать новые значения, вам нужно знать ещё несколько правил.
Если вы укажете тип, то сможете присваивать переменной новые значения, но они всегда должны иметь совместимый тип.
let foo: number = 1; foo = 2; // Works! foo = '3'; // Error!
Если у переменной нет аннотации, Flow выводит точный тип на основе её инициализатора или первоначального присваивания. Все последующие присваивания этой переменной будут ограничены этим типом. В этом разделе приведены примеры того, как Flow определяет тип, выводимый для переменной без аннотации.
Если вы хотите, чтобы у переменной был тип, отличный от выводимого Flow, всегда можно добавить аннотацию типа в объявление переменной. Она переопределит всё, что обсуждается на этой странице!
Переменные, инициализированные при объявлении
Обычно тип переменной без аннотации определяется довольно просто: если переменная объявлена с инициализатором, отличным от литерала null, то с этого момента она будет иметь тип инициализатора, и все последующие присваивания переменной будут ограничены этим типом.
import * as React from 'react';
type Props = Readonly<{prop: string}>;
declare const x: number;
declare const y: number;
declare const props: Props;
let product = Math.sqrt(x) + y;
// `product` has type `number`
let Component = ({prop}: Props): React.Node => {
return <div />;
};
// `Component` has type `component(prop: string)`
let element = <Component {...props} />;
// `element` has type `React.MixedElement`
/* Let's define a new component */
type OtherProps = Readonly<{...Props, extra_prop: number}>;
declare const OtherComponent: OtherProps => React.Node;
declare const other_props: OtherProps;
/* Any subsequent assignments to `product`, `Component`, or `element` will be
* checked against the types that Flow infers for the initializers, and if
* conflicting types are assigned, Flow will signal an error. */
product = 'Our new product is...'; // Error
Component = ({prop}: OtherProps): React.Node => { // Error
return <div />;
};
element = <OtherComponent {...other_props} />; Чтобы типы в этих примерах проверялись и Flow понимал, что этим переменным можно присваивать значения разных типов, добавьте в их объявления аннотацию типа, отражающую этот более общий тип:
import * as React from 'react'; let product: number | string = 1; let Component: unknown = null; // No good type to represent this! Consider restructuring let element: React.Node = <div />;
Переменные, объявленные без инициализаторов
Переменные часто объявляются без инициализаторов. В таких случаях Flow попытается определить тип по первому присваиванию или нескольким первым присваиваниям переменной. Здесь «первое» означает как порядок сверху вниз, так и переход от внешней области видимости к вложенной: сначала Flow попытается выбрать присваивание, происходящее в той же области видимости функции, что и объявление переменной, и будет искать присваивания во вложенных функциях, только если в текущей области их не окажется:
let topLevelAssigned;
function helper() {
topLevelAssigned = 42; // Error: `topLevelAssigned` has type `string`
}
topLevelAssigned = 'Hello world'; // This write determines the var's type
topLevelAssigned = true; // Error: `topLevelAssigned` has type `string` Если возможно несколько «первых присваиваний» из-за инструкции if или switch, будут учитываться оба варианта. Это один из немногих случаев, когда Flow по-прежнему выводит объединения типов переменных:
let myNumberOrString;
declare const condition: boolean;
if (condition) {
myNumberOrString = 42; // Determines type
} else {
myNumberOrString = 'Hello world'; // Determines type
}
myNumberOrString = 21; // fine, compatible with type
myNumberOrString = 'Goodbye'; // fine, compatible with type
myNumberOrString = false; // Error: `myNumberOrString` has type `number | string` Однако это работает, только если присваивание переменной происходит в обеих ветвях. Если присваивание есть лишь в одной ветви, после неё переменная будет иметь тип этого присваивания (хотя Flow по-прежнему проверит, что переменная обязательно инициализирована):
let oneBranchAssigned;
declare const condition: boolean;
if (condition) {
oneBranchAssigned = 'Hello world!';
}
oneBranchAssigned.toUpperCase(); // Error: `oneBranchAssigned` may be uninitialized
oneBranchAssigned = 42; // Error: `oneBranchAssigned` has type `string` Переменные, инициализированные значением null
Наконец, есть одно исключение из общего принципа, согласно которому типы переменных определяются их первыми присваиваниями: когда переменная инициализируется буквальным значением null (или получает его при первом присваивании). В таких случаях тип переменной определяется следующим присваиванием ненулевого значения (согласно тем же правилам, что и выше), а итоговый тип переменной становится объединением null и типа последующего присваивания. Это позволяет использовать распространённый шаблон, при котором переменной сначала присваивается null, а затем значение другого типа:
function findIDValue<T>(dict: {[key: string]: T}): T {
let idVal = null; // initialized as `null`
for (const key in dict) {
if (key === 'ID') {
idVal = dict[key]; // Infer that `idVal` has type `null | T`
}
}
if (idVal === null) {
throw new Error('No entry for ID!');
}
return idVal;
} Переменные catch
Если у переменной catch нет аннотации, её тип по умолчанию — any.
При желании можно указать для неё тип unknown или any. Например:
try {
} catch (e: unknown) {
if (e instanceof TypeError) {
e as TypeError; // OK
} else if (e instanceof Error) {
e as Error; // OK
} else {
throw e;
}
} Используя unknown, вы можете повысить безопасность и покрытие Flow ценой увеличения количества проверок во время выполнения.
Тип по умолчанию для переменных catch без аннотации можно изменить, установив параметр use_unknown_in_catch_variables в значение true.
См. также
- Требование аннотаций — случаи, когда Flow требует явные аннотации типов
- Уточнения — как типы переменных сужаются с помощью условных проверок
-
Константные выражения — использование
as constдля вывода более узких типов переменных по их инициализаторам -
Приведение типов — использование
asдля расширения типов переменных или явного указания их типов
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/lang/variables