NextJS Typescript - 保护后对象可能未定义?
Posted
技术标签:
【中文标题】NextJS Typescript - 保护后对象可能未定义?【英文标题】:NextJS Typescript - Object is possibly undefined after guard? 【发布时间】:2021-10-08 06:44:35 【问题描述】:好吧,我被难住了。我有一个简单的 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()
,但随后我收到一条抱怨“'never' 类型上不存在属性 'ls'”。
真正令人头疼的是,这个错误只出现在我的 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”
【问题讨论】:
如果您像state: State = bible: undefined ;
一样明确地注释state
属性,它会得到修复吗?如果您可以生成适合放入独立 IDE(如 The TypeScript Playground)的 minimal reproducible example,这将非常有帮助,其中唯一存在的问题就是您看到的问题。
【参考方案1】:
无论好坏,当您有一个 class
和 extends
超类时,子类的重写属性不会从超类继承它们的类型。相反,它们完全从初始化程序中获取类型,就好像超类不存在一样。因此,子类属性很容易成为比超类属性更窄 的类型。请参阅 microsoft/TypeScript#10570 了解有关此问题的讨论、它如何使人们感到困惑以及他们如何没有找到更好的办法。
这意味着RevApp
的state
属性不继承Component<, State>['state']
的类型。相反,它是从 bible: undefined
初始化程序推断出来的,这导致类型 bible: undefined
。所以RevApp
的state
属性的bible
属性永远是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 = // 带注释的 圣经:未定义, ;
现在类型保护可以正常工作了; bible
的类型在检查前是Bible | undefined
,如果是真的,则只是Bible
:
render()
const bible = this.state;
return <div>bible ? bible.ls() : "Loading Bible..."</div>; // okay
Playground link to code
【讨论】:
【参考方案2】:interface State
bible?: Bible;
这是因为你告诉它它可以是未定义的,所以打字稿正在接受它可能是未定义的。
interface State
bible: Bible;
去掉“?”如果你不想让打字稿抱怨它。或者,如果它实际上可以是未定义的:
state =
bible: undefined,
;
那么你需要确保在使用前检查它:
return <div>bible && bible.ls()</div>
这里有点奇怪的是,您从“./models”导入圣经,然后将其用作类型和方法(onReady),我不确定“圣经” ' 可以同时是类型和类。您可能使用的类型不正确,需要为该模型导入另一种类型。
【讨论】:
【参考方案3】:这里的问题是,如果 State 可以未定义,则不需要对其进行初始化。所以删除这一行:
state =
bible: undefined,
;
解决了问题 - 将圣经重新定义为严格未定义而不是 Bible?
!
【讨论】:
以上是关于NextJS Typescript - 保护后对象可能未定义?的主要内容,如果未能解决你的问题,请参考以下文章
在类型保护错误 TS2532 之后,Typescript 对象可能未定义
NextJS/Typescript/Apollo 错误;类型上不存在属性
NextJS _app.tsx Component 和 pageProps 应该是啥 TypeScript 类型?