Typescript 声明文件 .d.ts

当使用第三方库时,需要引用它的声明文件,才能获知其数据类型,以获得对应的代码补全、接口提示等功能。
声明文件用于定义类型而非具体的值,不会保留在编译结果的 js 中。

TS编译检查时,变量的声明查找顺序
  1. 当前编译上下文找该变量的定义
  2. 变量所在模块的index.d.ts声明文件中查找(或其package.jsontypes字段指向文件)
    通常由npm包的维护者提供
  3. node_modules/@types/npm包名 查找声明
    通常是作者没有提供声明文件,由社区其他开发者提供,以@types/开头的声明文件,如:npm install @types/jquery
  4. 通过配置文件 tsconfig.json 中的 pathsbaseUrl 字段指向的其他目录
    通常是自己本地写的声明文件

生成 .d.ts 文件

tsc指令中添加 --declaration(简写 -d),或者在 tsconfig.json 中添加 declaration 选项,就可以同时也生成 .d.ts 声明文件了。

{
    "compilerOptions": {
        "module": "commonjs",
        "outDir": "lib",
        "declaration": true,
    }
}

全局变量声明

在script模式中,通过declare进行全局变量声明,通过script标签引入,全局生效。
注意全局变量文件中不能有importexport关键字,否则会被视为npm/UMD包文件。

type 和 interface

两者本来就只是类型的定义,因此不需搭配declare关键字,直接在声明文件中使用
为了防止命名冲突,通常放在namespace

声明全局变量
declare var jQuery: (selector: string) => any;
declare let jQuery: (selector: string) => any;
declare const jQuery: (selector: string) => any;
声明函数

支持函数重载

declare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any;
声明类

注意 declare 里可以声明类的构造函数,interface 里不可以

declare class Animal {
    name: string;
    constructor(name: string);
    sayHi(): string;
}
声明枚举
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
声明命名空间

命名空间(namespace)是TS自己的模块化方案,随着ES6的module广泛运用,已不推荐使用。不同于ES6的module,一个文件里可以有多个namespace。
目前通常在声明文件中使用,表示一个对象中具有很多子属性。
通过命名空间.属性来调用内部属性。

declare namespace jQuery {
    function ajax(url: string, settings?: any): void;
    const version: number;
    class Event {
        blur(eventType: EventType): void
    }
    enum EventType {
        CustomClick
    }
}

命名空间也可以嵌套

// src/jQuery.d.ts
declare namespace jQuery {
    function ajax(url: string, settings?: any): void;
    namespace fn {
        function extend(object: any): void;
    }
}
//或
declare namespace jQuery.fn {
    function extend(object: any): void;
}

// src/index.ts
jQuery.ajax('/api/get_something');
jQuery.fn.extend({
    check: function() {
        return this.each(function() {
            this.checked = true;
        });
    }
});
声明合并

同名变量可以重复声明,表示有不同的形状

declare function jQuery(selector: string): any;
declare namespace jQuery {
    function ajax(url: string, settings?: any): void;
}

通过声明合并,还可以为系统变量添加新属性,称为扩展全局变量

interface String {
    prependHello(): string;
}

模块声明

npm包(ES6)规范的声明

.d.ts文件中,如存在export,则为一个模块声明文件,npm包中即使用该方式。
在模块声明中,只有export导出 + import导入的声明,才会生效。

可以对每个变量分别export,也可以用declare分别声明,并统一export(注意,同全局变量声明文件,type 和 interface 不需要 declare)

// types/foo/index.d.ts
export const name: string;
export interface Options {
    data: any;
}

// types/foo/index.d.ts
declare const name: string;
interface Options {
    data: any;
}
export { name, Options };

// src/index.ts
import { name, Options } from 'foo';
console.log(name);
let options: Options = {
    data: {
        name: 'foo'
    }
};
commonjs 规范的声明

ts中,对采用commonjs规范的库,可以通过以下方式导入:

  • commonjs语法:
const foo = require('foo');
const bar = require('foo').bar;
  • ESM语法:
import * as foo from 'foo';
import { bar } from 'foo';
  • TS新增语法import + requireexport =搭配使用:
// types/foo/index.d.ts
export = foo;
declare function foo(): string;
declare namespace foo {
    const bar: number;
}


// 整体导入
import foo = require('foo');
import * as foo from 'foo';
import foo from 'foo';//当启用allowSyntheticDefaultImports时

// 单个导入
import bar = foo.bar;
UMD规范的声明

既可以通过 <script> 标签引入,又可以通过 import 导入的库,需要通过export as namespace将声明好的一个变量声明为全局变量

// types/foo/index.d.ts

export as namespace foo;
export = foo;

declare function foo(): string;
declare namespace foo {
    const bar: number;
}
直接扩展全局变量

有的第三方库扩展了一个全局变量,可是此全局变量的类型却没有相应的更新过来,就会导致 ts 编译错误,此时就需要扩展全局变量的类型。
注意声明文件仍然需要导出一个空对象,用来告诉编译器这是一个模块的声明文件

// types/foo/index.d.ts

declare global {
    //通过声明合并,给 String 添加属性或方法。
    interface String {
        prependHello(): string;
    }
}

export {};

// src/index.ts

'bar'.prependHello();
模块插件

declare module可用于在一个文件中一次性声明多个模块的类型,或扩展某模块类型

// types/moment-plugin/index.d.ts
import * as moment from 'moment';

declare module 'moment' {
    export function foo(): moment.CalendarKey;
}

// src/index.ts
import * as moment from 'moment';
import 'moment-plugin';

moment.foo();
三斜线指令

类似于声明文件中的 import,它可以用来导入另一个声明文件。
三斜线指令必须放在文件的最顶端,三斜线指令的前面只允许出现单行或多行注释。
当且仅当在以下几个场景下,我们才需要使用三斜线指令替代 import:

  • 当我们在书写一个全局变量的声明文件时(避免出现importexport关键字,以免被识别为npm、UMD文件)
// types/jquery-plugin/index.d.ts

/// <reference types="jquery" />

declare function foo(options: JQuery.AjaxSettings): string;


// src/index.ts

foo({});
  • 当我们需要依赖一个全局变量的声明文件时
    由于全局变量不支持通过 import 导入,当然也就必须使用三斜线指令来引入了
// types/node-plugin/index.d.ts

/// <reference types="node" />

export function foo(p: NodeJS.Process): string;


// src/index.ts

import { foo } from 'node-plugin';

foo(global.process);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 看到别人写ts文件的时候,通常会有以.d.ts结尾的文件跟着,它的作用是,描述Javascript模板内导出的接口...
    Adder阅读 8,681评论 0 6
  • 声明文件中,declare 表示声明的意思js原有类型必须加declare,js不存在的类型不用加,如:inter...
    wyc0859阅读 7,707评论 0 1
  • 本文摘自 https://ts.xcatliu.com/basics/declaration-files 声明文件...
    Lia代码猪崽阅读 1,279评论 0 1
  • 点击原文 now我们来看一看TS怎么声明文件, 在JS里面我们经常会使用各种第三方类库,引入方式也不太相同,常见的...
    videring阅读 3,239评论 0 4
  • 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。这是因为前端第三方库大多都是非...
    CondorHero阅读 5,143评论 1 5

友情链接更多精彩内容