实用类型
Flow 提供了一组实用类型来操作其他类型以创建新类型。
$Keys<T>
您可以从 对象类型 中提取键的类型。通常这将是 联合 的 字符串字面量 类型
1const countries = {2 US: "United States",3 IT: "Italy",4 FR: "France"5};6
7type Country = $Keys<typeof countries>;8
9const italy: Country = 'IT'; // Works10const nope: Country = 'nope'; // Error!
10:23-10:28: Cannot assign `'nope'` to `nope` because property `nope` is missing in object literal [1]. [prop-missing]
在上面的示例中,Country
的类型等效于 type Country = 'US' | 'IT' | 'FR'
,但 Flow 能够从 countries
的键中提取它。
如果您想创建枚举类型,Flow 枚举 可能更适合您的用例。
$Values<T>
$Values<T>
表示 对象类型 中所有可枚举属性的值类型的联合类型
1type Props = {2 name: string,3 age: number,4};5
6// The following two types are equivalent:7type PropValues = string | number;8type Prop$Values = $Values<Props>;9
10const name: Prop$Values = 'Jon'; // Works11const age: Prop$Values = 42; // Works12const fn: Prop$Values = true; // Error!
12:25-12:28: Cannot assign `true` to `fn` because: [incompatible-type] Either boolean [1] is incompatible with string [2]. Or boolean [1] is incompatible with number [3].
请注意,在 typeof
对象字面量上使用 $Values
将导致比您预期更通用的类型
1const obj = {2 foo: 1,3 bar: 2,4};5
6function acceptsValues(x: $Values<typeof obj>) { /* ... */ }7
8acceptsValues(1); // Works9acceptsValues(3); // Works, because the type was interpreted as `number`.
如果您在对象字面量表达式上使用 Object.freeze
,此行为将发生变化
1const obj = Object.freeze({2 foo: 1,3 bar: 2,4});5
6function acceptsValues(x: $Values<typeof obj>) { /* ... */ }7
8acceptsValues(1); // Works9acceptsValues(3); // Error! Because the type was interpreted as `1 | 2`.
9:15-9:15: Cannot call `acceptsValues` with `3` bound to `x` because number [1] is incompatible with literal union [2]. [incompatible-call]
如果您想创建枚举类型,Flow 枚举 可能更适合您的用例。
$ReadOnly<T>
$ReadOnly<T>
是一种类型,它表示给定 对象类型 或 元组类型 T
的只读版本(对元组的支持适用于 Flow ≥0.212)。只读对象类型是一种其所有键都是 只读 的对象类型。类似地,只读元组是每个元素都是 只读 的元组。
这意味着以下内容是等效的
1type ReadOnlyObj = {2 +key: number, // read-only field, marked by the `+` annotation3};4type ReadOnlyTuple = [+foo: number];
→
1type ReadOnlyObj = $ReadOnly<{2 key: number,3}>;4type ReadOnlyTuple = $ReadOnly<[number]>;
当您需要使用已定义的对象类型的只读版本时,这很有用,而无需手动重新定义和注释每个键为只读。例如
1type Props = {2 name: string,3 age: number,4};5
6type ReadOnlyProps = $ReadOnly<Props>;7
8function render(props: ReadOnlyProps) {9 const {name, age} = props; // Works10 props.age = 42; // Error! 11}
10:9-10:11: Cannot assign `42` to `props.age` because property `age` is not writable. [cannot-write]
此外,其他实用类型,例如 $ObjMap<T>
,可能会剥离任何读/写注释,因此 $ReadOnly<T>
是一种在对对象进行操作后快速使其再次变为只读的便捷方法
1type Obj = {2 +key: number,3};4
5type MappedObj = $ReadOnly<$ObjMap<Obj, <T>(T) => Array<T>>> // 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>
≥0.201
此实用程序将对象或接口的所有命名字段转换为可选字段,同时保留对象的所有其他属性(例如精确度、方差)。使用此实用程序代替 $Shape
。
从 Flow ≥0.212 开始,它还将元组类型的所有元素转换为 可选 的。
对象示例
1type Person = {2 name: string,3 age: number,4};5type PartialPerson = Partial<Person>;6// Above equivalent to `{name?: string, age?: number}`7
8const a: PartialPerson = {}; // OK9const b: PartialPerson = {name: 'George'}; // OK10const c: PartialPerson = {name: 'George', age: 123}; // OK11
12c as Person; // ERROR: `PersonDetails` is not a `Person` (unlike with `$Shape`)
12:1-12:1: Cannot cast `c` to `Person` because undefined [1] is incompatible with number [1] in property `age`. [incompatible-cast]12:1-12:1: Cannot cast `c` to `Person` because undefined [1] is incompatible with string [1] in property `name`. [incompatible-cast]
元组
1type AllRequired = [number, string];2[] as Partial<AllRequired>; // OK: like `[a?: number, b?: string]` now
由于可变性,类型为 T
的对象或元组不能提供给 Partial<T>
。您可以通过使对象 变为只读 来解决此问题
1type Person = {2 name: string,3 age: number,4};5
6const person: Person = {name: 'George', age: 123};7
8function noPerson(o: Partial<Person>) {9 // Can mutate:10 o.name = undefined;11}12noPerson(person); // Error! 13
14function okPerson(o: $ReadOnly<Partial<Person>>) {15 // Can't mutate - it's read-only!16}17okPerson(person); // Works
12:10-12:15: Cannot call `noPerson` with `person` bound to `o` because undefined [1] is incompatible with number [1] in property `age`. This property is invariantly typed. See https://flow.node.org.cn/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-call]12:10-12:15: Cannot call `noPerson` with `person` bound to `o` because undefined [1] is incompatible with string [1] in property `name`. This property is invariantly typed. See https://flow.node.org.cn/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-call]
注意:直到 Flow 版本 0.201,此实用程序类型名为 $Partial
。
Required<T>
≥0.201
Required
实用程序类型与 Partial
相反:它将对象或接口的所有可选字段转换为必需字段。
从 Flow ≥0.212 开始,它还将元组类型的所有元素转换为 必需 的。
对象示例
1type PartialPerson = {2 name?: string,3 age?: number,4};5type Person = Required<PartialPerson>;6// Above equivalent to `{name: string, age: number}`7
8const a: Person = {name: 'George', age: 123}; // OK9const b: Person = {age: 123}; // ERROR: missing `name` property
9:19-9:28: Cannot assign object literal to `b` because property `name` is missing in object literal [1] but exists in `PartialPerson` [2]. [prop-missing]
元组
1type AllOptional = [a?: number, b?: string];2[] as Required<AllOptional>; // ERROR: like `[a: number, b: string]` now
2:1-2:2: Cannot cast array literal to required of `AllOptional` because empty array literal [1] has 0 elements but `AllOptional` [2] has 2 elements. [invalid-tuple-arity]
ReturnType<F>
≥0.209
此实用程序类型从给定的函数类型中提取返回类型。
1declare function f(s: string, n: number): boolean;2type Bool = ReturnType<typeof f>;3true as Bool;41 as Bool; // Error: number is not boolean
4:1-4:1: Cannot cast `1` to `Bool` because number [1] is incompatible with boolean [2]. [incompatible-cast]
Parameters<F>
≥0.209
此实用程序类型将给定函数类型的参数类型提取到 元组类型 中。
1declare function f(s: string, n: number): boolean;2type Tuple = Parameters<typeof f>; // Evaluates to [string, number]3's' as Tuple[0];41 as Tuple[1];5false as Tuple[2]; // Error: tuple type only has two elements
5:16-5:16: Cannot access number literal `2` on `Tuple` because tuple type [1] only has 2 elements, so index 2 is out of bounds. [invalid-tuple-index]
Exclude<T, U>
≥0.209
此实用程序类型从 T
中排除 U
的所有子类型。
1type T = Exclude<1 | 2 | 3 | 4, 1 | 3>; // evaluates to 2 | 421 as T; // error 32 as T; // ok43 as T; // error 54 as T; // ok
2:1-2:1: Cannot cast `1` to `T` because number [1] is incompatible with literal union [2]. [incompatible-cast]4:1-4:1: Cannot cast `3` to `T` because number [1] is incompatible with literal union [2]. [incompatible-cast]
Extract<T, U>
≥0.209
此实用程序类型仅保留 T
中 U
的子类型。
1declare class Car {}2declare class Animal {}3declare class Dog extends Animal {}4declare class Cat extends Animal {}5type T = Extract<Car | Dog | Cat, Animal>; // evaluates to Dog | Cat6new Car() as T; // error 7new Dog() as T; // ok8new Cat() as T; // ok
6:1-6:9: Cannot cast `new Car()` to `T` because: [incompatible-cast] Either `Car` [1] is incompatible with `Dog` [2]. Or `Car` [1] is incompatible with `Cat` [3].
ThisParameterType<F>
≥0.209
此实用程序类型从给定的函数类型中提取 this
参数的类型。
1type T = ThisParameterType<(this: number, bar: string) => void>; // Evaluates to number2'1' as T; // error 32 as T; // ok
2:1-2:3: Cannot cast `'1'` to `T` because string [1] is incompatible with number [2]. [incompatible-cast]
OmitThisParameter<F>
≥0.209
此实用程序类型从给定的函数类型中删除 this
参数。
1type HasThisParamFun = (this: number, bar: string) => void;2type NoThisParamFun = OmitThisParameter<HasThisParamFun> // Evaluates to (bar: string) => void3declare const hasThisParam: HasThisParamFun;4declare const noThisParam: NoThisParamFun;5
6hasThisParam(''); // error: global object is not number 7noThisParam(''); // ok: no this type requirement
6:1-6:12: Cannot call `hasThisParam` because global object [1] is incompatible with number [2]. [incompatible-call]
Pick<O, Keys>
≥0.211
此实用程序类型允许您使用来自另一个对象类型的字段子集来生成对象类型。
1type O = {foo: number, bar: string, baz: boolean};2type FooAndBar = Pick<O, 'foo' | 'bar'>;3
4declare const fooAndBar: FooAndBar;5fooAndBar.baz; // error: baz is missing 6fooAndBar.foo as number; // ok7fooAndBar.bar as string; // ok
5:11-5:13: Cannot get `fooAndBar.baz` because property `baz` (did you mean `bar`?) is missing in `Pick` [1]. [prop-missing]
Omit<O, Keys>
≥0.211
此实用程序类型允许您通过从另一个对象类型中省略指定的字段来生成对象类型。
1type O = {foo: number, bar: string, baz: boolean};2type JustBaz= Omit<O, 'foo' | 'bar'>;3
4declare const justBaz: JustBaz;5justBaz.baz as boolean; // ok6justBaz.foo; // error: missing foo 7justBaz.bar; // error: missing bar
6:9-6:11: Cannot get `justBaz.foo` because property `foo` is missing in `Pick` [1]. [prop-missing]7:9-7:11: Cannot get `justBaz.bar` because property `bar` (did you mean `baz`?) is missing in `Pick` [1]. [prop-missing]
Record<Keys, Type>
≥0.211
此实用程序类型允许您从键的联合中生成对象类型,每个字段的 Type
都是给定的。
1type NumberRecord = Record<'foo' | 'bar', number>;2declare const numberRecord: NumberRecord;3numberRecord.foo as number; // ok4numberRecord.bar as number; // ok5numberRecord.baz; // error
5:14-5:16: Cannot get `numberRecord.baz` because property `baz` (did you mean `bar`?) is missing in `Record` [1]. [prop-missing]
请注意,Record
与使用索引器不同
1type NumberRecord = Record<'foo' | 'bar', number>;2type IndexedObject = {['foo' | 'bar']: number};3
4// Record uses explicit fields, which means they are all required5const rec: Record = {}; // error 6// Indexers do not have this same requirement7const idx: IndexedObject = {}; // no error
5:12-5:17: Cannot use `Record` [1] without 2 type arguments. [missing-type-arg]
$Exact<T>
您可以使用 $Exact
使 非精确对象类型 变为精确
1type InexactUser = {name: string, ...};2type ExactUser = $Exact<InexactUser>;3
4const user = {name: 'John Wilkes Booth'};5// These will both be satisfied because they are equivalent:6const a: ExactUser = user;7const b: {name: string} = user;
这是一个要避免的实用程序类型,因为从精确对象类型开始并使用 对象类型扩展 使其变为非精确(如果您希望具有一个对象类型的非精确和精确变体)更清晰、更简洁
1type ExactUser = {name: string};2type InexactUser = {...ExactUser, ...};3
4const user = {name: 'John Wilkes Booth'};5const a: ExactUser = user;
$Diff<A, B>
顾名思义,$Diff<A, B>
是表示 A
和 B
的集合差的类型,即 A \ B
,其中 A
和 B
都是 对象类型。以下是一个示例
1type Props = {name: string, age: number, ...};2type DefaultProps = {age: number};3type RequiredProps = $Diff<Props, DefaultProps>;4
5function setProps(props: RequiredProps) {6 // ...7}8
9setProps({name: 'foo'}); // Works10setProps({name: 'foo', age: 42, baz: false}); // Works, you can pass extra props too11setProps({age: 42}); // Error! `name` is required
11:10-11:18: Cannot call `setProps` with object literal bound to `props` because property `name` is missing in object literal [1] but exists in `Props` [2]. [prop-missing]
您可能已经注意到,该示例并非随机的。$Diff
正是 React 定义文件用来定义 React 组件接受的道具类型的类型。
请注意,如果要从中删除属性的对象没有要删除的属性,即如果 B
具有 A
中不存在的键,则 $Diff<A, B>
会出错
1type Props = {name: string, age: number};2type DefaultProps = {age: number, other: string};3type RequiredProps = $Diff<Props, DefaultProps>; // Error! 4
5function setProps(props: RequiredProps) {6 props.name;7 // ...8}
3:28-3:32: Cannot instantiate `$Diff` because undefined property `other` [1] is incompatible with string [2]. [incompatible-type]
作为解决方法,您可以将 A
中不存在的属性指定为可选。例如
1type A = $Diff<{}, {nope: number}>; // Error! 2type B = $Diff<{}, {nope: number | void}>; // Works3
4const a: A = {};5const b: B = {};
1:16-1:17: Cannot instantiate `$Diff` because undefined property `nope` [1] is incompatible with number [2]. [incompatible-type]
$Rest<A, B>
$Rest<A, B>
是表示运行时对象剩余操作的类型,例如:const {foo, ...rest} = obj
,其中 A
和 B
都是 对象类型。此操作产生的类型将是一个对象类型,其中包含 A
的自身属性,这些属性不是 B
中的自身属性。在 Flow 中,我们将 精确对象类型 上的所有属性视为 自身。对于非精确对象,属性可能是也可能不是自身。
例如
1type Props = {name: string, age: number};2
3const props: Props = {name: 'Jon', age: 42};4const {age, ...otherProps} = props;5otherProps as $Rest<Props, {age: number}>;6otherProps.age; // Error!
6:12-6:14: Cannot get `otherProps.age` because property `age` is missing in rest of object pattern [1]. [prop-missing]
与 $Diff<A, B>
的主要区别在于,$Rest<A, B>
旨在表示真正的运行时剩余操作,这意味着在 $Rest<A, B>
中对精确对象类型的处理方式不同。例如,$Rest<{n: number}, {...}>
将导致 {n?: number}
,因为非精确空对象可能具有 n
属性,而 $Diff<{n: number}, {...}>
将导致 {n: number}
。
$NonMaybeType<T>
$NonMaybeType<T>
将类型 T
转换为非 也许类型。换句话说,$NonMaybeType<T>
的值是 T
的值,除了 null
和 undefined
。
1type MaybeName = ?string;2type Name = $NonMaybeType<MaybeName>;3
4'Gabriel' as MaybeName; // Works5null as MaybeName; // Works6'Gabriel' as Name; // Works7null as Name; // Error! `null` can't be annotated as Name because Name is not a maybe type
7:1-7:4: Cannot cast `null` to `Name` because null [1] is incompatible with string [2]. [incompatible-cast]
$KeyMirror<O>
$KeyMirror<Obj>
是 $ObjMapi<Obj, F>
的特例,当 F
是恒等函数类型时,即 <K>(K) => K
。换句话说,它将对象的每个属性映射到属性键的类型。您可以编写 $KeyMirror<Obj>
,而不是编写 $ObjMapi<Obj, <K>(K) => K>
。例如
1const obj = {2 a: true,3 b: 'foo'4};5
6declare function run<O: {...}>(o: O): $KeyMirror<O>;7
8// newObj is of type {a: 'a', b: 'b'}9const newObj = run(obj);10
11newObj.a as 'a'; // Works12newObj.b as 'a'; // Error! String 'b' is incompatible with 'a'
12:1-12:8: Cannot cast `newObj.b` to string literal `a` because string literal `b` [1] is incompatible with string literal `a` [2]. [incompatible-cast]
提示:如果可能,优先使用 $KeyMirror
而不是 $ObjMapi
来修复某些类型的 [invalid-exported-annotation]
错误。
$TupleMap<T, F>
$TupleMap<T, F>
接受一个可迭代类型 T
(例如:Tuple
或 Array
)和一个 函数类型 F
,并返回通过使用提供的函数类型 F
映射可迭代类型中每个值的类型获得的可迭代类型。这类似于 JavaScript 函数 map
。
延续我们之前在 $ObjMap<T>
中的示例,假设 run
函数接受一个函数数组而不是对象,并对它们进行映射,返回一个包含函数调用结果的数组。我们可以这样注释它的返回值类型
1// Function type that takes a `() => V` and returns a `V` (its return type)2type ExtractReturnType = <V>(() => V) => V3
4function run<A, I: Array<() => A>>(iter: I): $TupleMap<I, ExtractReturnType> {5 return iter.map(fn => fn());6}7
8const arr = [() => 'foo', () => 'bar'];9run(arr)[0] as string; // Works10run(arr)[1] as string; // Works11run(arr)[1] as boolean; // Error!
11:1-11:11: Cannot cast `run(...)[1]` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]
Class<T>
给定一个类型 T
,它代表类 C
的实例,类型 Class<T>
就是类 C
的类型。例如
1class Store {}2class ExtendedStore extends Store {}3class Model {}4
5function makeStore(storeClass: Class<Store>) {6 return new storeClass();7}8
9makeStore(Store) as Store;10makeStore(ExtendedStore) as Store;11makeStore(Model) as Model; // Error!
11:1-11:16: Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. [incompatible-cast]11:11-11:15: Cannot call `makeStore` with `Model` bound to `storeClass` because `Model` [1] is incompatible with `Store` [2]. [incompatible-call]
对于接受类型参数的类,您也必须提供参数。例如
1class ParamStore<T> {2 constructor(data: T) {}3}4
5function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {6 return new storeClass(data);7}8makeParamStore(ParamStore, 1) as ParamStore<number>;9makeParamStore(ParamStore, 1) as ParamStore<boolean>; // Error!
9:28-9:28: Cannot call `makeParamStore` with `1` bound to `data` because number [1] is incompatible with boolean [2]. [incompatible-call]
$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;
已弃用的实用程序类型
$PropertyType<T, k>
警告: 从 Flow 版本 0.155 开始,$PropertyType
已被弃用,将在 Flow 的未来版本中删除。
$PropertyType<T, 'k'>
等效于 T['k']
索引访问类型。
$ElementType<T, K>
警告: 从 Flow 版本 0.155 开始,$ElementType
已被弃用,将在 Flow 的未来版本中删除。
$ElementType<T, K>
等效于 T[K]
索引访问类型。
$Partial
≤0.202
以前是 Partial 的别名。在版本 0.203 中删除了支持。
$Shape<T>
注意:已弃用! 此实用程序不安全 - 请使用上面记录的 Partial
来使对象的所有字段可选。
类型为 $Shape<T>
的变量(其中 T
是某种对象类型)可以被赋值为包含 T
中包含的属性子集的对象 o
。对于 T
的每个属性 p: S
,o
中 p
的潜在绑定的类型必须与 S
兼容。
例如
1type Person = {2 age: number,3 name: string,4};5// $FlowIgnore[deprecated-utility]6type PersonDetails = $Shape<Person>;7
8const person1: Person = {age: 28}; // ERROR: missing `name` 9const person2: Person = {name: 'a'}; // ERROR: missing `age` 10const person3: PersonDetails = {age: 28}; // OK11const person4: PersonDetails = {name: 'a'}; // OK12const person5: PersonDetails = {age: 28, name: 'a'}; // OK13const person6: PersonDetails = {age: '28'}; // ERROR: string is incompatible with number
8:25-8:33: Cannot assign object literal to `person1` because property `name` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]9:25-9:35: Cannot assign object literal to `person2` because property `age` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]
注意:$Shape<T>
不 等效于将所有字段标记为可选的 T
。特别是,Flow 不合理地允许 $Shape<T>
在几种情况下用作 T
。例如在
const personShape: PersonDetails = {age: 28};
personShape as Person;
Flow 将不合理地允许此最后的强制转换成功。
它在某些情况下也不等效于自身
function f<T>(input: $Shape<T>): $Shape<T> {
return input; // ERROR: `T` is incompatible with `$Shape` of `T`
}
此实用程序类型已弃用,将在将来删除 - 请改用 Partial
。
$Call<F, T...>
注意:已弃用! 此实用程序从 Flow 版本 0.208 开始已弃用 - 请改用 条件类型 或 索引访问类型 来提取类型。
$Call<F, T...>
是一种类型,它表示使用 0 个或多个参数 T...
调用给定 函数类型 F
的结果。这类似于在运行时调用函数(或更确切地说,类似于调用 Function.prototype.call
),但在类型级别;这意味着函数类型调用在静态发生,即不在运行时发生。
让我们看几个例子
1// Takes an object type, returns the type of its `prop` key2type ExtractPropType = <T>({prop: T, ...}) => T;3type Obj = {prop: number};4type PropType = $Call<ExtractPropType, Obj>; // Call `ExtractPropType` with `Obj` as an argument5type Nope = $Call<ExtractPropType, {nope: number}>; // Error! Argument doesn't match `Obj`. 6
75 as PropType; // Works8true as PropType; // Error! PropType is a number
5:36-5:49: Cannot instantiate `$Call` because property `prop` is missing in object type [1] but exists in object type [2] in the first argument. [prop-missing]8:1-8:4: Cannot cast `true` to `PropType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
1// Takes a function type, and returns its return type2type ExtractReturnType = <R>(() => R) => R;3type Fn = () => number;4type ReturnType = $Call<ExtractReturnType, Fn>;5
65 as ReturnType; // Works7true as ReturnType; // Error! ReturnType is a number
7:1-7:4: Cannot cast `true` to `ReturnType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
$Call
非常强大,因为它允许您在类型层进行调用,而您原本需要在运行时进行。类型层调用在静态发生,并在运行时被擦除。
1// Getting return types:2function getFirstValue<V>(map: Map<string, V>): ?V {3 for (const [key, value] of map.entries()) {4 return value;5 }6 return null;7}8
9// Using $Call, we can get the actual return type of the function above:10type Value = $Call<typeof getFirstValue, Map<string, number>>;11
125 as Value;13true as Value; // Error! Value is a `number` 14
15// We could generalize it further:16type GetMapValue<M> =17 $Call<typeof getFirstValue, M>;18
195 as GetMapValue<Map<string, number>>;20true as GetMapValue<Map<string, boolean>>;21true as GetMapValue<Map<string, number>>; // Error! value is a `number`
13:1-13:4: Cannot cast `true` to `Value` because boolean [1] is incompatible with number [2]. [incompatible-cast]21:1-21:4: Cannot cast `true` to `GetMapValue` because boolean [1] is incompatible with number [2]. [incompatible-cast]
$ObjMap<T, F>
注意:已弃用! 此实用程序从 Flow 版本 0.211 开始已弃用 - 请改用 映射类型。
ObjMap<T, F>
接受一个 对象类型 T
和一个 函数类型 F
,并返回通过使用提供的函数类型 F
映射对象中每个值的类型获得的对象类型。换句话说,$ObjMap
将 调用(在类型级别)给定的函数类型 F
,用于 T
中的每个属性值类型,并返回从这些调用中获得的结果对象类型。
让我们看一个例子。假设您有一个名为 run
的函数,它接受一个 thunk 对象(形式为 () => A
的函数)作为输入
1function run<O: {[key: string]: (...$ReadOnlyArray<mixed>) => mixed}>(o: O): $FlowFixMe {2 return Object.keys(o).reduce<{[string]: (...$ReadOnlyArray<mixed>) => mixed}>(3 (acc, k) => ({...acc, [(k: string)]: o[k]()}),4 {},5 );6}
该函数的目的是运行所有 thunk 并返回一个由值组成的对象。此函数的返回值类型是什么?
键是相同的,但值具有不同的类型,即每个函数的返回值类型。在值级别(函数的实现)上,我们本质上是在对对象进行映射以生成键的新值。如何在类型级别表达这一点?
这就是 ObjMap<T, F>
派上用场的地方
1// let's write a function type that takes a `() => V` and returns a `V` (its return type)2type ExtractReturnType = <V>(() => V) => V;3
4declare function run<O: {[key: string]: (...$ReadOnlyArray<mixed>) => mixed}>(o: O): $ObjMap<O, ExtractReturnType>;5
6const o = {7 a: () => true,8 b: () => 'foo'9};10
11run(o).a as boolean; // Works12run(o).b as string; // Works13run(o).b as boolean; // Error! `b` is a string 14run(o).c; // Error! `c` was not in the original object
13:1-13:8: Cannot cast `run(...).b` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]14:8-14:8: Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]
这对于表达操作对象值的函数的返回值类型非常有用。您可以使用类似的方法(例如)来提供 bluebird 的 Promise.props
函数的返回值类型,它类似于 Promise.all
,但接受一个对象作为输入。
这是一个可能的函数声明,它与我们的第一个示例非常相似
1declare function props<A, O: {[key: string]: A}>(promises: O): Promise<$ObjMap<O, typeof $await>>;2
3const promises = {a: Promise.resolve(42)};4props(promises).then(o => {5 o.a as 42; // Works6 o.a as 43; // Error! Flow knows it's 42 7});
6:3-6:5: Cannot cast `o.a` to number literal `43` because number [1] is incompatible with number literal `43` [2]. [incompatible-cast]
$ObjMapi<T, F>
注意:已弃用! 此实用程序从 Flow 版本 0.211 开始已弃用 - 请改用 映射类型。
ObjMapi<T, F>
类似于 ObjMap<T, F>
。区别在于函数类型 F
将 被调用,并包含对象类型 T
元素的键和值类型,而不仅仅是值类型。例如
1const o = {2 a: () => true,3 b: () => 'foo'4};5
6type ExtractReturnObjectType = <K, V>(K, () => V) => { k: K, v: V };7
8declare function run<O: {...}>(o: O): $ObjMapi<O, ExtractReturnObjectType>;9
10run(o).a as {k: 'a', v: boolean}; // Works11run(o).b as {k: 'b', v: string}; // Works12run(o).a as {k: 'b', v: boolean}; // Error! `a.k` is "a" 13run(o).b as {k: 'b', v: number}; // Error! `b.v` is a string 14run(o).c; // Error! `c` was not in the original object
12:1-12:8: Cannot cast `run(...).a` to object type because string literal `a` [1] is incompatible with string literal `b` [2] in property `k`. [incompatible-cast]13:1-13:8: Cannot cast `run(...).b` to object type because string [1] is incompatible with number [2] in property `v`. [incompatible-cast]14:8-14:8: Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]
$ObjMapConst<O, T>
注意:已弃用! 此实用程序从 Flow 版本 0.211 开始已弃用 - 请改用 映射类型。
$ObjMapConst<Obj, T>
是 $ObjMap<Obj, F>
的特例,当 F
是一个常量函数类型时,例如 () => T
。您可以编写 $ObjMapConst<Obj, T>
而不是 $ObjMap<Obj, () => T>
。例如
1const obj = {2 a: true,3 b: 'foo'4};5
6declare function run<O: {...}>(o: O): $ObjMapConst<O, number>;7
8// newObj is of type {a: number, b: number}9const newObj = run(obj);10
11newObj.a as number; // Works12newObj.b as string; // Error! Property `b` is a number
12:1-12:8: Cannot cast `newObj.b` to string because number [1] is incompatible with string [2]. [incompatible-cast]
提示:如果可能,请优先使用 $ObjMapConst
而不是 $ObjMap
,以修复某些类型的 [invalid-exported-annotation]
错误。