如何使用 Flow 描述 Immutable.js 地图形状
How to describe Immutable.js Map shape with Flow
我想使用 Immutable 的流类型定义来描述地图的形状。
您可以通过以下方式描述物体的形状:
const stateShape: {
id: number,
isActive: boolean
} = {
id: 123,
isActive: true
};
Immutable 的地图有类似的东西吗?
TL;DR;
不,但是使用 Records 可以让 Flow 对形状而不是类型进行类型检查。
长篇
正确的答案是:不,因为地图没有形状 (至少在 Flow 中是这样和不可变)。但是 Immutable 确实有一个带有形状的 "Maps" 类型。那将是 记录 。但是由于下面描述的原因(因为它不是严格相关的)Immutable.Record
的流 libdef 非常松散并且实际上不检查形状。
更好的 Record libdef
如果我们忽略直接访问 Record 属性的(可以说是不必要的)功能,我们可以创建一个更好的 libdef。看起来像这样:
declare class Record<T: Object> {
static <T: Object>(spec: T, name?: string): Record<T>;
get: <A>(key: $Keys<T>) => A;
set<A>(key: $Keys<T>, value: A): Record<T>;
remove(key: $Keys<T>): Record<T>;
}
通过这个声明我们可以定义记录的形状。 Here it is in action. But we still can't define the types of the actual values. Flow does define an undocumented $PropertyType<T, K>
type. Which takes an object T
and a string literal K
. To make $PropertyType
work in our case it would need to work for $Keys<T>
which is a string union type. A few weeks ago an issue was opened to make this happen. It can be found here.
地图与对象的区别
在流程上,它们是截然不同的。这是一张地图:
type MyMaps = { [key: string]: number }
实际密钥未知。 Flow 唯一知道的是,所有键都必须是字符串,所有值都必须是数字。另一方面,对象类型看起来像:
type MyObject = { a: string, x: boolean }
当创建或更改对象时,newObj
,类型为 MyObject
Flow,将检查 newObj.a
是一个字符串并且 newObj.x
是一个布尔值。
为什么现在的定义这么宽松
A Record 通过直接键访问公开每个 key/value 对。
type R = { a: string }
const r = Record({ a: 'Supa' })
r.a === r.get('a')
这需要 r
的类型定义是 Record<R>
和 R
的交集(不完全是,但足够接近)。所以:
(r: R & Record<R>)
这不起作用,因为 Flow 不支持与对象相交的类型。 Here's how that looks in action.
我想使用 Immutable 的流类型定义来描述地图的形状。
您可以通过以下方式描述物体的形状:
const stateShape: {
id: number,
isActive: boolean
} = {
id: 123,
isActive: true
};
Immutable 的地图有类似的东西吗?
TL;DR;
不,但是使用 Records 可以让 Flow 对形状而不是类型进行类型检查。
长篇
正确的答案是:不,因为地图没有形状 (至少在 Flow 中是这样和不可变)。但是 Immutable 确实有一个带有形状的 "Maps" 类型。那将是 记录 。但是由于下面描述的原因(因为它不是严格相关的)Immutable.Record
的流 libdef 非常松散并且实际上不检查形状。
更好的 Record libdef
如果我们忽略直接访问 Record 属性的(可以说是不必要的)功能,我们可以创建一个更好的 libdef。看起来像这样:
declare class Record<T: Object> {
static <T: Object>(spec: T, name?: string): Record<T>;
get: <A>(key: $Keys<T>) => A;
set<A>(key: $Keys<T>, value: A): Record<T>;
remove(key: $Keys<T>): Record<T>;
}
通过这个声明我们可以定义记录的形状。 Here it is in action. But we still can't define the types of the actual values. Flow does define an undocumented $PropertyType<T, K>
type. Which takes an object T
and a string literal K
. To make $PropertyType
work in our case it would need to work for $Keys<T>
which is a string union type. A few weeks ago an issue was opened to make this happen. It can be found here.
地图与对象的区别
在流程上,它们是截然不同的。这是一张地图:
type MyMaps = { [key: string]: number }
实际密钥未知。 Flow 唯一知道的是,所有键都必须是字符串,所有值都必须是数字。另一方面,对象类型看起来像:
type MyObject = { a: string, x: boolean }
当创建或更改对象时,newObj
,类型为 MyObject
Flow,将检查 newObj.a
是一个字符串并且 newObj.x
是一个布尔值。
为什么现在的定义这么宽松
A Record 通过直接键访问公开每个 key/value 对。
type R = { a: string }
const r = Record({ a: 'Supa' })
r.a === r.get('a')
这需要 r
的类型定义是 Record<R>
和 R
的交集(不完全是,但足够接近)。所以:
(r: R & Record<R>)
这不起作用,因为 Flow 不支持与对象相交的类型。 Here's how that looks in action.