19_TypeScript 声明文件 --[深入浅出 TypeScript 测试]

TypeScript 声明文件(.d.ts 文件)用于描述 JavaScript 库或模块的类型信息,使得 TypeScript 编译器能够在使用这些库时提供类型检查和智能感知。声明文件并不包含任何实现代码,只定义了接口、类、函数等的类型签名。这对于确保类型安全和提高开发效率非常重要。

以下是关于 TypeScript 声明文件的一些关键概念和如何创建及使用它们的指南:

1. 什么是声明文件

TypeScript 声明文件(通常具有 .d.ts 扩展名)是用于描述 JavaScript 库或模块的类型信息的特殊文件。这些文件并不包含实际的实现代码,而是定义了库中所有公共 API 的类型签名,包括接口、类、函数、变量等。声明文件的主要目的是为了让 TypeScript 编译器能够理解第三方 JavaScript 代码的结构,从而提供静态类型检查和增强开发体验(如智能感知、自动补全、参数提示等)。以下是关于声明文件的一些关键点:

1. 为什么需要声明文件

  • 类型安全:通过为 JavaScript 库添加类型信息,开发者可以在使用这些库时获得编译时的类型检查,减少运行时错误。
  • 更好的工具支持:编辑器和 IDE 可以根据声明文件提供更强大的代码辅助功能,例如自动补全、参数提示、跳转到定义等。
  • 文档化 API:声明文件可以作为库的 API 文档,帮助开发者更好地理解和使用库的功能。

2. 声明文件的内容

声明文件中主要包含以下几种类型的声明:

  • 命名空间和模块声明:用于组织和导出一组相关的类型定义。
  • 接口声明:定义对象的形状,指定哪些属性和方法是可用的。
  • 类声明:描述类的构造函数、属性和方法。
  • 函数声明:指定函数的参数和返回值类型。
  • 变量声明:定义全局变量或模块级别的变量及其类型。
  • 类型别名:为复杂类型创建简单的名称。
  • 枚举声明:定义一组命名的常量值。
  • 泛型声明:允许在声明中使用泛型参数。

2. 何时需要声明文件

在使用 TypeScript 开发时,声明文件(.d.ts 文件)对于确保类型安全和提高开发体验非常重要。以下是一些需要创建或使用声明文件的具体场景:

1. 使用没有类型定义的 JavaScript 库

当你想在 TypeScript 项目中使用一个现有的 JavaScript 库,并且该库本身没有提供类型定义时,你需要创建一个声明文件来描述库的 API。这将使你能够在使用库的同时享受 TypeScript 的静态类型检查和智能感知功能。

示例:如果你想要使用 lodash 这样的库,但不想放弃 TypeScript 的类型安全性,你可以通过 @types/lodash 安装它的类型定义,或者如果找不到官方支持的类型定义,则可以自己编写一个。

npm install --save-dev @types/lodash

2. 为自定义 JavaScript 模块添加类型信息

如果你有一个自定义编写的 JavaScript 模块,并希望其他 TypeScript 代码能够以类型安全的方式使用它,那么你应该为这个模块创建相应的 .d.ts 声明文件。这样做不仅有助于维护代码质量,还能让其他开发者更容易理解和使用你的模块。

示例:假设你有一个名为 math-utils.js 的文件,其中包含了一些数学操作函数。你可以为其创建一个 math-utils.d.ts 文件来定义这些函数的参数和返回值类型。

3. 全局对象或浏览器 API 扩展

有时你需要为某些全局对象或浏览器内置 API 添加额外的属性或方法,而这些扩展并没有默认的类型定义。这时,你可以通过创建全局声明文件来定义这些新的成员。

示例:如果你想给 Window 接口添加一个新的方法 myCustomMethod,可以在全局声明文件中这样声明:

// global.d.ts
interface Window {
  myCustomMethod(): void;
}

4. 发布带有类型定义的 npm 包

如果你正在开发一个 npm 包,并希望用户能够在 TypeScript 项目中轻松地使用它,那么应该随包一起发布类型定义文件。这可以通过在项目的根目录下放置 .d.ts 文件并在 package.json 中设置 "types" 字段来实现。

