typescript声明文件写法

本文详细介绍了typescript声明文件的定义、查找规则,包括传统模块解析和node模块解析,并展示了如何写一个声明文件,包括声明全局变量、扩展全局变量或模块,以及创建node包的声明文件。此外,还提供了语法速查表和参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目前typescript已经非常流行了,我们经常在开发中用到类型、接口、枚举等功能,但一般业务开发中很少需要用到声明文件,只有在诸如扩展一个全局变量、声明一些全局类型的情况下会用到。但如果要写一个node包,那么声明文件就是必不可少的,typescript的声明文件有多种形式,而且不同形式的用法差别很大,本文主要汇总了声明文件的书写方法。

声明文件的定义

声明文件一般包含一些变量和函数的类型定义,如C、C++的.h头文件。在typescript中以.d.ts为扩展名的文件是声明文件。

声明文件的查找规则

当在代码中通过import关键字引入某个路径或库的时候,typescript会以某种规则检测对应的文件是否存在,并引入对应的文件,检测文件存在的解析模式分为传统模块解析和node模块解析,默认使用的是后者。

传统模块解析

以路径名引入:查找对应路径下的.ts.d.ts文件

如在/a/b/c.ts文件中import depend from '../depend',文件查找顺序是:/a/depend.ts; /a/depend.d.ts; [[Error]]

以模块名引入:根据目录名逐层查找对应的.ts.d.ts文件

如在/a/b/c.ts文件中import depend from 'depend',文件查找顺序是:/a/b/depend.ts; /a/b/depend.d.ts; /a/depend.ts; /a/depend.d.ts; /depend.ts; /depend.d.ts; [[Error]]

node模块解析

以路径名引入:查找对应路径下的.ts.d.ts文件 -> 如果引入的路径是一个node包目录,读取node包package.json的types字段对应的文件 -> 读取node包根目录下的index.tsindex.d.ts文件

如在/a/b/c.ts文件中import depend from '../depend',文件查找顺序是:/a/depend.ts; /a/depend.d.ts; /a/depend/package.json[[types]]; /a/depend/index.ts; /a/depend/index.d.ts; [[Error]]

可见node模块解析的方式相比传统解析增加了判断node包的逻辑,而且对于node包优先读取的声明文件是package.json中types字段声明的文件,如果没有找到才读取根目录下的index.tsindex.d.ts文件,我们可以在开发node包的时候灵活运用这些规则。

以模块名引入:类似上面路径名的引入规则,但是在node_modules目录下查找,而且会回溯到上一级目录查找。

如在/a/b/c.ts文件中import depend from 'depend',文件查找顺序是:

/a/b/node_modules/depend.ts; 
/a/b/node_modules/depend.d.ts; 
/a/b/node_modules/depend/package.json[[types]]; 
/a/b/node_modules/depend/index.ts; 
/a/b/node_modules/depend/index.d.ts;

/a/node_modules/depend.ts; 
/a/node_modules/depend.d.ts; 
/a/node_modules/depend/package.json[[types]]; 
/a/node_modules/depend/index.ts; 
/a/node_modules/depend/index.d.ts;

/node_modules/depend.ts; 
/node_modules/depend.d.ts; 
/node_modules/depend/package.json[[types]]; 
/node_modules/depend/index.ts; 
/node_modules/depend/index.d.ts;

[[Error]]

tsc工具默认使用node模块解析,可以使用tsc --moduleResolution classic切换到传统模块解析。

写一个声明文件

声明文件大体包含以下几种类型,可以根据自己的需要选择对应的写法:

  • 声明全局变量 (导入了一个包含全局变量的库,或者定义全局枚举, interface, type等)
  • 扩展全局变量或引入的模块
  • 写一个node包的声明文件

声明全局变量

一般使用declare关键字声明一个全局变量或类型,interface和type不需要添加declare,如:

// 声明变量,一般是const声明,给定值
declare const LIMIT_SIZE = 10; 
// 声明一个带属性的全局变量,在ts代码中可能这样用`myBundle.getOrigin()`
declare namespace myBundle {
    type origin = 1 | 2 | 3;
    function getOrigin(): origin;
}
// 声明一个全局函数
declare function setAnchor (id: number): void;
// 声明一个全局类
declare class Earth {
    private weight: number;
    private rotate(): void;
}

// 声明一个全局枚举值
declare enum DATES { 'MON', 'TUES', 'WEDS', 'THES', 'FRI', 'SAT', 'SUN' }
// interface和type不需要declare
interface Person {
    name: string,
    age: number
}
type origin = number | string;

