正确引入ts的类型
引入ts类型
在我们刚开始学习ts的时候,常常是会创建一个types.ts
文件,然后在里面声明一个类型并导出:
export interface User {
name: string;
age: number;
}
使用的时候通过import导入:
import { User } from "./types";
const user:User = {
name: "a",
age: 18
};
很高兴你已经学会了如何引入ts类型,教程结束!
当然,这是骗你的,但是这种用法确实是最开始ts所提供的,你不能说它错了,我们需要思考一下ts是如何被转为js,就会发现一些问题。
在大部分的项目配置中,我们总是在编辑器里使用ts的类型校验,而在运行时使用的是babel进行编译转换,也就是说在静态编译阶段使用的是编辑器,比如vscode的内置ts检测功能,代码实际上是通过babel进行编译的,这是一种很常见的做法。
在ts的3.8之前的阶段,babel对于ts的类型引入在转换时并不会删除它,因为ts并没有提供清晰的方式来表明import引入的是一个类型声明,这就意味着import { User } from "./types"
写法会被保留下来,只是说会编译成引入一个不存在的导出,从上面的给的代码例子上看,好像也没什么问题。
这里我们就要了解一个新名字:模块副作用
在JavaScript中,模块副作用通常指在模块文件中,除了定义变量、函数、类等导出外,还有一些直接执行的代码,这些代码可能会改变全局变量、改变其他模块的导出项等等,这些操作的结果就是副作用。
转换成代码示意:
export interface User {
name: string;
age: number;
}
window.abc = {
...
}
此时window.abc
的操作就是副作用。
如果babel在转换的时候没有将import { User } from "./types"
去除,就有可能导致出现问题,因为window.abc
我们可能是故意的,在某个地方导入时需要这么做,但是在其他地方引入User
并不需要这么做,从而产生了很奇怪的问题!
事实上正式的项目可能这种问题会更加隐晦!
为了解决这个问题,ts在3.8之后,增加了一种新的声明引入类型的方式:
import type { User } from "./types";
我们明确声明引入的是一个类型,此时babel就能很明确的知道这段代码的意义,从而在转换的时候会进行删除处理。
问题被完美解决,皆大欢喜。
但是在后续的开发中,我们又发现,我们往往要从一个统一的入口里引入很多东西,比如:
import { SomeFunction } from "./module";
import type { SomeType } from "./module";
这种写法很繁琐,在引入的值是少部分的时候,需要书写两行代码,有什么办法可以提高引入的便利性呢?
在ts 4.5版本的时候,加入了一种新的类型引入方式:inline import types
import { type SomeType, SomeFunction } from "./module";
我们可以通过内联的方式引入,从而提高了代码的可读性和编写的便利性。
eslint规范
你需要先安装一个eslint插件:
npm i @typescript-eslint/eslint-plugin -D
完毕后配置一下eslint
{
"plugins": [
"@typescript-eslint"
],
"extends": [
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/consistent-type-exports": "error"
}
}
@typescript-eslint/consistent-type-imports
表示引入类型时必须加上type或者使用内联type。
@typescript-eslint/consistent-type-exports
表示导出类型时必须是:export type UserId
或者export interface User
的方式,这个其实配不配都可以,类型导出不就这样嘛。
建议是开启eslint的自动修复功能,vscode安装插件 ESLint ,然后在设置里搜索 eslint auto fix
设置on开启即可,这样每次保存的时候会自动进行eslint修复。
动态导入
对于动态导入的情况下,@typescript-eslint/consistent-type-imports
会出现问题,我们需要调整一些规则定义:
{
"plugins": [
"@typescript-eslint"
],
"extends": [
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/consistent-type-imports": [
"error",
{ disallowTypeAnnotations: false }
],
"@typescript-eslint/consistent-type-exports": "error"
}
}
示例代码:
type T = import('Foo').Foo;
const x: import('Bar') = 1;
通过import动态引入的类型就不会报错了,如果你项目中用不到这种方式,事实上大部分情况下是用不到的,就没必要这么写,等用到了再改。
ts 5.0的新问题
在ts5.0的时候,重新改写了编译方式,对于内联type类型,会出现意料之外的情况:
import { type User } from "./types";
当我们的import引入是一个全是内联type的时候,它会被编译为:
import { } from "./types";
这就导致又会产生我们之前所说的模块副作用问题。
好在eslint提供了一个解决方式,配置一个rules:
{
"rules": {
"@typescript-eslint/no-import-type-side-effects": "error",
}
}
@typescript-eslint/no-import-type-side-effects
表示在类型引入时不要有副作用,配置之后配合eslint自动修复功能,它会将代码修正:
import { type User } from "./types";
// 修正后
import type { User } from "./types";
此时再被编译,就不会出现副作用的问题,整个类型引入在编译后会被删除。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据