示例:如果你的 npm 包是用纯 JavaScript 编写的,但你提供了详细的类型定义,那么可以在 package.json 中指定类型定义文件的位置:

{
  "name": "my-package",
  "version": "1.0.0",
  "types": "./index.d.ts"
}

5. 处理不兼容的类型定义

有时候你可能会遇到第三方库提供的类型定义与实际库的行为不一致的情况。在这种情况下,你可以选择覆盖或修正那些不准确的类型定义。通常可以通过创建自己的声明文件并调整它们来解决这个问题。

6. 为了更好的工具支持

即使你不直接依赖某个库,但你希望通过编辑器获得更好的代码补全、跳转到定义等功能,也可以安装对应的类型定义。例如,一些流行的前端框架如 React 或 Vue 都有丰富的类型定义,这使得开发体验更加流畅。

总结

总的来说,每当你要在 TypeScript 项目中引入外部 JavaScript 代码,并且希望保持类型安全性和良好的开发体验时,就需要考虑创建或使用声明文件。这不仅可以帮助你在编写代码时减少错误,还可以大大提升团队协作和代码维护的效率。通过适当地应用声明文件,你可以在充分利用现有 JavaScript 生态系统的同时,享受到 TypeScript 提供的所有好处。

3. 常见的声明文件形式

单独的声明文件

如果你有一个单独的 JavaScript 文件,你可以为其创建一个对应的 .d.ts 文件来定义它的类型。

example.js

function greet(name) {
  console.log(`Hello, ${name}!`);
}

example.d.ts

declare function greet(name: string): void;
示例:为 math-utils.ts 创建声明文件

它包含了一些简单的数学操作函数:

math-utils.ts

export function add(a: number, b: number): number {
    return a + b;
  }
  
  export function subtract(a: number, b: number): number {
    return a - b;
  }
  
  export function multiply(a: number, b: number): number {
    return a * b;
  }
  
  export function divide(a: number, b: number): number {
    if (b === 0) {
      throw new Error("Cannot divide by zero");
    }
    return a / b;
  }

为了在 TypeScript 中以类型安全的方式使用这些函数,我们需要为它们创建一个对应的 .d.ts 声明文件。

math-utils.d.ts

// 定义 math-utils.js 中所有公开的函数及其参数和返回值类型

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

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

declare function multiply(a: number, b: number): number;

declare function divide(a: number, b: number): number;
使用声明文件

一旦创建了声明文件,你就可以在 TypeScript 项目中导入并使用 math-utils.js 中的函数,同时获得类型检查和智能感知支持。

main.ts

/// <reference path="./math-utils.d.ts" />

import { add, subtract, multiply, divide } from './math-utils';

console.log(add(10, 5)); // 输出: 15
console.log(subtract(10, 5)); // 输出: 5
console.log(multiply(10, 5)); // 输出: 50
try {
  console.log(divide(10, 2)); // 输出: 5
  console.log(divide(10, 0)); // 抛出错误: Cannot divide by zero
} catch (error) {
  console.error(error.message);
}

请注意,在现代的 TypeScript 和模块系统(如 ES6 模块)中,通常不需要使用 /// <reference> 注释来引用声明文件。如果你已经正确配置了 tsconfig.json 并且你的模块路径设置正确,TypeScript 编译器应该能够自动找到并应用相应的声明文件。

tsconfig.json 配置

确保你的 tsconfig.json 文件配置正确,以便 TypeScript 知道在哪里查找类型定义文件。例如:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts"],
  "files": ["./math-utils.d.ts"]
}

在这个配置中,"include" 字段指定了哪些文件应该被编译,而 "files" 字段明确列出了需要包含的声明文件。如果你的项目结构允许,通常不需要指定 "files",因为 "include" 已经足够让 TypeScript 找到所有的 .ts.d.ts 文件。

总结

