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】:

无论好坏,当您有一个 classextends 超类时,子类的重写属性不会从超类继承它们的类型。相反,它们完全从初始化程序中获取类型,就好像超类不存在一样。因此,子类属性很容易成为比超类属性更窄 的类型。请参阅 microsoft/TypeScript#10570 了解有关此问题的讨论、它如何使人们感到困惑以及他们如何没有找到更好的办法。

这意味着RevAppstate 属性不继承Component&lt;, State&gt;['state'] 的类型。相反,它是从 bible: undefined 初始化程序推断出来的,这导致类型 bible: undefined。所以RevAppstate 属性的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


大概您实际上希望statebible 属性可能不是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 类型?

Typescript - 作为私有或受保护的对象

Nextjs 和 TypeScript 中的条件渲染不起作用

React + NextJS - 受保护的路线