# 定义全局类型
新建
user.d.ts文件,并把该文件加入tsconfig.json的include属性中,表明该文件加入tsc的编译。编写类型定义
// user.d.ts type UserName = 'Alice' | 'Bob' interface User { name: UserName; age: number; }1
2
3
4
5
6
7此时
user.d.ts中定义的类型为全局类型,项目中其他地方直接可以使用:const user: User = { name: 'Alice', age: 20 };1
假设现在类型User中新增加一个address属性,且该属性的类型需要从其他文件引入
// address.ts
export const address = {
北京: 'beijing',
上海: 'shanghai',
广州: 'guangzhou',
深圳: 'shenzhen',
};
1
2
3
4
5
6
7
2
3
4
5
6
7
// user.d.ts
import { address } from './address'
type UserName = 'Alice' | 'Bob'
interface User {
name: UserName;
age: number;
address: keyof typeof address
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
按照最初的使用方式:
// index.ts
const user: User = { name: 'Alice', age: 20, address: '上海' };
1
2
2
此时将会报错:
type User = /*unresolved*/ any
找不到名称“User”。
1
2
2
原因:TypeScript与ECMAScript 2015一样,任何包含顶级import或者export的文件都被当成一个模块。相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为全局可见的(因此对模块也是可见的),这就是为什么有时候没有引用某个.d.ts文件,但是在该.d.ts文件内部的类型定义在其它文件中仍然能检测得到,这是因为该.d.ts文件定义的类型已经变成全局的了。
# 解决方法
手动引入类型定义
// index.ts import { User } from './user.d.ts'; const user: User = { name: 'Alice', age: 20, address: '上海' };1
2
3
4使用
declare global// user.d.ts import { address } from './address' declare global { type UserName = 'Alice' | 'Bob' interface User { name: UserName; age: number; address: keyof typeof address; } }1
2
3
4
5
6
7
8
9
10
11
12使用方式:
const user: User = { name: 'Alice', age: 20, address: '上海' };1作为命名空间全局导出
// user.d.ts import { address } from './address' declare namespace UserType { type UserName = 'Alice' | 'Bob' interface User { name: UserName; age: number; address: keyof typeof address; } } export = UserType; export as namespace UserType;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16使用方式:
// index.ts // 注意是在命名空间下导出的,所以引用方式是UserType.User const user: UserType.User = { name: 'Alice', age: 20, address: '上海' };1
2
3
上面的第三种解决方案,通过命名空间的全局导出方式,就是官方文档内的UMD模块 (opens new window)的说明:
有些模块被设计成兼容多个模块加载器,或者不使用模块加载器(全局变量)。它们以UMD模块为代表。这些库可以通过导入的形式或全局变量的形式访问。例如: math-lib.d.ts
export function isPrime(x: number): boolean;
export as namespace mathLib;
1
2
2
之后,这个库可以在某个模块里通过导入来使用:
import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // 错误: 不能在模块内使用全局定义。
1
2
3
2
3
它同样可以通过全局变量的形式使用,但只能在某个脚本(指不带有模块导入或导出的脚本文件)里。
mathLib.isPrime(2);
1
参考资料