Vue3和@types/node的类型不兼容问题

2022-11-23 14:47:15 浏览数 (1)

原创@前端司南

插播个广告:最近我在掘金社区有个专栏,主要涉及Vite,Vue3,TypeScript,业务组件库等关键词,目的是帮助读者掌握基于 Vite 构建现代组件库的核心方法。 链接:https://juejin.cn/column/7140103979697963045

最近有个新项目启动,主体内容与先前做的一个项目相似度很高,于是我准备拿这个旧项目作为模板简单改改,就可以启动新项目的开发了。

先说说现状,为了更好地拥抱云原生,部门内部的构建方案进行过升级,目前采用的是 Buildpacks 构建项目镜像,并且相关的服务器架构也做了调整,打镜像的 Runner 是部署在内网的,没有外网通道,也就是说安装 npm 依赖时必须从企业私有的 Nexus NPM 代理走

带来的问题就是:这个旧项目启动时还是采纳的旧版镜像构建方案,并不存在新版镜像构建方案带来的内网限制。而现在要基于这个旧项目开发新项目,对接的相关环境都是采纳的新方案,如果不将 npm registry 调整为私有的 Nexus NPM 代理,构建镜像这一步就没法走下去。

所以我就必须得先把 npm registry 调整一下,重新生成 lock 文件。

.npmrc修改成如下(已去掉敏感信息):

代码语言:javascript复制
registry=https://nexus.xxx.tech:8443/repository/npm-group/
always-auth=true
_auth=xxGxxxxxxxxxxyQ0xxlGxmc=

yarnrc也修改一下:

代码语言:javascript复制
registry "https://nexus.xxx.tech:8443/repository/npm-group/"

npm-group 包含了 npm-proxy 和 npm-hosted,从这里既可以下载通过 npm-proxy 代理过来的公开发行的 npm 包,也可以下载通过 npm-hosted 维护的企业内部私有的 npm 包。

这个项目用的是 Yarn,所以我接着删掉 yarn.lock 重新生成 lock 文件。

此时最好参照旧的 lock 文件,将关键依赖的版本号先锁住,再重新生成新的 lock 文件,防止在 ~, ^ 这种约束不强的规则下,最终安装的依赖版本号发生变化的情况。

生成完 lock 文件后,检查一下 dev 和 build 等场景,是不是基本上没什么问题。不出意外的话,就要出意外了!

很快,我就在一段 tsx 代码上遇到了这么一个报错:

代码语言:javascript复制
Type '() => void' is not assignable to type 'MouseEvent'.ts(2322)
runtime-dom.d.ts(1401, 3): The expected type comes from property 'onClick' which is declared here on type 'IntrinsicAttributes & AntdIconProps'

这个报错是从 runtime-dom.d.ts 中抛出来的,我第一反应就是看看@vue/runtime-dom这个包的版本是不是变了。

查了一下发现,@vue/runtime-dom确实是变了,从3.2.33变成了3.2.40

而这个变化是由于vue的版本号变化引起的,这是因为我的vue版本约束是~3.2.29,重新生成 lock 文件时会检查有没有较新的版本。

好,那我就锁vue的版本号,就定为原来生成的3.2.33版本。

代码语言:javascript复制
"vue": "3.2.33",

重新安装依赖,期待能解决问题。

但是这并没有解决问题,报错依然存在。于是我尝试去锁可能影响这个问题的一些依赖的版本号,包括typescript, @typescript-eslint/eslint-plugin, @vue/eslint-config-typescript 等等,最终都没有解决这个问题,搞了个把小时,emo了...

于是,我就尝试找问题源runtime-dom.d.ts有没有什么问题,先仔细观察下报错信息,

代码语言:javascript复制
The expected type comes from property 'onClick' which is declared here ...

既然你说 onClick 未声明,那我把 onClick 设置为可选的行不行?

代码语言:javascript复制
// node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts

export interface Events {
  // ...
  onClick?: MouseEvent;
  // ...
}

改了之后这个报错还真的消失了!!!

但是直接改 node_modules 里的代码肯定是不行的,离开自己的机器,就没法同步到这个修改。

借助 patch-package 可以实现修改 node_modules 后也能让其他人安装时同步到修改信息这种能力,但是我还不想这么做,能不能在项目中加一个d.ts,把这个interface修改一下呢?

考虑到interface有合并能力,我先尝试在global.d.ts中加同名的interface Events

代码语言:javascript复制
declare interface Events {
    onClick?: MouseEvent;
}

但是发现也并没有作用,因为runtime-dom.d.ts中用了export interface Events,这意味着Events接口是模块下的,我这样直接加在全局是合并不了的,那有没有办法合并模块下的interface呢?

我简单尝试了一下declare一个同名的module,然后加入一个interface Events,也不行,这样就直接覆盖了node_modules里的类型声明。

最后实在没办法了,我想到:既然覆盖了,那就全部覆盖吧!我干脆把node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts整个文件抄了出来,就改了onClick?: MouseEvent;这一行,效果确实可以,这个问题算是临时解决了。

福无双至,祸不单行,我就知道事情没这么简单!

继续往下测试,我又遇到一个报错:

这真的是搞心态啊,@ant-design/icons-vue不报错了,div又报错了。

收拾好心情,发现 VSCode 右下角出现了一个提示信息。

打开一看,终于找到了问题原因,这是 Volar 给出的提示。

原来是@types/node@18.8.4版本与vue@3.2.40版本不兼容,会造成模板中的 DOM event type 出错,解决的方法有两个:

  1. 降低@types/node版本至18.8.0
  2. 升级 Vue 的版本号至3.2.41,后面还备注了(如果已发行)。

于是,我去看了一下 Vue 的最新版本,发现 3.2.41 还没有发布,可能也正在解决这个问题吧!

那就选择降低@types/node的版本号吧,最终解决了这个问题,前面改的那个interface相关的代码也可以删了。

相关 issue 还是 2 天前提出的,说明这个问题还蛮新的。

为啥 Volar 不早点提示我呢?难道是因为我第一个报错是在.tsx中?估计是...

END

0 人点赞