Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用TypeScript并升级到React 18 #29

Open
flytam opened this issue Apr 23, 2022 · 0 comments
Open

使用TypeScript并升级到React 18 #29

flytam opened this issue Apr 23, 2022 · 0 comments
Labels
react New feature or request

Comments

@flytam
Copy link
Owner

flytam commented Apr 23, 2022

为了支持React 18,React类型定义进行了升级,其中包含了一些break change。本文将讲述在TypeScript中如何升级到React 18

React 18和Definitely Typed

在alpha和beta测试经历了相当长的一段时间后,React 18 于2022年3月29日正式发布。在第一个alpha版本发布的时候,TypeScript就提供了支持

这是通过Definitely Typed(一个社区维护的各种TypeScript类型定义的库)的类型定义实现的)来使用。感谢Sebastian Silbermann的贡献,他在React18的类型定义工作中投入了大量的精力

目前React 18已经发布并且React 18 的类型定义在 Sebastian 的pr合并后也进行了更新。许多项目会面临一些break change。本文章将介绍会产生哪些break change及如何解决

Definitely Typed和语义版本控制

开发者习惯于在使用的软件中进行语义版本控制。通常来说在主版本的修改是表明有重大更改的。这正是React从v17升级到v18所做的事

Definitely Typed是不支持语义版本控制的

这不是故意的。因为Definitely Typed特意将类型定义发布到npm的@types作用域下。例如,React的类型定义被发布到@types/react

需要注意的是,npm 建立在语义版本控制之上。为了使类型定义的使用更容易,类型定义包的版本将等同于它支持的 npm 包的版本。对于 react18.0.0,对应的类型定义是@types/react18.0.0

如果@types/react类型定义发生breaking change,则会发布新版本而不是增加主要或次要版本号

修改将仅应用于修订号。这样做是为了通过npm维护当前更简单的类型消费模型

React 18: 类型上的breaking change

综上所述,对于那些被广泛使用的类型定义包,都会尽量减少产生breaking change

顺便说一句,Definitely Typed自动化工具将类型定义分为三类: "深受大家喜爱(Well-liked by everyone)"、"流行(Popular)"和"关键(Critical)"。感谢Andrew Branch的分享。被广泛使用的React被认为是"关键的"

当Sebastian提交了一个pr来升级TypeScript的React类型定义时,就有机会来做一些重大的修改。这些修改可能并不都与React 18有直接关系但会修复React类型定义中长期存在的一些问题

Sebastian pr非常好,我建议你去看一下。以下是重大更改的摘要

  • 移除隐式children
  • 移除ReactFragment中的{}(related to 1.)
  • this.context变成unkown
  • Using noImplicitAny now enforces a type is supplied with useCallback
  • noImplicitAny应用到useCallback
  • 删除不推荐使用的类型与React官方保持一致

在上述修改中,移除隐式children是最具破坏性的。Sebastian专门写了一篇博客来解释其原因。他还写了一个codemod来有利于进行这个代码迁移

下面让我们开始将代码库的react升级到18吧!

升级

我将通过升级我阿姨的网站进行演示。这是一个简单的网站,升级的pr

首先在package.json中升级React

-    "react": "^17.0.0",
-    "react-dom": "^17.0.0",
+    "react": "^18.0.0",
+    "react-dom": "^18.0.0",

然后升级类型定义

-    "@types/react": "^17.0.0",
-    "@types/react-dom": "^17.0.0",
+    "@types/react": "^18.0.0",
+    "@types/react-dom": "^18.0.0",

升级的时候需要检查lock依赖(yarn.lock / package-lock.json等),确保只有18版本的@types/reactreact

现在依赖安装已完成,会看到以下报错

Property ‘children’ does not exist on type ‘LoadingProps’.ts(2339)

代码如下

interface LoadingProps {
  // 你会注意到这里没有 `children` 属性 - 这就是出现错误的原因
  noHeader?: boolean;
}
// if props.noHeader is true then this component returns just the icon and a message
// if props.noHeader is true then this component returns the same but wrapped in an h1
const Loading: React.FunctionComponent<LoadingProps> = (props) =>
  props.noHeader ? (
    <>
      <FontAwesomeIcon icon={faSnowflake} spin /> Loading {props.children} ...
    </>
  ) : (
    <h1 className="loader">
      <FontAwesomeIcon icon={faSnowflake} spin /> Loading {props.children} ...
    </h1>
  );

在这里看到的是去除隐式children的改动。在我们进行升级之前,所有React.ComponentReact.FunctionComponent都有一个children属性,它允许React用户在不声明children的情况下直接使用

升级18后就不一样了。如果有一个带有子组件,则必须显式声明这个组件的类型

在这个例子中,通过直接添加children属性的声明可以修复这个问题

interface LoadingProps {
  noHeader?: boolean;
  children: string;
}

但是,当可以让其他方式帮我们写代码的话,为什么还要写代码呢?

我们可以使用Sebastian开发的codemod来替代手动修改代码。使用它直接通过以下的命令就可以:

npx types-react-codemod preset-18 ./src

执行后,会看到如下提示:

选择a并让codemod运行。对于这个项目,有37个文件更新了。所有文件都需要进行相同的修改。在每种情况下,组件的props都被React.PropsWithChildren包起来。例如Loading组件如下

-const Loading: React.FunctionComponent<LoadingProps> = (props) =>
+const Loading: React.FunctionComponent<React.PropsWithChildren<LoadingProps>> = (props) =>

PropsWithChildren仅仅是将children属性添加,如下

type PropsWithChildren<P> = P & { children?: ReactNode | undefined };

这就解决了上面遇到的编译问题,没有类型问题报错了

总结

通过本文我们已经学习到React 18是如何出现类型的破坏性更改,并知道可以使用codemod快速进行升级

@flytam flytam added the react New feature or request label Apr 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
react New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant