使用文字类型保护缩小 TypeScript 不相交联合在函数中起作用,但在 React 状态下不起作用
Narrowing TypeScript disjoint union with literal type guard works in function, but not on React state
我有一个带有 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;
}
};
};
这行得通。 Here is a TypeScript Playground of the executeNextAction 显示 executeNextAction 函数。
如果我将 executeNextAction 的主体移动到 useEffect 中,以便 nextAction 直接来自状态,它说,“属性 'name' 在类型 'NextAction' 上不存在” .对于值 属性,我得到了同样的错误。下面是我没有单独的 executeNextAction 函数的代码。 Here is a simplified version of my code below in a TypeScript Playground showing the error.
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 语句充当 literal type guard,所以我不需要类型转换。我猜这就是为什么它在我将代码移动到 executeNextAction 函数的版本中起作用的原因。使用 nextAction 作为函数参数或在状态上访问它有什么区别?如果我在某个地方添加一个 as const
,我可以在状态上使用它吗?
我无法正确解释为什么会这样,但我有两个不涉及断言的不同解决方案。
- 将
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]);
};
- 在回调之外解构
name
和 value
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]);
};
我有一个带有 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;
}
};
};
这行得通。 Here is a TypeScript Playground of the executeNextAction 显示 executeNextAction 函数。
如果我将 executeNextAction 的主体移动到 useEffect 中,以便 nextAction 直接来自状态,它说,“属性 'name' 在类型 'NextAction' 上不存在” .对于值 属性,我得到了同样的错误。下面是我没有单独的 executeNextAction 函数的代码。 Here is a simplified version of my code below in a TypeScript Playground showing the error.
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 语句充当 literal type guard,所以我不需要类型转换。我猜这就是为什么它在我将代码移动到 executeNextAction 函数的版本中起作用的原因。使用 nextAction 作为函数参数或在状态上访问它有什么区别?如果我在某个地方添加一个 as const
,我可以在状态上使用它吗?
我无法正确解释为什么会这样,但我有两个不涉及断言的不同解决方案。
- 将
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]);
};
- 在回调之外解构
name
和value
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]);
};