通过为 math-utils.js 创建一个 .d.ts 声明文件,我们不仅能够在 TypeScript 代码中安全地使用这些函数,还可以享受到编辑器提供的智能感知功能,比如参数提示、跳转到定义等。这极大地提高了开发效率,并减少了运行时错误的可能性。

模块声明文件

对于 ES6 模块或 CommonJS 模块,你应该在一个单独的 .d.ts 文件中声明该模块的内容。

myLibrary.js

export function add(a, b) {
  return a + b;
}

myLibrary.d.ts

export function add(a: number, b: number): number;
配置 tsconfig.json

确保你的 tsconfig.json 文件配置正确,以便 TypeScript 知道在哪里查找类型定义文件。例如:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts"]
}

在这个配置中,"include" 字段指定了哪些文件应该被编译,包括 .ts.d.ts 文件。如果你的项目结构允许,通常不需要显式列出所有文件,因为 "include" 已经足够让 TypeScript 找到所有的相关文件。

更复杂的模块声明文件示例

如果模块更加复杂,比如包含类、接口或命名空间,那么声明文件也会相应地变得更加复杂。下面是一个稍微复杂一点的例子,展示了如何为包含类和接口的模块创建声明文件。

复杂的 TypeScript 模块(带类和接口)

complex-module.ts

export interface Shape {
  area(): number;
}

export class Circle implements Shape {
  constructor(public radius) {}

  area() {
    return Math.PI * this.radius * this.radius;
  }
}

export class Rectangle implements Shape {
  constructor(public width, public height) {}

  area() {
    return this.width * this.height;
  }
}
对应的声明文件

complex-module.d.ts

// 定义接口 Shape
export interface Shape {
  area(): number;
}

// 实现 Shape 接口的 Circle 类
export class Circle implements Shape {
  constructor(radius: number);
  area(): number;
}

// 实现 Shape 接口的 Rectangle 类
export class Rectangle implements Shape {
  constructor(width: number, height: number);
  area(): number;
}

通过这种方式,你可以为更复杂的模块提供详细的类型信息,从而确保在 TypeScript 项目中使用这些模块时能够获得全面的类型安全性和开发辅助功能。

总结

通过为 math-utils.jscomplex-module.js 创建相应的 .d.ts 文件,我们不仅能够在 TypeScript 代码中安全地使用这些模块,还可以享受到编辑器提供的智能感知功能,比如参数提示、跳转到定义等。这极大地提高了开发效率,并减少了运行时错误的可能性。模块声明文件是确保第三方库和自定义模块在 TypeScript 项目中类型安全的关键工具。

全局声明文件

如果你想为全局变量或浏览器 API 添加类型定义,可以在一个全局声明文件中进行声明。

global.d.ts

declare const myGlobalVar: string;

interface Window {
  myCustomMethod(): void;
}

4. 如何创建声明文件

  • 为现有库创建声明文件:如果你正在使用一个没有类型定义的库,可以为它创建一个 .d.ts 文件并定义其类型。确保覆盖所有公开的 API。

  • 发布你的库时包含声明文件:如果你自己编写了一个库,应该同时提供相应的类型定义文件,以便其他开发者可以受益于类型检查和智能感知。

5. 使用现有的声明文件

  • 通过 @types 安装:许多流行的库都有社区维护的类型定义文件,可以通过 npm 安装。

    npm install --save-dev @types/library-name
    
  • 直接引用声明文件:如果库自带类型定义或你有自定义的 .d.ts 文件,可以直接在代码中通过 /// <reference path="..." />import 语句引用它们。

6. 最佳实践

  • 保持简洁:尽量只暴露必要的公共 API 的类型定义,避免不必要的复杂性。
  • 遵循标准:尽可能遵循 TypeScript 和 JavaScript 社区的标准和约定。
  • 测试:确保声明文件与实际库的行为相匹配,最好能有一些简单的测试用例来验证类型定义是否正确。
  • 文档化:为复杂的类型定义添加注释和文档,帮助其他开发者理解。

7. 示例:为 jQuery 创建声明文件

这里是一个简化版的 jQuery 类型定义的例子:

