5.TypeScript声明文件与内置对象完全指南

小鱼
2025-09-11 15:40:42
TypeScript

声明文件:连接JavaScript与TypeScript的桥梁

什么是声明文件?

声明文件(.d.ts)是TypeScript中一种特殊的文件,它不包含具体的实现代码,只包含类型声明。这些文件让TypeScript能够理解已有的JavaScript代码库,提供类型检查和智能提示功能。

为什么需要声明文件?

当我们在TypeScript项目中使用第三方JavaScript库时,TypeScript编译器无法识别这些库的类型信息。声明文件解决了这个问题:

typescript

// 没有声明文件时,TypeScript会报错
// Error: Cannot find name 'jQuery'
jQuery('#my-element');

声明语句基础

使用 declare关键字可以告诉TypeScript某个变量或类型的存在:

typescript

// 简单的声明语句
declare var jQuery: (selector: string) => any;

// 现在可以使用jQuery了
jQuery('#my-element');

使用现有的声明文件

大多数流行的JavaScript库都有现成的声明文件,可以通过npm安装:

bash

# 安装jQuery的声明文件
npm install @types/jquery --save-dev

安装后,TypeScript会自动识别这些类型定义,无需额外配置。

自定义声明文件

当使用没有现成声明文件的库时,可以创建自己的声明文件:

typescript

// custom-library.d.ts
declare module 'custom-library' {
  export function doSomething(config: any): void;
  export interface Config {
    apiUrl: string;
    timeout?: number;
  }
}

然后在代码中使用:

typescript

import { doSomething, Config } from 'custom-library';

const config: Config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
};

doSomething(config);

TypeScript内置对象

TypeScript提供了对JavaScript内置对象的类型支持,这些对象可以直接在代码中使用而无需额外声明。

ECMAScript内置对象

TypeScript完整支持ECMAScript标准定义的内置对象:

typescript

// Boolean对象
let boolObj: Boolean = new Boolean(true);
let primitiveBool: boolean = Boolean(true);

// Number对象
let numObj: Number = new Number(42);
let primitiveNum: number = Number('42');

// String对象
let strObj: String = new String('hello');
let primitiveStr: string = String(123);

// Date对象
let now: Date = new Date();
let timestamp: number = now.getTime();

// RegExp对象
let regex: RegExp = /^[a-zA-Z]+$/;
let regexObj: RegExp = new RegExp('^\\d+$');

// Error对象
let error: Error = new Error('Something went wrong');

重要区别:注意基本类型与包装对象的区别。基本类型(boolean, number, string)与它们的包装对象(Boolean, Number, String)是不同的类型,不能直接互换使用。

DOM和BOM内置对象

TypeScript还提供了对浏览器环境对象的完整类型支持:

DOM操作

typescript

// 获取元素
const container: HTMLElement = document.getElementById('app')!;
const buttons: NodeList = document.querySelectorAll('.btn');

// 创建元素
const newDiv: HTMLDivElement = document.createElement('div');
newDiv.className = 'panel';
newDiv.textContent = 'Hello World';

// 添加事件监听
document.addEventListener('click', (event: MouseEvent) => {
  console.log(`Clicked at: ${event.clientX}, ${event.clientY}`);
});

// 使用DocumentFragment提升性能
const fragment: DocumentFragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
  const item = document.createElement('li');
  item.textContent = `Item ${i}`;
  fragment.appendChild(item);
}
container.appendChild(fragment);

常用DOM类型

typescript

// 元素类型
const div: HTMLDivElement = document.createElement('div');
const input: HTMLInputElement = document.createElement('input');
const img: HTMLImageElement = document.createElement('img');

// 事件类型
element.addEventListener('keydown', (event: KeyboardEvent) => {
  if (event.key === 'Enter') {
    // 处理回车键
  }
});

element.addEventListener('submit', (event: SubmitEvent) => {
  event.preventDefault();
  // 处理表单提交
});

BOM对象

typescript

// 使用Window对象
const width: number = window.innerWidth;
const height: number = window.innerHeight;

// 使用Location对象
const currentUrl: string = window.location.href;
const hostname: string = window.location.hostname;

