Spec-Zone.ru › Flow

Типы функций

Функции имеют два места, где применяются типы: параметры (вход) и возвращаемое значение (выход).

// @flow
function concat(a: string, b: string): string {
  return a + b;
}

concat("foo", "bar"); // Works!
// $ExpectError
concat(true, false);  // Error!

Используя выведение типов, эти типы часто необязательны:

// @flow
function concat(a, b) {
  return a + b;
}

concat("foo", "bar"); // Works!
// $ExpectError
concat(true, false);  // Error!

Иногда выведение типов Flow создаёт более допускающие типы, чем вам хотелось бы.

// @flow
function concat(a, b) {
  return a + b;
}

concat("foo", "bar"); // Works!
concat(1, 2);         // Works!

По этой причине (и другим) полезно указывать типы для важных функций.

Синтаксис функций

Существует три формы функций, каждая из которых имеет свой немного отличающийся синтаксис.

Объявления функций

Здесь вы можете увидеть синтаксис объявлений функций с добавленными и без типов.

function method(str, bool, ...nums) {
  // ...
}

function method(str: string, bool?: boolean, ...nums: Array<number>): void {
  // ...
}

Стрелочные функции

Здесь вы можете увидеть синтаксис стрелочных функций с добавленными и без типов.

let method = (str, bool, ...nums) => {
  // ...
};

let method = (str: string, bool?: boolean, ...nums: Array<number>): void => {
  // ...
};

Типы функций

Здесь вы можете увидеть синтаксис написания типов, которые являются функциями.

(str: string, bool?: boolean, ...nums: Array<number>) => void

Вы также можете необязательно опустить имена параметров.

(string, boolean | void, Array<number>) => void

Вы можете использовать эти типы функций для чего-то вроде обратного вызова.

function method(callback: (error: Error | null, value: string | null) => void) {
  // ...
}

Параметры функций

Параметры функций могут иметь типы, добавив двоеточие : после имени параметра и перед типом.

function method(param1: string, param2: boolean) {
  // ...
}

Необязательные параметры

Вы также можете иметь необязательные параметры, добавив знак вопроса ? после имени параметра и перед двоеточием :.

function method(optionalValue?: string) {
  // ...
}

Необязательные параметры будут принимать пропущенные, undefined, или соответствующие типы. Но они не будут принимать null.

// @flow
function method(optionalValue?: string) {
  // ...
}

method();          // Works.
method(undefined); // Works.
method("string");  // Works.
// $ExpectError
method(null);      // Error!

Остаточные параметры

JavaScript также поддерживает остаточные параметры или параметры, которые собирают массив аргументов в конце списка параметров. Они имеют многоточие ... перед ними.

Вы также можете добавить аннотации типов для остаточных параметров, используя тот же синтаксис, но с Array.

function method(...args: Array<number>) {
  // ...
}

Вы можете передавать любое количество аргументов в остаточный параметр.

// @flow
function method(...args: Array<number>) {
  // ...
}

method();        // Works.
method(1);       // Works.
method(1, 2);    // Works.
method(1, 2, 3); // Works.

Примечание: Если вы добавляете аннотацию типа к остаточному параметру, она всегда должна явно быть типом Array.

Возвращаемые значения функций

Возвращаемые значения функций также могут добавить тип, используя двоеточие : после списка параметров.

function method(): number {
  // ...
}

Типы возвращаемых значений гарантируют, что каждая ветвь вашей функции возвращает тот же тип. Это предотвращает случайное отсутствие возвращаемого значения в определённых условиях.

// @flow
// $ExpectError
function method(): boolean {
  if (Math.random() > 0.5) {
    return true;
  }
}

Асинхронные функции неявно возвращают промис, поэтому тип возвращаемого значения должен всегда быть Promise.

// @flow
async function method(): Promise<number> {
  return 123;
}

Функция this

Каждая функция в JavaScript может вызываться со специальным контекстом, называемым this. Вы можете вызывать функцию с любым желаемым контекстом. Flow позволяет аннотировать тип этого контекста, добавив специальный параметр в начало списка параметров функции:

// @flow
function method<T>(this: { x: T }) : T {
  return this.x;
}

var num: number = method.call({x : 42});
var str: string = method.call({x : 42}); // error