jquery.d.ts

// 声明 jQuery 函数
declare function jQuery(selector: string): JQuery;

// 声明 jQuery 静态方法
interface JQueryStatic {
  (selector: string): JQuery;
  ajax(url: string, settings?: any): void;
}

// 声明 jQuery 实例方法
interface JQuery {
  html(): string;
  html(content: string): JQuery;
  on(event: string, handler: (eventObject: Event) => void): JQuery;
}

// 将 jQuery 设置为全局变量
declare var $: JQueryStatic;
declare var jQuery: JQueryStatic;

总结

声明文件是 TypeScript 生态系统中不可或缺的一部分,它们使得我们可以安全地与 JavaScript 世界交互,同时享受静态类型的诸多好处。了解如何创建和使用声明文件可以帮助我们构建更加健壮、易于维护的应用程序。无论是为现有的库添加类型定义,还是为自己的项目发布类型信息,掌握这一技能都是非常有价值的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/952780.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

acwing_5722_十滴水

acwing_5722_十滴水 下面这篇大佬的题解属实是把指针用明白了&#xff0c;可以好好理解一下&#xff1a; 原题解连接&#xff1a;AcWing 5722. 一个简单模拟实现 - AcWing map/unordered_map的用法:见收藏夹 #include<iostream> #include<unordered_map> #incl…

零基础学AI大模型要多久?真的能学会吗?

很多人都对学习AI大模型抱有疑问&#xff0c;特别是那些完全没有编程基础的朋友。其实&#xff0c;从零开始学习AI大模型是可以做到的&#xff0c;关键在于你的学习方法和投入的时间。下面我们来详细聊一聊这个问题。 学习时间 自学&#xff1a; 如果你选择自学&#xff0c;…

攻防靶场(34):隐蔽的计划任务提权 Funbox1

目录 1. 侦查 1.1 收集目标网络信息&#xff1a;IP地址 1.2 主动扫描&#xff1a;扫描IP地址段 1.3 搜索目标网站 2. 初始访问 2.1 有效账户&#xff1a;默认账户 2.2 利用面向公众的应用 2.3 有效账户&#xff1a;默认账户 3. 权限提升 3.1 计划任务/作业&#xff1a;Cron 靶场…

Java高频面试之SE-11

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本牛马baby今天又来了&#xff01;哈哈哈哈哈嗝&#x1f436; Java中是引用传递还是值传递&#xff1f; 在 Java 中&#xff0c;方法参数传递是通过 值传递 的方式实现的&#xff0c;但这可能会引起一…

2.两数相加--力扣

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …

与Linux的设备树文件(dts)的基础知识【编写和操作】

编写相关 01-双引号中的内容表示字符串,<>中的内容表示数值 示例如下&#xff1a; / {swh_led0 {compatible "swh_leddrv";pin <0x00030001>;};.......};compatible的具体内容为字符串swh_leddrv&#xff0c;而pin的值为数值0x00030001。 操作相关…

STM32第6章、WWDG

一、简介 WWDG&#xff1a;全称Window watchdog&#xff0c;即窗口看门狗&#xff0c;本质上是一个能产生系统复位信号和提前唤醒中断的计数器。 特性&#xff1a; 是一个递减计数器。 看门狗被激活后&#xff0c; 当递减计数器值从 0x40减到0x3F时会产生复位&#xff08;即T6位…

国产fpga nvme ip高速存储方案设计

国产高速存储方案主要是使用nvme ip实现高速存储方案&#xff0c;nvme ip采用纯verilog语言实现&#xff0c;用户拿到nvme ip使用起来也很简单。 先看看效果如 zu7eg板子&#xff0c;这个芯片支持pcie3.0 x4. zynq 7045板子只支持pcie 2.0 x4 速度测试&#xff0c;测试nvme …

《框架程序设计》复习题解析-1

目录 单选题 1.以下关于Maven说法错误的是&#xff1f;&#xff08; &#xff09;。 2.在项目的开发过程中&#xff0c;MyBatis承担的责任是( ) 3.当项目引用依赖的范围设置为以下&#xff08; &#xff09;选项时&#xff0c;表示依赖在编译时是必需的&#xff0c;但在运…

