缩小TypeScript与文字类型保护的不相交并集在函数中有效,但在React状态下无效



我有一个具有nextAction状态的reducer,它的工作方式类似于回调。NextAction是这样键入的:

type NextActionSave = {type: 'save'};
type NextActionSaveAndSet = {type: 'saveAndSet', name: TextFieldName, value: string};
type NextAction = NextActionSave | NextActionSaveAndSet;
interface EditRowState {
...
nextAction: NextAction | null;
}

在另一个文件中,我有一个关于state.nextAction.type的switch语句

import React from 'react';
import {EditRowState, EditRowAction, NextAction} from './reducer';
interface Props {
save: () => Promise<void>;
state: EditRowState;
dispatch: React.Dispatch<EditRowAction>;
}
const useNextAction = (props: Props) => {
React.useEffect(() => {
executeNextAction(props.state.nextAction);
}, [props.state.nextAction]);
const executeNextAction = (nextAction: NextAction | null) => {
if (nextAction === null) return;
switch(nextAction.type) {
case 'save':
props.save();
break;
case 'saveAndSet':
props.save().then(() => {
const {name, value} = nextAction;
props.dispatch({type: 'set', name, value, advance: true});
});
default: return;
}
};
};

这是有效的。这里是executeNextAction的TypeScript游乐场,显示了executeNextAction函数。

如果我在useEffect中移动executeNextAction的主体,那么nextAction直接来自状态,它会说,";类型"NextAction"上不存在属性"name";。对于value属性,我得到了相同的错误。下面是没有单独executeNextAction函数的代码。下面是我在TypeScript Playground中代码的简化版本,显示了错误。

import React from 'react';
import {EditRowState, EditRowAction} from './reducer';
interface Props {
save: () => Promise<void>;
state: EditRowState;
dispatch: React.Dispatch<EditRowAction>;
}
const useNextAction = (props: Props) => {
React.useEffect(() => {
if (props.state.nextAction === null) return;
switch(props.state.nextAction.type) {
case 'save':
props.save();
break;
case 'saveAndSet':
props.save().then(() => {
const {name, value} = props.state.nextAction;
props.dispatch({type: 'set', name, value, advance: true});
});
default: return;
}
}, [props.state.nextAction]);
};

如果我使用as NextActionSaveAndSet键入props.state.nextAction,它就会起作用。switch语句看起来像是一个文字类型保护,所以我不需要类型转换。我猜这就是为什么它在我将代码移动到executeNextAction函数的版本中工作的原因。使用nextAction作为函数参数和在状态中访问它有什么区别?如果我在某个地方添加了as const,我能在状态中使用它吗?

我无法正确解释为什么这样做,但我有两种不同的解决方案,它们不涉及断言。

  1. this.props.nextAction保存到变量中
const useNextAction = (props: Props) => {
React.useEffect(() => {
const {nextAction} = props.state;
if (nextAction === null) return;
switch(nextAction.type) {
case 'save':
props.save();
break;
case 'saveAndSet':
props.save().then(() => {
const {name, value} = nextAction;
props.dispatch({type: 'set', name, value, advance: true});
});
default: return;
}
}, [props.state.nextAction]);
};
  1. 在回调之外取消结构namevalue
const useNextAction = (props: Props) => {
React.useEffect(() => {
if (props.state.nextAction === null) return;
switch(props.state.nextAction.type) {
case 'save':
props.save();
break;
case 'saveAndSet':
const {name, value} = props.state.nextAction;
props.save().then(() => {
props.dispatch({type: 'set', name, value, advance: true});
});
default: return;
}
}, [props.state.nextAction]);
};

打字游戏场链接

最新更新