Вспомогательные типы
Flow предоставляет набор вспомогательных типов для работы с другими типами и создания новых типов.
Большинство вспомогательных типов Flow напрямую соответствуют типам TypeScript: Readonly, Pick, Omit, Record, Partial, Required, Exclude, Extract, NonNullable, Parameters, ReturnType, ConstructorParameters, InstanceType, Awaited, ThisParameterType, OmitThisParameter, NoInfer, Uppercase, Lowercase, Capitalize, Uncapitalize и keyof. Подробнее см. раздел Концепции TypeScript, которые можно напрямую перенести.
keyof T
Можно извлечь тип ключей из типа объекта. Обычно это будет объединение типов строковых литералов:
const countries = {
US: "United States",
IT: "Italy",
FR: "France"
};
type Country = keyof typeof countries;
const italy: Country = 'IT'; // Works
const nope: Country = 'nope'; // Error! В приведённом выше примере тип Country эквивалентен type Country = 'US' | 'IT' | 'FR', но Flow смог извлечь его из ключей countries.
Если вы хотите создать тип перечисления, для вашего случая может лучше подойти перечисление Flow.
Values<T>
Values<T> представляет собой тип объединения всех типов значений свойств типа объекта или элементов типа массива либо кортежа:
type Props = {
name: string,
age: number,
};
// The following two types are equivalent:
type PropValues = string | number;
type PropValues2 = Values<Props>;
const name: PropValues2 = 'Jon'; // Works
const age: PropValues2 = 42; // Works
const fn: PropValues2 = true; // Error! Для массивов и кортежей:
type Arr = Array<string>; type Str = Values<Arr>; //=> string 's' as Str; // OK type Tup = [1, 2]; type Num = Values<Tup>; //=> 1 | 2 1 as Num; // OK
Обратите внимание: применение Values к typeof литералу объекта или массива приведёт к типу более общему, чем можно было ожидать:
const obj = {
foo: 1,
bar: 2,
};
function acceptsValues(x: Values<typeof obj>) { /* ... */ }
acceptsValues(1); // Works
acceptsValues(3); // Works, because the type was interpreted as `number`. Это поведение изменится, если применить Object.freeze к выражению литерала объекта:
const obj = Object.freeze({
foo: 1,
bar: 2,
});
function acceptsValues(x: Values<typeof obj>) { /* ... */ }
acceptsValues(1); // Works
acceptsValues(3); // Error! Because the type was interpreted as `1 | 2`. Если вы хотите создать тип перечисления, для вашего случая может лучше подойти перечисление Flow.
В TypeScript нет вспомогательного типа Values<T>; его эквивалентом является индексированный доступ T[keyof T].
Readonly<T>
Readonly<T> — это тип, представляющий версию заданного типа объекта или типа кортежа T только для чтения. Тип объекта только для чтения — это тип объекта, все ключи которого доступны только для чтения. Аналогично, кортеж только для чтения — это кортеж, каждый элемент которого доступен только для чтения.
Это означает, что следующие записи эквивалентны:
type ReadOnlyObj = {
readonly key: number, // read-only field, marked by the `readonly` keyword
};
type ReadOnlyTuple = [readonly foo: number]; →
type ReadOnlyObj = Readonly<{
key: number,
}>;
type ReadOnlyTuple = Readonly<[number]>; Это полезно, если нужно использовать уже определённый тип объекта только для чтения, не переопределяя его вручную и не помечая каждый ключ как доступный только для чтения. Например:
type Props = {
name: string,
age: number,
};
type ReadOnlyProps = Readonly<Props>;
function render(props: ReadOnlyProps) {
const {name, age} = props; // Works
props.age = 42; // Error!
} Кроме того, другие вспомогательные типы, например распространение, могут удалять аннотации чтения и записи, поэтому Readonly<T> — удобный способ снова быстро сделать объект доступным только для чтения после работы с ним:
type Obj = {
readonly key: number,
};
type MappedObj = Readonly<{...Obj, foo: string}> // Still read-only Вспомогательный тип Readonly работает с типами объектов и кортежей. Чтобы сделать доступными только для чтения другие типы, можно воспользоваться одним из следующих вариантов:
-
Array<T>->ReadonlyArray<T> -
Set<T>->ReadonlySet<T> -
Map<K, V>->ReadonlyMap<K, V> -
WeakSet<T>->ReadonlyWeakSet<T> -
WeakMap<K, V>->ReadonlyWeakMap<K, V>
Partial<T>
Этот вспомогательный тип делает все именованные поля объекта или интерфейса необязательными, сохраняя при этом все остальные свойства объекта (например, точность и вариантность).
Он также делает все элементы типа кортежа необязательными.
Пример для объектов:
type Person = {
name: string,
age: number,
};
type PartialPerson = Partial<Person>;
// Above equivalent to `{name?: string, age?: number}`
const a: PartialPerson = {}; // OK
const b: PartialPerson = {name: 'George'}; // OK
const c: PartialPerson = {name: 'George', age: 123}; // OK
c as Person; // ERROR: `PartialPerson` is not a `Person` Для кортежей:
type AllRequired = [number, string]; [] as Partial<AllRequired>; // OK: like `[a?: number, b?: string]` now
Объект или кортеж типа T нельзя передать в Partial<T> из-за изменяемости. Это можно исправить, сделав объект доступным только для чтения:
type Person = {
name: string,
age: number,
};
const person: Person = {name: 'George', age: 123};
function noPerson(o: Partial<Person>) {
// Can mutate:
o.name = undefined;
}
noPerson(person); // Error!
function okPerson(o: Readonly<Partial<Person>>) {
// Can't mutate - it's read-only!
}
okPerson(person); // Works
Required<T>
Вспомогательный тип Required противоположен типу Partial: он делает все необязательные поля объекта или интерфейса обязательными.
Он также делает все элементы типа кортежа обязательными.
Пример для объектов:
type PartialPerson = {
name?: string,
age?: number,
};
type Person = Required<PartialPerson>;
// Above equivalent to `{name: string, age: number}`
const a: Person = {name: 'George', age: 123}; // OK
const b: Person = {age: 123}; // ERROR: missing `name` property Для кортежей:
type AllOptional = [a?: number, b?: string]; [] as Required<AllOptional>; // ERROR: like `[a: number, b: string]` now
Pick<O, Keys>
Этот вспомогательный тип позволяет создать тип объекта, используя подмножество полей другого типа объекта.
type O = {foo: number, bar: string, baz: boolean};
type FooAndBar = Pick<O, 'foo' | 'bar'>;
declare const fooAndBar: FooAndBar;
fooAndBar.baz; // error: baz is missing
fooAndBar.foo as number; // ok
fooAndBar.bar as string; // ok
Omit<O, Keys>
Этот вспомогательный тип позволяет создать тип объекта, исключив из другого типа объекта указанные поля.
type O = {foo: number, bar: string, baz: boolean};
type JustBaz= Omit<O, 'foo' | 'bar'>;
declare const justBaz: JustBaz;
justBaz.baz as boolean; // ok
justBaz.foo; // error: missing foo
justBaz.bar; // error: missing bar
Record<Keys, Type>
Этот вспомогательный тип позволяет создать тип объекта из объединения ключей, задав для каждого поля Type.
type NumberRecord = Record<'foo' | 'bar', number>; declare const numberRecord: NumberRecord; numberRecord.foo as number; // ok numberRecord.bar as number; // ok numberRecord.baz; // error
Обратите внимание: Record отличается от использования индексатора:
type NumberRecord = Record<'foo' | 'bar', number>;
type IndexedObject = {['foo' | 'bar']: number};
// Record uses explicit fields, which means they are all required
const rec: NumberRecord = {}; // error
// Indexers do not have this same requirement
const idx: IndexedObject = {}; // no error
Exclude<T, U>
Этот вспомогательный тип исключает все подтипы U из T.
type T = Exclude<1 | 2 | 3 | 4, 1 | 3>; // evaluates to 2 | 4 1 as T; // error 2 as T; // ok 3 as T; // error 4 as T; // ok
Extract<T, U>
Этот вспомогательный тип оставляет только подтипы U из T.
declare class Car {}
declare class Animal {}
declare class Dog extends Animal {}
declare class Cat extends Animal {}
type T = Extract<Car | Dog | Cat, Animal>; // evaluates to Dog | Cat
new Car() as T; // error
new Dog() as T; // ok
new Cat() as T; // ok
ReturnType<F>
Этот вспомогательный тип извлекает тип возвращаемого значения из заданного типа функции.
declare function f(s: string, n: number): boolean; type Bool = ReturnType<typeof f>; true as Bool; 1 as Bool; // Error: number is not boolean
Parameters<F>
Этот вспомогательный тип извлекает типы параметров из заданного типа функции и помещает их в тип кортежа.
declare function f(s: string, n: number): boolean; type Tuple = Parameters<typeof f>; // Evaluates to [string, number] 's' as Tuple[0]; 1 as Tuple[1]; false as Tuple[2]; // Error: tuple type only has two elements
ConstructorParameters<C> ≥0.317
Этот вспомогательный тип извлекает типы параметров конструктора класса и помещает их в тип кортежа. Передайте сам класс через typeof:
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
type PointArgs = ConstructorParameters<typeof Point>; // Evaluates to [number, number]
[1, 2] as PointArgs; // OK
'x' as PointArgs[0]; // Error: string is not number Если у класса нет явного конструктора, используется синтезированный конструктор по умолчанию, который не принимает аргументов:
class Empty {}
type EmptyArgs = ConstructorParameters<typeof Empty>; // Evaluates to []
[] as EmptyArgs; // OK
[1] as EmptyArgs; // Error: default constructor takes no arguments Класс без собственного конструктора наследует параметры конструктора родительского класса:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {} // no explicit constructor
type DogArgs = ConstructorParameters<typeof Dog>; // Inherited [string]
['Rex'] as DogArgs; // OK
[1] as DogArgs; // Error: number is not string
InstanceType<C> ≥0.317
Этот вспомогательный тип извлекает тип экземпляра, создаваемого конструктором класса. Передайте сам класс через typeof:
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
type UserInstance = InstanceType<typeof User>; // Evaluates to User
declare const u: UserInstance;
u.name as string; // OK
u as User; // OK Это полезно, если у вас есть тип класса (статическая сторона, например, из ограничения обобщённого типа или Class<T>), а нужен тип создаваемых им значений.
Для обобщённого класса без контекста вывода параметры типа класса принимают значения своих ограничений (по умолчанию — unknown):
class Box<T> {
value: T;
constructor(value: T) {
this.value = value;
}
}
type BoxInstance = InstanceType<typeof Box>; // Box<unknown>
declare const b: BoxInstance;
b.value as unknown; // OK
ThisParameterType<F>
Этот вспомогательный тип извлекает тип параметра this из заданного типа функции.
type T = ThisParameterType<(this: number, bar: string) => void>; // Evaluates to number '1' as T; // error 2 as T; // ok
OmitThisParameter<F>
Этот вспомогательный тип удаляет параметр this из заданного типа функции.
type HasThisParamFun = (this: number, bar: string) => void;
type NoThisParamFun = OmitThisParameter<HasThisParamFun> // Evaluates to (bar: string) => void
declare const hasThisParam: HasThisParamFun;
declare const noThisParam: NoThisParamFun;
hasThisParam(''); // error: global object is not number
noThisParam(''); // ok: no this type requirement
Awaited<T>
Awaited<T> снимает обёртку типа Promise, чтобы извлечь тип разрешённого значения. Если T не является Promise, он возвращает T без изменений.
type Response = Awaited<Promise<string>>; // Evaluates to string 'hello' as Response; // OK 42 as Response; // Error: number is not string
Это полезно, когда нужно обратиться к разрешённому типу Promise, не снимая обёртку вручную. Если T не является Promise, Awaited<T> просто вычисляется в T:
type A = Awaited<Promise<number>>; // number type B = Awaited<string>; // string (non-Promise types are returned as-is) 42 as A; // OK 'hello' as B; // OK 'hello' as A; // Error: string is not number 42 as B; // Error: number is not string
Можно объединить Awaited с другими вспомогательными типами, например ReturnType, чтобы извлечь разрешённый тип из возвращаемого значения асинхронной функции:
async function fetchUser(): Promise<{name: string}> {
return {name: 'George'};
}
type User = Awaited<ReturnType<typeof fetchUser>>;
const user: User = {name: 'George'}; // OK
const bad: User = 'not a user'; // Error
NonNullable<T>
NonNullable<T> преобразует тип T в тип, который не является maybe-типом. Иными словами, значения NonNullable<T> — это значения T, за исключением null и undefined.
type MaybeName = ?string; type Name = NonNullable<MaybeName>; 'Gabriel' as MaybeName; // Works null as MaybeName; // Works 'Gabriel' as Name; // Works null as Name; // Error! `null` can't be annotated as Name because Name is not a maybe type
NoInfer<T>
NoInfer<T> не позволяет выводить параметр типа по обёрнутой позиции. При выводе Flow параметра типа T обычно учитывает все позиции, в которых встречается T. Если обернуть позицию в NoInfer, она не будет учитываться при выводе, поэтому T определяется только по оставшимся (необёрнутым) позициям. Полученный тип по-прежнему является T — NoInfer влияет только на вывод, но не на сам тип.
Это полезно, когда у обобщённой функции несколько параметров используют один и тот же параметр типа, но нужно, чтобы на вывод влияли только некоторые из них. Без NoInfer Flow может расширить параметр типа, чтобы учесть все аргументы, и тогда несовпадения останутся незамеченными.
Рассмотрим функцию, в которой оба параметра используют один и тот же параметр типа T:
declare function assertEqual<T>(actual: T, expected: T): void;
assertEqual('hello', 42); // No error: T is inferred as string | number Поскольку оба аргумента участвуют в выводе, Flow расширяет T до string | number, и вызов выполняется успешно, хотя типы двух аргументов различаются.
Обёрнув параметр expected в NoInfer, можно добиться того, чтобы T выводился только по actual:
declare function assertEqual<T>(actual: T, expected: NoInfer<T>): void;
assertEqual('hello', 'world'); // OK
assertEqual('hello', 42); // Error! Теперь T выводится как string только по первому аргументу, и 42 не соответствует ему.
Вложенные типы
NoInfer также работает, если T встречается внутри более сложного типа. Все вхождения параметра типа внутри NoInfer исключаются из вывода:
declare function createSignal<T>(
initialValue: T,
defaultValue: NoInfer<T>,
): void;
createSignal('hello', 'default'); // OK
createSignal('hello', 42); // Error! Функции обратного вызова
Можно использовать NoInfer в позициях параметров функции обратного вызова, чтобы она не влияла на параметр типа, но при этом получала выведенный тип:
declare function filter<T>( items: Array<T>, predicate: (item: NoInfer<T>) => boolean, ): Array<T>; const numbers: Array<number> = [1, 2, 3]; filter(numbers, (item) => item > 1); // OK
Здесь T выводится как number по аргументу items. Функция обратного вызова predicate получает item типа number, но не участвует в выводе T.
$KeyMirror<O>
$KeyMirror<Obj> сопоставляет каждому свойству объекта тип ключа этого свойства. Например:
const obj = {
a: true,
b: 'foo'
};
declare function run<O extends {...}>(o: O): $KeyMirror<O>;
// newObj is of type {a: 'a', b: 'b'}
const newObj = run(obj);
newObj.a as 'a'; // Works
newObj.b as 'a'; // Error! String 'b' is incompatible with 'a'
Class<T>
Если тип T представляет экземпляры класса C, то тип Class<T> является типом самого класса C. Например:
class Store {}
class ExtendedStore extends Store {}
class Model {}
function makeStore(storeClass: Class<Store>) {
return new storeClass();
}
makeStore(Store) as Store;
makeStore(ExtendedStore) as Store;
makeStore(Model) as Model; // Error! Для классов с параметрами типа необходимо также указать этот параметр. Например:
class ParamStore<T> {
constructor(data: T) {}
}
function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {
return new storeClass(data);
}
makeParamStore(ParamStore, 1) as ParamStore<number>;
makeParamStore(ParamStore, 1) as ParamStore<boolean>; // Error! В TypeScript нет встроенного аналога Class<T>; обычно используют записи new (...args: any[]) => T или typeof C для конкретного класса.
$Exports<T>
Следующие варианты функционально эквивалентны
import typeof * as T from 'my-module';
type T = $Exports<'my-module'>;
Преимущество синтаксиса $Exports в том, что тип можно export в той же строке
export type T = $Exports<'my-module'>;
тогда как в противном случае в варианте import typeof пришлось бы экспортировать псевдоним
import typeof * as T from 'my-module'; export type MyModuleType = T;
StringPrefix и StringSuffix
Типы StringPrefix и StringSuffix представляют строки с заданным префиксом и суффиксом соответственно. Первый аргумент типа должен быть типом строкового литерала, задающим префикс или суффикс.
С помощью StringPrefix можно определить тип, принимающий строки, которые начинаются с data-:
type DataProp = StringPrefix<'data-'>; 'data-foo' as DataProp; // OK 'data-bar' as DataProp; // OK 'random string' as DataProp; // ERROR
С помощью StringSuffix можно определить тип, принимающий строки, которые заканчиваются на !:
type Exclaim = StringSuffix<'!'>; 'yay!' as Exclaim; // OK 'woo!' as Exclaim; // OK 'random string' as Exclaim; // ERROR
Их можно объединять с помощью типов-пересечений:
type CSSVar = StringPrefix<'var(--'> & StringSuffix<')'>; 'var(--color)' as CSSVar; // OK 'random string' as CSSVar; // ERROR
Оба вспомогательных типа принимают необязательный второй аргумент типа, задающий тип оставшейся части:
type Price = StringPrefix<'$', '1' | '2'>; '$1' as Price; // OK '$2' as Price; // OK '$999' as Price; // ERROR
Если он не указан, тип оставшейся части — просто string.
Uppercase<T>, Lowercase<T>, Capitalize<T>, Uncapitalize<T> ≥0.316
Эти четыре вспомогательных типа меняют регистр строкового типа:
-
Uppercase<T>переводит все символы в верхний регистр. -
Lowercase<T>переводит все символы в нижний регистр. -
Capitalize<T>переводит первый символ в верхний регистр, оставляя остальные без изменений. -
Uncapitalize<T>переводит первый символ в нижний регистр, оставляя остальные без изменений.
Аргумент должен быть типом строки. Если применить его к типу строкового литерала, результатом будет преобразованный литерал:
type Greeting = Uppercase<'hello'>; // 'HELLO' 'HELLO' as Greeting; // OK 'hello' as Greeting; // ERROR
type Tag = Lowercase<'WARNING'>; // 'warning' 'warning' as Tag; // OK 'WARNING' as Tag; // ERROR
Capitalize и Uncapitalize изменяют только первый символ:
type Name = Capitalize<'flow'>; // 'Flow' 'Flow' as Name; // OK 'flow' as Name; // ERROR 'FLOW' as Name; // ERROR
type Field = Uncapitalize<'FullName'>; // 'fullName' 'fullName' as Field; // OK 'FullName' as Field; // ERROR 'fullname' as Field; // ERROR (the tail is left untouched)
Они распределяются по типам объединения, преобразуя каждый элемент независимо:
type Direction = Uppercase<'north' | 'south'>; // 'NORTH' | 'SOUTH' 'NORTH' as Direction; // OK 'SOUTH' as Direction; // OK 'south' as Direction; // ERROR
Их можно вкладывать друг в друга, чтобы комбинировать преобразования:
type RoundTrip = Uncapitalize<Capitalize<'foo'>>; 'foo' as RoundTrip; // OK type Nested = Uppercase<Lowercase<'AbC'>>; 'ABC' as Nested; // OK
При применении к общему типу string результатом также будет string, но он будет представлять только строки, уже имеющие преобразованный вид. Конкретный литерал входит в этот тип, только если он уже имеет такой вид:
type Loud = Uppercase<string>; declare const x: Loud; x as string; // OK 'ANYTHING' as Loud; // OK 'anything' as Loud; // ERROR (not already upper case)
$Exact<T> (Не рекомендуется)
Избегайте $Exact<T>. Создайте точный тип объекта, а затем получайте из него неточные варианты с помощью распространения типа объекта.
$Exact делает неточный тип объекта точным:
type InexactUser = {name: string, ...};
type ExactUser = $Exact<InexactUser>;
const user = {name: 'John Wilkes Booth'};
// These will both be satisfied because they are equivalent:
const a: ExactUser = user;
const b: {name: string} = user; Предпочтительнее обратный подход: определить точный тип и распространить его в неточный.
type ExactUser = {name: string};
type InexactUser = {...ExactUser, ...};
const user = {name: 'John Wilkes Booth'};
const a: ExactUser = user; См. также
- Отображаемые типы — общий механизм преобразования типов объектов (обобщает многие вспомогательные типы)
- Условные типы — условные конструкции на уровне типов для более сложных преобразований
- Обобщённые типы — большинство вспомогательных типов являются обобщёнными; понимание обобщённых типов помогает эффективно их использовать
- Типы индексированного доступа — извлечение типов свойств из объектов и кортежей
-
Модернизация устаревшего синтаксиса Flow — переход от вспомогательных типов с префиксом
$, таких как$ObjMap,$PropertyTypeи$NonMaybeType, к их современным эквивалентам
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/types/utilities