STM32F103的ADC通道映射

ADC通道映射 STM32F103带3个ADC控制器&#xff0c;一共支持23个通道&#xff0c;包括21个外部和2个内部信号源。ADC1控制器最多有18个通道&#xff0c;包括16个外部和2个内部信号源。 ADC1和ADC2的16个外部通道相同&#xff0c;且ADC1和ADC2共用一个系统中断向量&#xff0c;A…

Vue2+OpenLayers使用Overlay实现点击获取当前经纬度信息(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、代码实现 关键参数&#xff1a; 实现思路&#xff1a; 核心代码&#xff1a; 完整代码&#xff1a; 四、Gitee源码 一、案例截图 二、安装OpenLayers库 npm install ol 三、代码实现 覆盖物&#xff08;Overlay&#xf…

[Transformer] The Structure of GPT, Generative Pretrained Transformer

The Structure of Generative Pretrained Transformer Reference: The Transformer architecture of GPT models How GPT Models Work

【芯片封测学习专栏 -- Substrate | RDL Interposer | Si Interposer | 嵌入式硅桥(EMIB)详细介绍】

请阅读【嵌入式开发学习必备专栏 Cache | MMU | AMBA BUS | CoreSight | Trace32 | CoreLink | ARM GCC | CSH】 文章目录 OverviewSubstrate&#xff08;衬底或基板&#xff09;Substrate 定义Substrate 特点与作用Substrate 实例 RDL Interposer&#xff08;重布线层中介层&a…

基于单片机的无线气象仪系统设计(论文+源码)

1系统方案设计 如图2.1所示为无线气象仪系统设计框架。系统设计采用STM32单片机作为主控制器&#xff0c;结合DHT11温湿度传感器、光敏传感器、BMP180气压传感器、PR-3000-FS-N01风速传感器实现气象环境的温度、湿度、光照、气压、风速等环境数据的检测&#xff0c;并通过OLED1…

【JavaWeb】JavaWeb入门之Tomcat详解

目录 1.Java Web前奏 1.1.C/S结构 1.2.B/S结构 1.3.静态网页和动态网页 1.4.常见的网页 1.5.Web服务器 2.HTTP协议 2.1.HTTP协议概念 2.2.无状态协议 2.3.HTTP1.0和HTTP1.1 2.4.请求协议和响应协议 2.5.请求协议 2.5.1.GET请求 2.5.2.POST请求 2.6.响应协议 1.J…

【SpringBoot】@Value 没有注入预期的值

问题复现 在装配对象成员属性时&#xff0c;我们常常会使用 Autowired 来装配。但是&#xff0c;有时候我们也使用 Value 进行装配。不过这两种注解使用风格不同&#xff0c;使用 Autowired 一般都不会设置属性值&#xff0c;而 Value 必须指定一个字符串值&#xff0c;因为其…

nginx反向代理和负载均衡的区别

1、反向代理&#xff0c;不需要服务器池&#xff0c;直接代理某台服务器 location / {proxy_pass http://192.168.18.201;proxy_set_header Host $host;proxy_set_header X-Forwarded-For $remote_addr; }proxy_set_header Host $host; …

uniApp通过xgplayer(西瓜播放器)接入视频实时监控

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

数据结构与算法之二叉树: LeetCode 701. 二叉搜索树中的插入操作 (Ts版)

二叉搜索树中的插入操作 https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/ 描述 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 value &#xff0c;将值插入二叉搜索树返回插入后二叉搜索树的根节点。 输入数据 保…

vue el-table 数据变化后,高度渲染问题

场景&#xff1a;el-table设置了height属性&#xff0c;但是切换查询条件后再次点击查询重新获取data时&#xff0c;el-table渲染的高度会有问题&#xff0c;滚动区域变矮了。 解决办法&#xff1a;使用doLayout方法‌&#xff0c;在表格数据渲染后调用doLayout方法可以重新布局…