Spec-Zone.ru › Flow

Объявления переменных

При объявлении новой переменной можно указать её тип.

В 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API