.d.ts文件
.d.ts文件是做什么的?
文件扩展名为.d.ts的文件是TypeScript中的声明文件,用来对对应的Javascript文件中的函数、变量等的数据没有数据类型的情况进行声明、补充完善。比如Javascript中的函数的入参、返回值等这些数据没有定义静态的数据类型,不满足TypeScript编译器的要求。
它允许开发者为JavaScript库或没有直接包含类型信息的代码提供类型注释,使得TypeScript编译器能够理解这些代码的结构和类型,从而在开发阶段提供静态类型检查、智能提示等功能,而不会对原始代码进行任何修改或编译操作。
为什么需要.d.ts文件?
1、可以提升开发效率:通过类型提示,开发者可以在编码时获得即时反馈,减少因类型错误导致的运行时问题。
2、增强了代码的可读性和可维护性:类型定义文件为代码提供了清晰的接口描述,便于团队成员理解代码的结构和意图。
3、支持第三方库:许多流行的JavaScript库并未直接提供TypeScript支持,通过社区维护的.d.ts文件,可以让这些库无缝集成到TypeScript项目中。
.d.ts文件的结构
一个简单的.d.ts文件可能包含以下内容:
// 声明一个全局的接口
declare interface NavigationCommonTitle {
main: string;
sub: string;
}
// 定义一个函数式接口并声明一个全局变量
interface ErrorConstructor {
new(message?: string): Error;
(message?: string): Error;
readonly prototype: Error;
}
declare var Error: ErrorConstructor;
// 声明一个模块
declare module 'my-module' {
export function greet(name: string): string;
}
declare关键字用来定义类型信息而不实际使用它,var用于声明全局变量,而module则用于定义模块及其导出的成员。
模块声明
对于npm包,通常会采用外部模块的声明方式:
// my-module.d.ts
declare module 'my-module' {
export interface User {
id: number;
name: string;
}
export function fetchUser(id: number): Promise<User>;
}
全局声明
有时侯需要向全局作用域添加类型定义,比如声明jQuery:
// jquery.d.ts
declare namespace jQuery {
function fn(name: string, selector: string): JQuery;
interface JQuery {
ajax(url: string, settings?: any): JQuery.jqXHR;
// ...其他jQuery方法
}
}
declare var $: JQueryStatic;
interface JQueryStatic extends JQuery {}
这里声明了jQuery的命名空间、静态成员和JQuery接口,使在TypeScript中编辑器能够识别并提供jQuery相关的方法和属性的智能提示。
在工程中使用.d.ts文件
引入三方js库以及类型定义
a、在TypeScript工程中引入:
对于第三方库,我们通常不需要手动创建.d.ts文件,因为TypeScript社区已经维护了一个庞大的类型定义仓库——DefinitelyTyped。使用npm安装对应的类型包即可:
npm install --save-dev @types/jquery
安装后,TypeScript编译器会自动找到并使用这些类型定义。
b、在Harmony工程中引入:
在开源鸿蒙仓的js类库中找到一个js库,比如spark-md5。在terminal中,切到目标module后,可以使用以下命令引入:
ohpm install spark-md5
在代码中使用时,import该库。
import spark from 'spark-md5';
自定义类型定义
a、对于Typescript工程:
对于自定义的JavaScript库或项目内部的模块,可以手动创建.d.ts文件,并确保其路径被tsconfig.json文件正确引用。例如,对于项目内的某个模块,可以在其目录下创建同名的.d.ts文件:
// tsconfig.json
{
"compilerOptions": {
// ...
"typeRoots": ["node_modules/@types", "src/types"] // 添加自定义类型定义目录
},
// ...
}
然后,在相应的目录下创建.d.ts文件,如src/types/myModule.d.ts。
b、对于Harmony工程:
在适配js库前,使用js-e2e工具扫描该三方库,检查库中是否存在依赖了node.js/web内置模块的情况。如果扫描结果显示库中没有依赖node.js/web内置模块,那么,这个库可以比较轻松地进行适配。注意:如果库中大量依赖了node.js/web内置组件,这时可能需要fork出源库代码,进行侵入式地修改,或者换其他三方库。
(推荐帖子:HarmonyOS 鸿蒙应用开发(十、第三方开源js库移植适配指南)_鸿蒙三方库-CSDN博客)
另外注意:
1、在移植三方js库时,对模块的导入导出是可能需要修改三方库里源码的。
因为Harmony的ArkTS(Ark TypeScript)使用的模块规范是ES6模块规范,而不是CommonJS模块规范。ES6模块规范(也称为ECMAScript 2015模块规范)是一种现代的模块系统,它使用import和export关键字来导入和导出模块成员。ES6模块规范支持静态导入和导出,具有更好的树摇(tree shaking)和代码拆分(code splitting)特性,有助于优化应用程序的性能和大小。相比之下,CommonJS模块规范是一种较旧的模块系统,它使用require和module.exports来导入和导出模块成员。CommonJS模块规范主要用于Node.js环境,并且在一些旧的浏览器环境中也有支持。
2、常用的三方开源js库一般都能在Typescript社区找到对应的.d.ts文件。
那么要在Harmony中使用时,推荐使用能在Typescript社区找到对应的.d.ts声明文件的三方js库,这样,我们只需要对该.d.ts文件进行一些稍微优化就可以引入到Harmony工程中使用了。
比如移植sm-crypto库:
a、找到其.d.ts声明文件并根据需要进行修改。比如它里面依赖了yyz116/jsbn的大数库。
//index.d.ts
import jsbn from '@yyz116/jsbn';
export interface KeyPairHex {
privateKey: string;
publicKey: string;
}
export interface KeyPairPoint extends KeyPairHex {
k: jsbn.BigInteger;
x1: jsbn.BigInteger;
}
/**
* Cipher Mode
* - `0`:C1C2C3
* - `1`:C1C3C2
*/
export type CipherMode = 0 | 1;
export namespace sm2 {
// TODO Type of parameter of jsbn.BigInteger constructor
function generateKeyPairHex(): KeyPairHex;
function doEncrypt(msg: string | ArrayLike<number>, publicKey: string, cipherMode?: CipherMode): string;
function doDecrypt(encryptData: string, privateKey: string, cipherMode?: CipherMode, outputType?: {
output?: "string" | "array";
}): string;
function doSignature(msg: string | number[], privateKey: string, options?: {
pointPool?: KeyPairPoint[] | undefined;
der?: boolean | undefined;
hash?: boolean | undefined;
publicKey?: string | undefined;
userId?: string | undefined;
}): string;
function doVerifySignature(msg: string | number[], signHex: string, publicKey: string, options?: {
der?: boolean | undefined;
hash?: boolean | undefined;
userId?: string | undefined;
}): boolean;
function getPoint(): KeyPairPoint;
}
export function sm3(input: string | ArrayLike<number>, hmac?: {
key: HexString | ArrayLike<number>;
mode?: "hmac";
}): string;
// SM4.encrypt() expects UTF8 strings (such as "hello"), while SM4.decrypt() expects hex strings (such as "8d0a1f").
export type HexString = string;
export type UTF8String = string;
export interface SM4ModeBase {
padding?: "none" | "pkcs#5" | "pkcs#7";
mode?: "cbc";
iv?: number[] | HexString;
}
export interface SM4Mode_StringOutput extends SM4ModeBase {
output: "string";
}
export interface SM4Mode_ArrayOutput extends SM4ModeBase {
output: "array";
}
export namespace sm4 {
function encrypt(
inArray: number[] | UTF8String,
key: number[] | HexString,
mode?: SM4ModeBase | SM4Mode_StringOutput,
): string;
function encrypt(inArray: number[] | UTF8String, key: number[] | HexString, mode: SM4Mode_ArrayOutput): number[];
function decrypt(
inArray: number[] | HexString,
key: number[] | HexString,
mode?: SM4ModeBase | SM4Mode_StringOutput,
): string;
function decrypt(inArray: number[] | HexString, key: number[] | HexString, mode: SM4Mode_ArrayOutput): number[];
}
b、以es6的模块引入方式对index.js文件修改如下:
// index.js
import * as Sm2 from './sm2/index.js';
import {sm3} from './sm3/index.js';
import * as Sm4 from './sm4/index.js';
export { Sm2 as sm2 };
export { sm3 };
export { Sm4 as sm4 };
c、sm3.js中的原有的CommonJS模块规范,需要修改如下:
// sm3.js
import {sm3 as Sm3, hmac } from '../sm2/sm3'
......
/*
module.exports = function (input, options) {
input = typeof input === 'string' ? utf8ToArray(input) : Array.prototype.slice.call(input)
if (options) {
const mode = options.mode || 'hmac'
if (mode !== 'hmac') throw new Error('invalid mode')
let key = options.key
if (!key) throw new Error('invalid key')
key = typeof key === 'string' ? hexToArray(key) : Array.prototype.slice.call(key)
return ArrayToHex(hmac(input, key))
}
return ArrayToHex(sm3(input))
}
* */
export function sm3 (input, options) {
input = typeof input === 'string' ? utf8ToArray(input) : Array.prototype.slice.call(input)
if (options) {
const mode = options.mode || 'hmac'
if (mode !== 'hmac') throw new Error('invalid mode')
let key = options.key
if (!key) throw new Error('invalid key')
key = typeof key === 'string' ? hexToArray(key) : Array.prototype.slice.call(key)
return ArrayToHex(hmac(input, key))
}
return ArrayToHex(Sm3(input))
}
混合使用JavaScript和TypeScript
当项目中同时包含了JavaScript和TypeScript文件时,.d.ts文件可以帮助TypeScript理解JavaScript文件中的类型信息。对于未使用ES6模块的JavaScript文件,可以通过/// 指令来引入类型定义:
// myScript.js
/// <reference path="./myScript.d.ts" />
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
sayHello('World');
对应的.d.ts文件中定义该JavaScript文件的类型信息:
// myScript.d.ts
declare function sayHello(name: string): void;
interface里的方法没有名称
在TypeScript中,interface里的方法如果没有名称,则表示这是一个函数的类型声明,而不是interface的一个方法。这通常用于函数的重载。
示例:
interface MyInterface {
(someParam: string): number;
}
这里的interface MyInterface是表示一个函数,它接受一个字符串参数,并返回一个数字。这是TypeScript中的函数类型的重载机制。
在Harmony的SDK中有很多.d.ts文件里有这样的情况,大多数是由于要适配Javascript中的函数而定义的interface。
函数示例:
function MyInterface(someParam: string): number {
// 实现
return 42;
}
参考帖子:
TypeScript 中的.d.ts文件是什么?如何在项目中使用? TypeScript 中的.d.ts文件是什么?如何在项目中使用?-CSDN博客
HarmonyOS 鸿蒙应用开发(十、第三方开源js库移植适配指南) HarmonyOS 鸿蒙应用开发(十、第三方开源js库移植适配指南)_鸿蒙三方库-CSDN博客