NextJS Typescript - 守卫后对象可能未定义?
NextJS Typescript - Object is possibly undefined after guard?
好吧,我在这里被难住了。我有一个简单的 React 组件(刚刚开始)。我需要获取数据,所以我正在等待一些异步数据。这是我现在的解决方案:
import { Component } from "react";
import { Bible } from "./models";
interface State {
bible?: Bible;
}
export class RevApp extends Component<{}, State> {
state = {
bible: undefined,
};
componentDidMount() {
Bible.onReady().then((bible) => {
this.setState({
bible,
});
});
}
render() {
const { bible } = this.state;
//return <div>{bible ? bible.ls() : "Loading Bible..."}</div>;
if (bible) {
return <div>{bible.ls()}</div>;
} else {
return <div>Loading Bible...</div>;
}
}
}
出于某种原因,我得到 bible.ls()
(特别是圣经对象)编译器错误“对象可能未定义”。这没有意义!我试过放一个! (即 bible!.ls()
但随后我收到投诉“属性 'ls' 在类型 'never' 上不存在”。
真正令人头疼的是这个错误只出现在我的 linter 中,而不是我的编译器中。这一切都很好,除了当我投入生产时,我的代码需要在编译之前通过 linter。
谁能告诉我为什么会出现这种行为?
这是我的 tsconfig,以防它与问题有关(由 nextjs 生成):
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
打字稿版本“4.3.5”
节点版本“14.17.3”
interface State {
bible?: Bible;
}
这是因为你告诉它它可以是未定义的,所以打字稿正在接受它可能是未定义的。
interface State {
bible: Bible;
}
删除“?”如果你不想打字稿抱怨它。或者,如果它实际上可以是未定义的:
state = {
bible: undefined,
};
那么你需要确保在使用之前检查它:
return <div>{bible && bible.ls()}</div>
这里有点奇怪的是,您从“./models”导入 Bible,然后将其用作类型和方法 (onReady),我不确定'Bible' 可以同时是类型和 class。您可能使用的类型不正确,需要为该模型导入另一种类型。
无论好坏,当您有一个 class
那个 extends
一个超级 class 时,子 class 的覆盖属性 不会从 superclass 继承它们的类型。他们取而代之的是完全从初始化程序中推断出类型,就好像 superclass 不存在一样。所以 subclass 属性 很容易成为比 superclass 更窄 的 类型。请参阅 microsoft/TypeScript#10570 了解有关此问题的讨论、它如何使人们感到困惑以及他们如何在这里找不到更好的方法。
也就是说RevApp
的state
属性不继承Component<{}, State>['state']
的类型。相反,它是从 {bible: undefined}
初始值设定项中推断出来的,这会导致类型 {bible: undefined}
。所以 bible
属性 of state
属性 of RevApp
永远永远 undefined
:
export class RevApp extends Component<{}, State> {
state = {
bible: undefined,
};
/* RevApp.state: { bible: undefined;} <-- oops */
}
所以你不可能通过类型保护从 state
的类型中消除 undefined
(好吧,你 可以 ,也许编译器会缩小never
,但这也不是你想要的);它保持 undefined
:
const { bible } = this.state;
// const bible: undefined
if (bible) {
bible // <-- still undefined
}
大概您实际上希望 state
的 bible
属性 可能不是 undefined
。如果是这样,获得此行为的唯一方法是注释它明确为您希望它成为的类型:
export class RevApp extends Component<{}, State> {
state: State = { // 注释
圣经:未定义,
};
}
现在类型保护可以正常工作了; bible
的类型在检查之前是 Bible | undefined
,如果它是真实的则只是 Bible
:
render() {
const { bible } = this.state;
return <div>{bible ? bible.ls() : "Loading Bible..."}</div>; // okay
}
这里的问题是State可以undefined就不需要初始化了。所以删除这一行:
state = {
bible: undefined,
};
解决了问题 - 将圣经重新定义为严格未定义,而不是 Bible?
!
好吧,我在这里被难住了。我有一个简单的 React 组件(刚刚开始)。我需要获取数据,所以我正在等待一些异步数据。这是我现在的解决方案:
import { Component } from "react";
import { Bible } from "./models";
interface State {
bible?: Bible;
}
export class RevApp extends Component<{}, State> {
state = {
bible: undefined,
};
componentDidMount() {
Bible.onReady().then((bible) => {
this.setState({
bible,
});
});
}
render() {
const { bible } = this.state;
//return <div>{bible ? bible.ls() : "Loading Bible..."}</div>;
if (bible) {
return <div>{bible.ls()}</div>;
} else {
return <div>Loading Bible...</div>;
}
}
}
出于某种原因,我得到 bible.ls()
(特别是圣经对象)编译器错误“对象可能未定义”。这没有意义!我试过放一个! (即 bible!.ls()
但随后我收到投诉“属性 'ls' 在类型 'never' 上不存在”。
真正令人头疼的是这个错误只出现在我的 linter 中,而不是我的编译器中。这一切都很好,除了当我投入生产时,我的代码需要在编译之前通过 linter。
谁能告诉我为什么会出现这种行为?
这是我的 tsconfig,以防它与问题有关(由 nextjs 生成):
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
打字稿版本“4.3.5”
节点版本“14.17.3”
interface State {
bible?: Bible;
}
这是因为你告诉它它可以是未定义的,所以打字稿正在接受它可能是未定义的。
interface State {
bible: Bible;
}
删除“?”如果你不想打字稿抱怨它。或者,如果它实际上可以是未定义的:
state = {
bible: undefined,
};
那么你需要确保在使用之前检查它:
return <div>{bible && bible.ls()}</div>
这里有点奇怪的是,您从“./models”导入 Bible,然后将其用作类型和方法 (onReady),我不确定'Bible' 可以同时是类型和 class。您可能使用的类型不正确,需要为该模型导入另一种类型。
无论好坏,当您有一个 class
那个 extends
一个超级 class 时,子 class 的覆盖属性 不会从 superclass 继承它们的类型。他们取而代之的是完全从初始化程序中推断出类型,就好像 superclass 不存在一样。所以 subclass 属性 很容易成为比 superclass 更窄 的 类型。请参阅 microsoft/TypeScript#10570 了解有关此问题的讨论、它如何使人们感到困惑以及他们如何在这里找不到更好的方法。
也就是说RevApp
的state
属性不继承Component<{}, State>['state']
的类型。相反,它是从 {bible: undefined}
初始值设定项中推断出来的,这会导致类型 {bible: undefined}
。所以 bible
属性 of state
属性 of RevApp
永远永远 undefined
:
export class RevApp extends Component<{}, State> {
state = {
bible: undefined,
};
/* RevApp.state: { bible: undefined;} <-- oops */
}
所以你不可能通过类型保护从 state
的类型中消除 undefined
(好吧,你 可以 ,也许编译器会缩小never
,但这也不是你想要的);它保持 undefined
:
const { bible } = this.state;
// const bible: undefined
if (bible) {
bible // <-- still undefined
}
大概您实际上希望 state
的 bible
属性 可能不是 undefined
。如果是这样,获得此行为的唯一方法是注释它明确为您希望它成为的类型:
export class RevApp extends Component<{}, State> { state: State = { // 注释 圣经:未定义, }; }
现在类型保护可以正常工作了; bible
的类型在检查之前是 Bible | undefined
,如果它是真实的则只是 Bible
:
render() {
const { bible } = this.state;
return <div>{bible ? bible.ls() : "Loading Bible..."}</div>; // okay
}
这里的问题是State可以undefined就不需要初始化了。所以删除这一行:
state = {
bible: undefined,
};
解决了问题 - 将圣经重新定义为严格未定义,而不是 Bible?
!