Этот параметр не оказывает никакого влияния во время выполнения и удаляется вместе с типами, когда Flow преобразуется в JavaScript. При наличии параметр this всегда должен стоять в самом начале списка параметров функции и должен иметь аннотацию. Кроме того, стрелочные функции стрелочные функции не могут иметь аннотацию параметра this, так как эти функции привязывают свой параметр this в месте определения, а не в месте вызова.

Если явный параметр this не указан, Flow попытается вывести его на основе использования. Если this не упоминается в теле функции, Flow выведет mixed для своего параметра this.

Функции-предикаты

Иногда вам нужно будет перенести условие из оператора if в функцию:

function concat(a: ?string, b: ?string): string {
  if (a && b) {
    return a + b;
  }
  return '';
}

Однако Flow выведет ошибку в коде ниже:

function truthy(a, b): boolean {
  return a && b;
}

function concat(a: ?string, b: ?string): string {
  if (truthy(a, b)) {
    // $ExpectError
    return a + b;
  }
  return '';
}

Вы можете исправить это, сделав truthy функцией-предикатом, используя аннотацию %checks следующим образом:

function truthy(a, b): boolean %checks {
  return !!a && !!b;
}

function concat(a: ?string, b: ?string): string {
  if (truthy(a, b)) {
    return a + b;
  }
  return '';
}

Ограничения функций-предикатов

Тело этих функций-предикатов должно быть выражениями (т.е. объявления локальных переменных не поддерживаются). Но можно вызывать другие функции-предикаты внутри функции-предиката. Например:

function isString(y): %checks {
  return typeof y === "string";
}

function isNumber(y): %checks {
  return typeof y === "number";
}

function isNumberOrString(y): %checks {
  return isString(y) || isNumber(y);
}

function foo(x): string | number {
  if (isNumberOrString(x)) {
    return x + x;
  } else {
    return x.length; // no error, because Flow infers that x can only be an array
  }
}

foo('a');
foo(5);
foo([]);

Ещё одно ограничение — это диапазон предикатов, которые можно закодировать. Уточнения, которые поддерживаются в функции-предикате, должны напрямую относиться к значению, которое передаётся как аргумент соответствующему вызову.

Например, рассмотрим встроенное уточнение

declare var obj: { n?: number };

if (obj.n) {
  const n: number = obj.n;
}

Здесь Flow позволит вам уточнить obj.n от ?number до number. Обратите внимание, что уточнение здесь относится к свойству n объекта obj, а не к самому obj.

Если вы попытаетесь создать функцию-предикат

function bar(a): %checks {
  return a.n;
}

для кодирования того же условия, то следующее уточнение не сработает

if (bar(obj)) {
  // $ExpectError
  const n: number = obj.n;
}

Это связано с тем, что единственные уточнения, поддерживаемые через bar , относятся к самому obj.

Вызываемые объекты

Вызываемые объекты могут быть типизированы, например:

type CallableObj = {
  (number, number): number,
  bar: string
};

function add(x, y) {
  return x + y;
}

// $ExpectError
(add: CallableObj);

add.bar = "hello world";

(add: CallableObj);

Function Тип

ПРИМЕЧАНИЕ: Для нового кода предпочитайте any или (...args: Array<any>) => any. Function стал псевдонимом any и будет устаревать и удалён в будущей версии Flow.

Иногда полезно писать типы, которые принимают произвольные функции, для этого нужно написать () => mixed следующим образом:

function method(func: () => mixed) {
  // ...
}

Однако, если вам нужно отказаться от проверки типов и не хотите доходить до any, вы можете вместо этого использовать (...args: Array<any>) => any. (Обратите внимание, что any небезопасен и следует избегать). По историческим причинам ключевое слово Function всё ещё доступно.

Например, следующий код не сообщит об ошибках:

function method(func: (...args: Array<any>) => any) {
  func(1, 2);     // Works.
  func("1", "2"); // Works.
  func({}, []);   // Works.
}

method(function(a: number, b: number) {
  // ...
});

Точно так же и этот:

function method(obj: Function) {
  obj = 10;
}

method(function(a: number, b: number) {
  // ...
});

Следует придерживаться всех тех же правил, что и any при использовании Function.

© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/types/functions

Spec-Zone.ru

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