Appearance
TS 杂谈
import 和 import type
概览
import:导入一个标准 JS 值和值类型
import type:导入值类型
import 存在的问题
通常会使用import去导入一些类型或者值
ts
// ./foo.ts
interface Options {
// ...
}
export function doThing(options: Options) {
// ...
}
// ./bar.ts
import { doThing, Options } from "./foo.js";
function doThingBetter(options: Options) {
// do something twice as good
doThing(options);
doThing(options);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
由于import elision的作用,能够同时导入值和类型。
在 typescript 编译时,会自动在生成的 js 中删除类型导入
js
// ./foo.js
export function doThing(options: Options) {
// ...
}
// ./bar.js
import { doThing } from "./foo.js";
function doThingBetter(options) {
// do something twice as good
doThing(options);
doThing(options);
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
问题 1:不明确导入是类型还是值
ts
// ./some-module.ts
export interface MyThing {
name: string;
}
// ./src.ts
import { MyThing } from "./some-module.ts";
export { MyThing };1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
最终生成的src.js文件中对MyThing的导入和导出都会被保留, 而在some-module.js文件中, MyThing仅作为一个类型而存在, 会在编译过程中被删除, 所以最终some-module.js文件是空的
问题2:会删除掉仅作为类型使用的import导入
ts
// 因为import elision的功能, 这行代码在最终生成的js代码中会被删除
import { SomeTypeFoo, SomeOtherTypeBar } from "./module-with-side-effects";
// 这行代码要一直存在
import "./module-with-side-effects";1
2
3
4
5
2
3
4
5
基于上面的问题, Typescript 3.8中引入了import type, 用一种更加清晰易懂的方式来控制某一个导入是否要被删除。
import type 优势
- 使用 import type 没有具体的 type,会报错
js
// foo.js
export function foo() {
console.log('Hello')
}
// bar.js
import type { foo } from './foo'1
2
3
4
5
6
7
2
3
4
5
6
7
Cannot import foo because there is no foo export in ./foo. [missing-export]
1| // bar.js
2| import type { foo } from "./foo";1
2
3
4
2
3
4
通过
import type可以避免循环依赖。import type只影响类型检测,不是运行时行为,不存在真实文件执行。代码可读性强,明确导入是类型
import type 注意事项
class
class既可以代表一个类型, 也可以代表一个值, 所以用import type引入一个class时, 不能extend这个class.
js
import type { Component } from "react";
interface ButtonProps {
// ...
}
class Button extends Component<ButtonProps> {
// error! 'Component' only refers to a type, but is being used as a value here.
}1
2
3
4
5
6
7
2
3
4
5
6
7
不可以同时引入默认和命名绑定
使用import type的时候, 可以引入一个默认导出的类型, 也可以引入命名绑定的形式导出的类型, 但是不可以同时使用两者.
ts
import type Foo, { Bar, Baz } from "some-module";
// ~~~~~~~~~~~~~~~~~~~~~~
// error! A type-only import can specify a default import or named bindings, but not both.1
2
3
2
3
import 和 import type 编译差异
ts
// foo.ts
export function foo() {
console.log('Hello')
}
// bar.ts
import { foo } from './foo'1
2
3
4
5
6
7
2
3
4
5
6
7
ts
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
var foo_1 = require('./foo')
// The require statement will execute the code in foo.ts and print "Hello" to the console1
2
3
4
2
3
4
import type 导入最终 js
ts
// bar.ts
import type { foo } from './foo'1
2
2
ts
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
// There is no require statement, so the code in foo.ts is not executed and nothing is printed to the console1
2
3
2
3