在组件的构造函数之外将组件的状态初始化为null
时,该状态在render函数中的类型为never
。
但是,在构造函数中初始化状态时,状态具有正确的类型。
根据关于堆栈初始化状态的两种方法(在babeled JS中)的大多数问题,这两种方法应该等效。但是,在Typescript中却不是。这是错误还是预期的行为?
import * as React from "react";
import * as ReactDOM from "react-dom";
interface Person {
name: string;
address: string;
}
interface Props {
items: Person[];
}
interface State {
selected: Person | null;
}
class PersonSelector extends React.Component<Props, State> {
// DOES NOT WORK:
state = {
selected: null
};
constructor(props: Props) {
super(props);
// WORKS:
// this.state = {
// selected: null
// };
}
handleClick = (item: Person) => {
this.setState({
selected: item
});
};
render() {
const { selected } = this.state;
let selectedLabel = <div>None selected</div>;
if (selected) {
selectedLabel = <div>You selected {selected.name}</div>;
}
return (
<div>
{selectedLabel}
<hr />
{this.props.items.map(item => (
<div onClick={() => this.handleClick(item)}>{item.name}</div>
))}
</div>
);
}
}
const people: Person[] = [
{ name: "asdf", address: "asdf asdf" },
{ name: "asdf2", address: "asdf asdf2" }
];
document.write('<div id="root"></div>');
ReactDOM.render(
<PersonSelector items={people} />,
document.getElementById("root")
);
这是CodeSandbox上的示例代码:https://codesandbox.io/s/10l73o4o9q
答案 0 :(得分:10)
根据StackOverflow上有关以下两种方法的大多数问题 初始化状态(在Babeled JS中),这两个方法应为 当量。但是,在Typescript中却不是。
它们在TypeScript中是不同的,因为在类主体(而不是在构造函数中)中分配state
会在state
中声明PersonSelector
,从而覆盖基类React.Component
中的声明。在TypeScript中,允许覆盖声明具有不同的,更严格的类型,并且与基类中相同属性的类型单向兼容。
在没有类型注释的情况下初始化时,此类型由值的类型确定:
class PersonSelector extends React.Component<Props, State> {
// DOES NOT WORK:
state = {
selected: null
};
您可以看到,state
的类型为{selected: null}
,正如预期的那样。在此代码中变成never
const { selected } = this.state;
let selectedLabel = <div>None selected</div>;
if (selected) {
由于在if
语句内部,使用selected
为selected
的信息缩小了true
的类型。 Null永远不能为真,因此类型变为never
。
如其他答案中所建议的那样,您可以在类主体中初始化时显式注释State
class PersonSelector extends React.Component<Props, State> {
state: State = {
selected: null
};
更新以阐明类主体中的初始化与在构造函数中进行分配的区别
在构造函数中设置state
时
constructor(props: Props) {
super(props);
this.state = {
selected: null
};
}
您正在将值分配给在基类中声明的state
属性。基类为React.Component<Props, State>
,其中的state
属性声明为State
类型,取自<Props, State>
中的第二个通用参数。
赋值不会更改属性的类型-不管分配的值是什么,它始终为State
。
当您在类主体中设置state
时,它不仅是分配-它是类属性的声明,并且每个声明都为声明的实体提供类型-通过类型注释显式或隐式地从初始值。即使该属性已经存在于基类中,也会发生这种键入。我在文档中找不到任何可以证实这一点的东西,但是有github issue准确地描述了这种行为,并确认有时它违背了开发人员的意图(到目前为止,还没有使用该语言实现的解决方案)。>
答案 1 :(得分:3)
您绝对可以在构造函数外部初始化状态,但是您需要确保键入状态,以便TypeScript可以协调初始值的类型和泛型的类型。试试:
class PersonSelector extends React.Component<Props, State> {
state: State = {
selected: null
};
// ...
}