我试图弄清楚为什么TypeScript报告我的条件总是错误的,因为在这种情况下Action.UP | Action.DOWN
和Action.LEFT
之间没有类型重叠(游乐场链接):
class Component<S> {
public state: S;
public render() {}
}
enum Action {
UP,
DOWN,
LEFT,
RIGHT,
}
interface State {
action: Action;
}
const initialAction: Action.UP | Action.DOWN = Action.UP;
class MyComponent extends Component<State> {
public state = {
action: initialAction,
}
public render() {
// Why is action not of type Action as declared in the interface?
const isLateral = this.state.action === Action.LEFT;
}
}
如果this.state.action
只有 Action.UP | Action.DOWN
类型的初始值,但在接口中声明为 Action
类型,为什么我不能将其与 Action.LEFT
进行比较?如果我将来可以将this.state.action
重新分配为 LEFT
或 RIGHT
,为什么条件总是假的?
回答"为什么?
为什么操作不是接口中声明的 Action 类型?
它属于不同的类型,因为子类正在重新定义 state
属性,并为其提供比父类更窄的类型。如果您通过设计这样做,那么您可以通过在 render()
中强制转换来访问父类的更广泛的接口。
const isLateral = (this as Component<State>).state.action === Action.LEFT;
推荐的方法
或者,按照 React 组件文档所说的操作:
。如果您的组件需要使用本地状态,请直接在构造函数中将初始状态分配给 this.state:
也就是说,不要重新定义父类的状态属性;而是使用 extend
提供的已定义的 state
属性。在构造函数中设置其初始值。
class MyComponent extends Component<State> {
constructor() {
super();
this.state = {
action: initialAction,
}
}
public render() {
const isLateral = this.state.action === Action.LEFT;
}
}
您必须像这样键入公共状态:
public state: State = {
action: initialAction,
}
否则 Typescript 会推断出state.action
与 initialAction
的类型相同。
回答您评论中的问题
(游乐场链接)
在设置为新属性时,重新定义了子类中state
的值和类型。
我的链接说明了这一点。我在父类中添加了一个名为 count
的新属性,类型为 number
。如果我在我的子类中将其定义为新属性,它会将其推断为 any
,因此可以在 render()
中设置为字符串。
但是,您可以看到我如何处理您原来的state
财产。为了使用父级的状态类型,可以在构造函数中调用super()
,然后定义this.state.action = initialAction;
。
这样,当您调用render()
时,它将在this.state.action
上查找Action
类型,而不是新定义的Action.UP | Action.DOWN
例子中,我们忘记了{}
const A: React.FC<IProps> = (first, second, ...rest)
因为必须const A: React.FC<IProps> = ({first, second, ...rest})
。这太糟糕:)