Flow 混淆了 React 组件的类型 属性 回调
Flow confuses type of React component property callback
流程以某种方式混淆了 Picker 组件的 onValueChange
回调类型。这是我的设置:
我用 gender
定义了 State 类型作为其中的一个字符串:
type State = {
gender: string,
weight: number
// code omitted
};
在构造函数中初始化(这不是强制性的,不是吗?)
export default class CustomComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { gender: '', weight: 0 };
}
render() {
const { gender, weight } = this.state;
// code omitted
在选择器组件中,我想在 onValueChange
回调中设置性别状态,如下所示:
<Picker
style={styles.inputElement}
selectedValue={gender}
onValueChange={itemvalue => {
this.setState({ gender: itemvalue, isDirty: true });
}}
> // code omitted
但是 Flow 在 itemvalue 处显示错误:this.setState({ gender: itemvalue, isDirty: true })
Cannot call `this.setState` with object literal bound to `partialState` because number [1] is incompatible with string [2].Flow(InferError)
我也试过像这样输入定义项目值:
onValueChange={(itemvalue: string) => ....}
但这只是移动了新添加的错误 string
看起来流认为 itemvalue 是一个数字,但实际上它是一个字符串,对吗?
我们来看the definition of the onValueChange
event on the react-native Picker component,这里使用的组件:
onValueChange?: ?(itemValue: string | number, itemIndex: number) => mixed,
所以回调的第一个参数的类型是string
和number
、string | number
的并集。为什么是这样?好吧,如果我们看一下 Picker
的实现,我们会发现它是在 the option values will either be numbers or strings 的假设下构建的。这比只允许字符串更方便,因为在某些情况下您需要将字符串转换为数字。这里的缺点是每个用户(使用流类型的人)都被迫处理字符串和数字的情况,即使他们显然只在一种或另一种类型中工作。这种问题通常可以使用泛型来解决,但还没有出现过,所以我们不得不处理这两种情况。
那么让我们看看我们的处理程序:
itemvalue => {
this.setState({ gender: itemvalue, isDirty: true });
}
首先,让我们正确输入参数,以便清楚我们正在使用什么:
(itemvalue: string | number) => {
this.setState({ gender: itemvalue, isDirty: true });
}
这当然还是会报错,因为gender
中的状态是string
,但至少我们现在可以很清楚地看到为什么会这样了。
在我们的实现中,我们知道 itemvalue
将永远是一个字符串,否则某些东西在某些基本方面会变得非常糟糕。我们只提供字符串选项值,所以如果我们得到一个数字,那么就发生了非常糟糕的事情。这种具体的期望通常称为 "invariant." 在我们的例子中,itemvalue
是 string
.
的不变量
那我们该怎么办呢?通常当你有一个多类型的联合,你想处理联合中不同类型的各种情况时,you're going to use type refinement。所以像这样:
(itemvalue: string | number) => {
if (typeof itemvalue === 'string') {
(itemvalue: string); // do something with the string case
} else {
(itemvalue: number); // do something with the number case
}
}
因此,一种可能的解决方案是忽略数字大小写,因为它实际上永远不会出现:
(itemvalue: string | number) => {
if (typeof itemvalue === 'string') {
this.setState({ gender: itemvalue, isDirty: true });
}
}
这应该可以正常工作,但它只是忽略 number
情况有点奇怪,该输入来自某个地方,可能应该以某种方式处理。那么当违反不变量时会发生什么?也就是说,如果我们收到一个数字(应该不可能的事情),我们的回调实际上应该做什么?
一般情况下,当违反不变量时,常见的行为是抛出异常,异常也可以用于流程中的类型细化:
(itemvalue: string | number) => {
if (typeof itemvalue === 'number') throw new Error('itemvalue should be a string!');
this.setState({ gender: itemvalue, isDirty: true });
}
但实际上我们应该在一般情况下解决这个问题,当然我们会 运行 在一个复杂的代码库中不止一个地方解决这个问题。如果我们将其提取到某种断言函数中会怎样?
const assert = (check: boolean, message: string) => {
if (!check) {
throw new Error(message);
}
};
(itemvalue: string | number) => {
assert(typeof itemvalue === 'number', 'itemvalue should be a string!');
this.setState({ gender: itemvalue, isDirty: true }); // error!
}
现在我们的错误又回来了,因为流中的类型细化只在块范围内存在。一旦我们的 assert
函数 returns,itemvalue
就变回 string | number
。幸运的是,flow 有一个针对此类函数的内置特例,但作为一个名为 invariant
的函数。我们可以实现我们自己的 invariant
功能,但我们应该只使用像 this 这样的 npm 包。流程中存在这种特殊情况,因为它是在 React 代码库和更普遍的 Facebook 中处理不变量的方式。那么,使用 invariant
时我们的处理程序是什么样的?
import invariant from 'invariant';
// ...
(itemvalue: string | number) => {
invariant(typeof itemvalue === 'number', 'itemvalue should be a string!');
this.setState({ gender: itemvalue, isDirty: true });
}
流程以某种方式混淆了 Picker 组件的 onValueChange
回调类型。这是我的设置:
我用 gender
定义了 State 类型作为其中的一个字符串:
type State = {
gender: string,
weight: number
// code omitted
};
在构造函数中初始化(这不是强制性的,不是吗?)
export default class CustomComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { gender: '', weight: 0 };
}
render() {
const { gender, weight } = this.state;
// code omitted
在选择器组件中,我想在 onValueChange
回调中设置性别状态,如下所示:
<Picker
style={styles.inputElement}
selectedValue={gender}
onValueChange={itemvalue => {
this.setState({ gender: itemvalue, isDirty: true });
}}
> // code omitted
但是 Flow 在 itemvalue 处显示错误:this.setState({ gender: itemvalue, isDirty: true })
Cannot call `this.setState` with object literal bound to `partialState` because number [1] is incompatible with string [2].Flow(InferError)
我也试过像这样输入定义项目值:
onValueChange={(itemvalue: string) => ....}
但这只是移动了新添加的错误 string
看起来流认为 itemvalue 是一个数字,但实际上它是一个字符串,对吗?
我们来看the definition of the onValueChange
event on the react-native Picker component,这里使用的组件:
onValueChange?: ?(itemValue: string | number, itemIndex: number) => mixed,
所以回调的第一个参数的类型是string
和number
、string | number
的并集。为什么是这样?好吧,如果我们看一下 Picker
的实现,我们会发现它是在 the option values will either be numbers or strings 的假设下构建的。这比只允许字符串更方便,因为在某些情况下您需要将字符串转换为数字。这里的缺点是每个用户(使用流类型的人)都被迫处理字符串和数字的情况,即使他们显然只在一种或另一种类型中工作。这种问题通常可以使用泛型来解决,但还没有出现过,所以我们不得不处理这两种情况。
那么让我们看看我们的处理程序:
itemvalue => {
this.setState({ gender: itemvalue, isDirty: true });
}
首先,让我们正确输入参数,以便清楚我们正在使用什么:
(itemvalue: string | number) => {
this.setState({ gender: itemvalue, isDirty: true });
}
这当然还是会报错,因为gender
中的状态是string
,但至少我们现在可以很清楚地看到为什么会这样了。
在我们的实现中,我们知道 itemvalue
将永远是一个字符串,否则某些东西在某些基本方面会变得非常糟糕。我们只提供字符串选项值,所以如果我们得到一个数字,那么就发生了非常糟糕的事情。这种具体的期望通常称为 "invariant." 在我们的例子中,itemvalue
是 string
.
那我们该怎么办呢?通常当你有一个多类型的联合,你想处理联合中不同类型的各种情况时,you're going to use type refinement。所以像这样:
(itemvalue: string | number) => {
if (typeof itemvalue === 'string') {
(itemvalue: string); // do something with the string case
} else {
(itemvalue: number); // do something with the number case
}
}
因此,一种可能的解决方案是忽略数字大小写,因为它实际上永远不会出现:
(itemvalue: string | number) => {
if (typeof itemvalue === 'string') {
this.setState({ gender: itemvalue, isDirty: true });
}
}
这应该可以正常工作,但它只是忽略 number
情况有点奇怪,该输入来自某个地方,可能应该以某种方式处理。那么当违反不变量时会发生什么?也就是说,如果我们收到一个数字(应该不可能的事情),我们的回调实际上应该做什么?
一般情况下,当违反不变量时,常见的行为是抛出异常,异常也可以用于流程中的类型细化:
(itemvalue: string | number) => {
if (typeof itemvalue === 'number') throw new Error('itemvalue should be a string!');
this.setState({ gender: itemvalue, isDirty: true });
}
但实际上我们应该在一般情况下解决这个问题,当然我们会 运行 在一个复杂的代码库中不止一个地方解决这个问题。如果我们将其提取到某种断言函数中会怎样?
const assert = (check: boolean, message: string) => {
if (!check) {
throw new Error(message);
}
};
(itemvalue: string | number) => {
assert(typeof itemvalue === 'number', 'itemvalue should be a string!');
this.setState({ gender: itemvalue, isDirty: true }); // error!
}
现在我们的错误又回来了,因为流中的类型细化只在块范围内存在。一旦我们的 assert
函数 returns,itemvalue
就变回 string | number
。幸运的是,flow 有一个针对此类函数的内置特例,但作为一个名为 invariant
的函数。我们可以实现我们自己的 invariant
功能,但我们应该只使用像 this 这样的 npm 包。流程中存在这种特殊情况,因为它是在 React 代码库和更普遍的 Facebook 中处理不变量的方式。那么,使用 invariant
时我们的处理程序是什么样的?
import invariant from 'invariant';
// ...
(itemvalue: string | number) => {
invariant(typeof itemvalue === 'number', 'itemvalue should be a string!');
this.setState({ gender: itemvalue, isDirty: true });
}