// 使用Navigator对象
const userAgent: string = window.navigator.userAgent;
const isMobile: boolean = /Mobile/.test(userAgent);

// 使用History对象
window.history.pushState({}, '', '/new-page');

// 使用Storage
localStorage.setItem('theme', 'dark');
const theme: string | null = localStorage.getItem('theme');

// 定时器
const timerId: number = setTimeout(() => {
  console.log('Delayed message');
}, 1000);

// 清除定时器
clearTimeout(timerId);

实践应用场景

场景1:增强第三方库的类型安全

typescript

// 假设我们使用一个图表库,但没有类型定义
declare module 'simple-charts' {
  export interface ChartConfig {
    type: 'bar' | 'line' | 'pie';
    data: Array<number>;
    labels?: Array<string>;
    colors?: Array<string>;
  }
  
  export function createChart(container: HTMLElement, config: ChartConfig): void;
  export function updateChart(data: Array<number>): void;
}

// 使用
import { createChart, ChartConfig } from 'simple-charts';

const config: ChartConfig = {
  type: 'bar',
  data: [12, 19, 3, 5, 2],
  labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple']
};

createChart(document.getElementById('chart')!, config);

场景2:安全操作DOM

typescript

// 安全的DOM操作函数
function getElement<T extends HTMLElement>(selector: string): T | null {
  return document.querySelector(selector);
}

function getRequiredElement<T extends HTMLElement>(selector: string): T {
  const element = document.querySelector(selector);
  if (!element) {
    throw new Error(`Element with selector "${selector}" not found`);
  }
  return element as T;
}

// 使用
const form = getRequiredElement<HTMLFormElement>('#user-form');
const submitButton = getElement<HTMLButtonElement>('#submit-btn');

form.addEventListener('submit', (event: SubmitEvent) => {
  event.preventDefault();
  if (submitButton) {
    submitButton.disabled = true;
    submitButton.textContent = 'Processing...';
  }
  
  // 处理表单提交
});

场景3:类型安全的本地存储

typescript

// 包装localStorage,提供类型安全
class TypedStorage<T> {
  constructor(private key: string) {}
  
  get(): T | null {
    const item = localStorage.getItem(this.key);
    return item ? JSON.parse(item) : null;
  }
  
  set(value: T): void {
    localStorage.setItem(this.key, JSON.stringify(value));
  }
  
  remove(): void {
    localStorage.removeItem(this.key);
  }
}

// 使用
interface UserPreferences {
  theme: 'light' | 'dark';
  language: string;
  notifications: boolean;
}

const userPrefsStorage = new TypedStorage<UserPreferences>('user-preferences');

// 保存设置
userPrefsStorage.set({
  theme: 'dark',
  language: 'zh-CN',
  notifications: true
});

// 读取设置
const prefs = userPrefsStorage.get();
if (prefs) {
  console.log(`Current theme: ${prefs.theme}`);
}

最佳实践与注意事项

  1. 优先使用现成的声明文件:在npm上搜索 @types/包名,大多数流行库都有现成的类型定义

  2. 合理组织自定义声明文件

    • 将全局声明放在 globals.d.ts
    • 按模块组织的声明放在各自模块的 .d.ts文件中
    • 使用三斜线指令引用依赖:/// <reference types="some-library" />
  3. 区分基本类型和包装对象
    typescript

    // 正确:使用基本类型
    let isDone: boolean = false;
    let count: number = 42;
    let name: string = "TypeScript";
    
    // 避免:不必要的包装对象
    let isDone: Boolean = new Boolean(false); // 不推荐
    
  4. 安全访问DOM元素
    typescript

    // 使用非空断言时要小心
    const element = document.getElementById('my-element')!;
    
    // 更好的做法:检查元素是否存在
    const element = document.getElementById('my-element');
    if (element) {
      // 安全操作元素
    }
    
  5. 利用类型断言处理不确定的类型
    typescript

    // 当TypeScript无法推断正确类型时
    const canvas = document.getElementById('my-canvas') as HTMLCanvasElement;
    const context = canvas.getContext('2d') as CanvasRenderingContext2D;
    
Copyright © 2025 aipanzhou.com All Rights Reserved.
备案号:黔ICP备2023000741号-1
贵公网安备 52022202000096号