# 类型声明和模块
# TS 类型声明的三种来源
TypeScript 设计了 declare
的语法,可以单独声明变量的类型:
interface Person {
name: string;
age: number;
}
declare const developer: Person;
declare function add(a: number, b: number): number;
单独声明了类型,使用这些 api 的时候也就能做类型检查。
像 JS 引擎那些 api,还有浏览器提供的 api,这些基本是必用的,而且都有标准的。所以 TypeScript 给内置了它们的类型声明。
TypeScript 包下有个 lib
目录,里面有一堆 lib.xx.d.ts
的类型声明文件,这就是 TS 内置的一些类型声明。因为这些只是声明类型,而没有具体的 JS 实现,TS 就给单独设计了一种文件类型,也就是 d.ts
, d
是 declare
的意思。
可以在 tsconfig.json
中配置 lib
属性,来指定使用哪些内置的类型声明:
{
"compilerOptions": {
"lib": ["dom", "es2015"]
}
}
内置的类型声明只有 dom
和 es
的。dom
是浏览器的,es
是 JS 标准的。其余的环境的 api 可能没有标准,经常变,所以没有内置。
node 等环境的 api 因为没有标准而没有被 TS 内置,但 TS 同样也支持了这些环境的类型声明的配置。
但是可以通过 npm
安装第三方的类型声明,比如 @types/node
,这样就可以使用 node 的 api 了。
{
"compilerOptions": {
"types": ["node"],
"typeRoots": ["./node_modules/@types", "./types"]
}
}
TS 会先加载内置的 lib
的类型声明,然后再去查找 @types
包下的类型声明。
除了给 node 等环境的 api 加上类型声明外,@types
包还有一种用途,就是给一些 JS 的包加上类型声明:
如果代码本身是用 ts 写的,那编译的时候就可以开启 compilerOptions.declaration
,来生成 d.ts
文件:
{
"compilerOptions": {
"declaration": true
}
}
然后在 package.json
里配置 types
来指定 dts
的位置, 这样就不需要单独的@types
包:
{
"types": "dist/index.d.ts"
}
如果代码不是用 ts 写的,那可能既需要单独写一个 @types/xxx
的包来声明 ts 类型,然后在 tsconfig.json
里配置下,加载进来。
对于自己写的 ts 代码,配置下编译的入口文件,通过 includes
指定一堆,然后通过 excludes
去掉一部分。还可以通过 files
再单独包含一些:
{
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"],
"files": ["src/index.ts"]
}
tsc 在编译的时候,会分别加载 lib
的,@types
下的,还有 include
和 files
的文件,进行类型检查。
# 全局类型声明 vs 模块类型声明
TS 最早支持的模块化方案是 namespace
:
namespace A {
export interface Person {
name: string;
age: number;
}
export const a = 1;
export function add(a: number, b: number) {
return a b;
}
}
// "use strict";
// var A;
// (function (A) {
// A.a = 1;
// function add(a, b) {
// return a b;
// }
// A.add = add;
// })(A || (A = {}));
namespace
编译后,在全局上放一个对象,然后对象上再挂几个暴露出去的属性。
后来,出现了 CommonJS 的规范,那种不能叫 namespace
了,所以 TS 支持了 module
,@types/node
的 api 定义就是一堆的 module
。
`module` 和 `namespace` 的区别
没什么区别,只不过 module
后一般接一个路径,而 namespace
后一般是一个命名空间名字。其他的语法都一样的。
再后来,JS 有了 es module
规范,所以现在推荐直接用 import export
的方式来声明模块和导入导出了。
额外多了 import type
的语法,可以单独引入类型:
import type { Person } from "./types";
现在声明模块不推荐用 namespace
和 module
,尽量用 es module
。
有了 es module
之后,TS 有了一个单独的设计:dts
中,如果没有 import
、export
语法,那所有的类型声明都是全局的,否则是模块内的。
在存在 import
时需要通过手动 declare global
来声明全局的类型:
declare global {
interface Person {
name: string;
age: number;
}
}
还可以使用 reference
来引入其他的 d.ts
文件同时,也可以声明全局的类型:
/// <reference path="./types.d.ts" />
# 使用 Project References 优化编译性能
如果项目下有一些相对独立的模块,别的模块的变动不影响它,但是它却要跟着重新编译一次,这时就可以用 Project Reference 来优化。
在独立的模块下添加 tsconfig.json
,加上 composite
的编译选项,在入口的 tsconfig.json
里配置 references
引用这些独立的模块。然后执行 tsc --build
或者 tsc -b
来编译。