// 声明合并的写法,如JQuery既是一个函数,又是一个namespace
declare function JQuery(selector: string): JQuery
declare namespace JQuery {
  ajax(...) => ...
}

值得注意的一点是,全局变量的声明文件不可以带import, export, 如果需要依赖其他的声明文件,需要使用三斜线语法。

/// <reference types="jquery" />
/// <reference path="../../special.d.ts" />

declare function foo(options: JQuery.Ajax): Promise<any>;

另外需要强调的一点是,声明文件仅仅作为类型声明,不可以用来定义变量,编译后不存在实体,比如声明文件中定义declare enum Platform {...},在编译后并不会生成任何js代码,因此希望通过declare来定义一个在代码工程中处处可用的全局枚举值是不能的,全局变量同理。

扩展全局变量或引入的模块

直接扩充接口:扩充语言类型的底层接口,如:

interface String{
  printf(): void;
}

扩充namespace:导入的库中包含全局变量的声明,扩充此声明,如:

// 给JQuery加一个fetch方法的声明
declare namespace jQuery {
  fetch(): Promise<unknown>
}

扩充模块:一般在一些插件中会用到,使用declare module + export语法。

// 插件给dayjs库增加一个foo方法
import * as dayjs from 'dayjs';

declare module 'dayjs' {
  export function foo(): dayjs.DayUnit;
}

写一个node包的声明文件

定义一个ES6风格的node包的导出,使用export关键字

export function add(a: number, b: number): number;
export function subtract(a: number, b: number): number;

或者使用declare + export一次性导出

export {add, subtract};

declare function add(a: number, b: number): number;
declare function subtract(a: number, b: number): number;
完整案例
  • /a/b/index.d.ts定义声明文件:
export {add, subtract};

declare function add(a: number, b: number): number;
declare function subtract(a: number, b: number): number;
  • /a/b/index.ts功能的具体实现,这里使用了commonjs规范:
function add(a: number, b: number) {
  return a + b;
}
function subtract(a: number, b: number) {
  return a - b;
}

module.exports = {
  add,
  subtract
}
  • /a/b/package.json定义模块types文件路径,根据上文模块查找规则,这里package.json中定义types可以引导ts编译器查找/a/b目录下的index.d.ts文件作为类型声明文件:
{
  "types": "./index.d.ts"
}
  • /a/c/main.ts引用定义的模块:
import { add } from '../b';

console.log(add(1, 2));
export =导出格式

export =是commonjs风格的导出格式,使用这种方式导出的包,导入的时候应该使用import ... = require()语法,如声明文件为:

export = foo;

declare function foo(): void;
declare namespace foo() {
    function bar(): string;
}

上面导出的foo既是一个函数,又可以作为对象取值,如果想要导入foo作为函数使用,就只能通过import foo = require('foo')这种方式,而不能使用import from语法。

举例来说,可以将上文案例中/a/b/index.d.ts的内容变成:

export = numUtil;

declare function numUtil(): void;
declare namespace numUtil {
  function add(a: number, b: number): number;
  function subtract(a: number, b: number): number;
}

/a/b/index.ts模块的实现稍加修改为:

// function add ...
// ... 以上是add和subtract的实现
const numUtil = function() {
  console.log('numUtil');
}

numUtil.add = add;
numUtil.subtract = subtract;

module.exports = numUtil;

/a/c/main.ts中使用该模块:

import numUtil = require('../b');

numUtil();
console.log(numUtil.add(1, 2));
其他

如果node包扩充了底层接口,需要用到declare global, 如:

// 这个包为String的原型添加了printf方法
declare global {
  interface String{
    printf(): void;
  }
}

如果node包依赖全局变量,但全局变量的声明文件中没有export,可以使用三斜线语法导入:

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

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

兼容UMD库:UMD同时支持import和script标签引入(一般会在globalThis上绑定一个全局变量),需要额外export一个全局命名空间,如:

export as namespace numUtil;
export {add, subtract};

declare function add(a: number, b: number): number;
declare function subtract(a: number, b: number): number;

语法速查表

语法导入/导出作用
declare const/namespace/class/function/enum使用///语法导入其他声明文件声明全局变量
interface/type使用///语法导入其他声明文件声明全局类型/接口/枚举
declare namespace JQuery扩充全局变量
import + declare module扩充模块
export / declare + export引入格式import … from …定义node包
export =引入格式import … = require(’…’)定义node包
declare globalnode包扩充底层接口
export as namespace包兼容UMD库

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值