typescript声明文件
声明文件
通常我们会把声明语句放到一个单独的 xx.d.ts 文件中
/path/to/project
├─ src
│ ├─ index.ts
│ └─ jQuery.d.ts
└─ tsconfig.json
第三方声明文件
声明文件查找 搜索已经定义好了的声明文件,直接用 npm 安装
npm install @types/jquery --save-dev
书写声明文件
全局变量
declare var 声明全局变量
declare const jQuery: (selector: string) => any
declare function 声明全局方法
declare function jQuery(selector: string): any
declare class 声明全局类
declare class Animal {
name: string
constructor(name: string)
sayHi(): string
}
declare enum 声明全局枚举类型
declare enum Directions {
Up,
Down,
Left,
Right,
}
declare namespace 声明(含有子属性的)全局对象 namespace是早期为了解决模块化而创造的关键字,中文名称为命名空间
在早期还没有 ES6 时,ts 提供了一种模块化方案,使用module关键字表示内部模块。但后来ES6也使用了module关键字,ts为了兼容 ES6,使用namespace替代了自己的module。 推荐使用 ES6 的模块化方案
declare namespace jQuery {
function ajax(url: string, settings?: any): void
const version: number
class Event {
blur(eventType: EventType): void
}
enum EventType {
CustomClick,
}
namespace fn {
function extend(object: any): void
}
}
interface 和 type 声明全局类型
npm 包
给一个npm包创建声明文件前先看看它的声明文件是否存在,一般来说可能存在与两个地方:
- 与该
npm包绑定在一起。判断依据是package.json中有types字段,或者有一个index.d.ts声明文件; - 发布到
@type里。尝试安装下对应的@type 包即可
若以上两种都没找到对应的声明文件,就需要自己为它写声明文件
- 可以创建一个
node_modules/@types/xxxx/index.d.ts文件存放xxx模块的声明文件,但目录不稳定,而且无法保存到仓库中 - 创建一个
types目录,来管理自己写的声明文件,将xxx的声明放到types/xxx/index/d/ts中,这种方式需要配置下tsconfig.json中的paths和baseUrl字段
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*": ["types/*"]
}
}
}
配置后,通过import导入的xxx时,会去types目录下寻找对应的模块声明文件
export const name: string
export function getName(): string
export class Animal {
constructor(name: string)
sayHi(): string
}
export enum Directions {
Up,
Down,
Left,
Right,
}
export interface Options {
data: any
}
export namespace foo {
const name: string
namespace bar {
function baz(): string
}
}
UMD 库
既可以通过<script>标签引入,又可以通过import导入的库,称为UMD库 相比npm包的类型声明文件,我们需要额外声明一个全局变量,ts提供了语法export as namespace
export as namespace foo
export default foo
declare function foo(): string
declare namespace foo {
const bar: number
}
在 npm 包或 UMD 库中扩展全局标量
对于npm包或UMD库,如果导入此库后会宽展全局变量,需要使用declare global在声明文件中扩展全局变量的类型
declare global {
interface String {
prependHello(): string
}
}
export {}
模块插件
有时通过 import 导入一个模块插件,可以改变另一个原有模块的结构。此时如果原有模块已经有了类型声明文件,而插件模块没有类型声明文件,就会导致类型不完整,缺少插件部分的类型。ts 提供了 declare module 可以用来扩展原有模块的类型
import * as moment from 'moment'
declare module 'moment' {
export function foo(): moment.CalendarKey
}
声明文件中的依赖
除了可以在声明文件中通过import导入另一个声明文件中的类型外,还可以用三斜线指令来导入另一个声明文件
- 书写一个全局变量的声明文件 在全局变量的声明文件中,是不允许出现
import,export关键字的,一旦出现了,会被视为一个npm包或UMD库。///后面使用 xml 格式添加了对jquery类型的依赖,可以在声明文件中使用JQuery.AjaxSerring类型了///指令必须放在文件的最顶端
/// <reference types="jquery" />
declare function foo(options: JQuery.AjaxSettings): string
- 依赖一个全局变量的声明文件 引入的 node 中的类型都是全局变量的类型,它们没有办法通过
import来导入,需要通过///来引入。在src/index.ts中foo(global.process)
/// <reference types="node" />
export function foo(p: NodeJS.Process): string
自动生成声明文件
如果库的源码本身就是由ts写的,那么在使用tsc脚本将ts编译为js时,添加declaration选项,就可以同时也生成.d.ts声明文件了
可以在命令行添加--declaration 或 -d,或者在tsconfing.json中添加declaration选项,添加outDir选项,将ts文件的编译结果输出到lib目录下
{
"compilerOptions": {
"module": "commonjs",
"outDir": "lib",
"declaration": true
}
}
发布声明文件
如果声明文件是通过tsc自动生成的,那么无需做任何配置,只需把编译好的文件也发布到npm上,使用放就可以获取到类型的提示了 如果是手动写的声明文件那么需要满足一下条件之一,才能被正确的识别
- 给
package.json中的types或typings字段指定一个类型声明文件地址 - 在项目根目录下,编写一个
index.d.ts文件 - 针对入口文件
(package.json中main字段指定的入口文件),编写一个同名不同后缀的.d.ts文件
{
"name": "xxx",
"version": "1.0.0",
"main": "lib/index.js",
"types": "xxx.d.ts"
}
如果我们是在给别人的仓库添加类型声明文件,但原作者不愿意合并,那么需要将声明文件发布到@types下 暴露在最外层的interface或type会作为全局类型作用于整个项目中,我们应该尽可能得减少全局变量或全局类型的数量。最好将他们放在namespace下
declare namespace jQuery {
interface AjaxSettings {
method?: 'GET' | 'POST'
data?: any
}
function ajax(url: string, settings?: AjaxSettings): void
}
// src/index.ts
let settings: jQuery.AjaxSettings = {
method: 'POST',
data: {
name: 'foo',
},
}
jQuery.ajax('/api/post_something', settings)
