当前位置: 首页 > news >正文

TypeScript 在前端开发中的应用实践

TypeScript 在前端开发中的应用实践

TypeScript 已经成为前端开发领域越来越多开发者的首选工具。它是一种静态类型的超集,由 Microsoft 推出,为开发者提供了强大的静态类型检查、面向对象编程和模块化开发的特性,解决了 JavaScript 的动态类型特性带来的一些问题。

在本篇博文中,我们将深入探讨 TypeScript 在前端开发中的应用实践。我们将介绍 TypeScript 的基础知识,包括数据类型、函数、类与面向对象编程以及模块化开发。了解这些基础知识将有助于开发者更好地理解 TypeScript 的工作原理和优势。我们将总结本文的内容,强调 TypeScript 在前端开发中的重要性和实际应用的价值。通过本篇博文的学习,读者将能够全面了解 TypeScript,并学会如何在实践中应用它来提高前端开发的效率和代码质量。

让我们一起探索 TypeScript 在前端开发中的应用实践,提升我们的技术能力和开发水平!

文章目录

  • TypeScript 在前端开发中的应用实践
  • 一、TypeScript的基础知识
    • 1.1 数据类型
      • 1.1.1 原始数据类型
      • 1.1.2 数组与元组
      • 1.1.3 对象与接口
    • 1.2 函数
      • 1.2.1 函数定义与参数类型
      • 1.2.2 函数返回类型与`void`类型
    • 1.3 类与面向对象编程
      • 1.3.1 类的定义与构造函数
      • 1.3.2 继承与多态
      • 1.3.3 访问修饰符(public、private、protected)
    • 1.4 模块化编程
      • 1.4.1 导入与导出模块
      • 1.4.2 命名空间与模块的区别
  • 二、TypeScript 在实践中的应用
    • 2.1 静态类型检查与编译时错误检测
      • 2.1.1 TypeScript 编译器的工作原理
      • 2.1.2 静态类型检查的优势
    • 2.2 编写可维护的代码
      • 2.2.1 类型注解与代码可读性
      • 2.2.2 代码重构与 IDE 支持
    • 2.3 使用 TypeScript 框架与工具
      • 2.3.1 Angular 框架的 TypeScript 支持
      • 2.3.2 使用 TypeScript 进行 React 开发
      • 2.3.3 编写测试用例与类型声明文件
  • 三、最佳实践与技巧
    • 3.1 类型声明文件的编写与维护
    • 3.2 使用泛型提高代码的重用性
    • 3.3 扩展 JavaScript 库与第三方模块的声明
  • 四、总结

一、TypeScript的基础知识

1.1 数据类型

1.1.1 原始数据类型

在 TypeScript 中,原始数据类型包括:

  1. number:表示数值,包括整数和浮点数。
  2. string:表示文本,使用单引号或双引号括起来。
  3. boolean:表示布尔值,可以是truefalse
  4. null:表示空值。
  5. undefined:表示未定义的值。
  6. symbol:表示独一无二的值,用于创建唯一的对象属性。

以下是一些示例:

let age: number = 25;
let name: string = "John";
let isReady: boolean = true;
let value: null = null;
let data: undefined = undefined;
let id: symbol = Symbol("id");

1.1.2 数组与元组

在 TypeScript 中,除了原始数据类型,还可以使用数组和元组来处理多个值的集合。

数组类型

数组是一组具有相同类型的值的集合。在 TypeScript 中,可以使用以下两种方式表示数组类型:

  1. 类型[]:使用类型后跟一个方括号,表示该数组中只能存储指定类型的元素。
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["John", "Jane", "Alice"];
  1. Array<类型>:使用Array关键字后跟尖括号,尖括号中指定元素的类型。
let numbers: Array<number> = [1, 2, 3, 4, 5];
let names: Array<string> = ["John", "Jane", "Alice"];

可以使用索引访问数组中的元素,并对数组进行遍历、添加或删除元素等操作。

元组类型

元组是一种表示已知类型和固定长度的数组。在 TypeScript 中,可以使用以下方式定义元组类型:

let person: [string, number] = ["John", 25];

上面的示例中,person是一个长度为 2 的数组,第一个元素是字符串类型(姓名),第二个元素是数字类型(年龄)。元组中的每个元素可以具有不同的类型。

可以使用索引访问元组中的元素,并对元组进行解构赋值和遍历等操作。

使用数组和元组可以更好地组织和处理多个值的集合,并提供类型安全性和代码可读性。在实际开发中,根据需要选择使用数组或元组来表示和操作不同类型的集合数据。

1.1.3 对象与接口

在 TypeScript 中,对象和接口是处理复杂数据类型的重要概念。

对象类型

对象是一组属性的集合,每个属性都有一个键值对。在 TypeScript 中,可以使用以下方式表示对象类型:

let person: { name: string, age: number } = { name: "John", age: 25 };

上面的示例中,person是一个对象,有两个属性:name是字符串类型,age是数字类型。

对象类型可以定义方法和嵌套对象等复杂结构。

接口

接口是一种抽象的数据类型,用于定义对象的结构和行为。使用接口可以提高代码的可读性、可维护性和可复用性。在 TypeScript 中,可以使用以下方式声明接口:

interface Person {name: string;age: number;
}let person: Person = { name: "John", age: 25 };

上面的示例中,Person接口定义了一个对象的结构,包括name属性(字符串类型)和age属性(数字类型)。通过将对象的类型指定为Person接口,我们可以确保该对象符合接口定义的结构。

接口还支持可选属性、只读属性、函数类型等高级特性,使得接口更加灵活和强大。

通过使用对象和接口,我们可以更好地描述和操作复杂的数据类型,并增加代码的可读性和可维护性。在实际开发中,根据需求和设计,选择合适的方式来表示和处理对象类型数据。

1.2 函数

1.2.1 函数定义与参数类型

在 TypeScript 中,我们可以使用箭头函数(=>)或关键字function来定义函数。同时,我们也可以为函数的参数指定类型。

// 箭头函数
const add = (x: number, y: number): number => {return x + y;
};// function 关键字
function multiply(x: number, y: number): number {return x * y;
}

在上面的例子中,函数addmultiply都有两个参数,并且参数类型都是number。它们都返回一个number类型的结果。

1.2.2 函数返回类型与void类型

函数也可以指定返回类型。如果函数不返回任何值,则可以使用void类型。

function sayHello(name: string): void {console.log(`Hello, ${name}!`);
}function calculateSum(x: number, y: number): number {return x + y;
}

在上面的例子中,函数sayHello没有返回值,因此返回类型为void。而函数calculateSum返回两个参数的和,因此返回类型为number

1.3 类与面向对象编程

1.3.1 类的定义与构造函数

在 TypeScript 中,可以使用class关键字定义类。类是一种面向对象编程的核心概念,用于描述具有相同属性和方法的对象。

class Person {name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}sayHello(): void {console.log(`Hello, my name is ${this.name}.`);}
}

在上面的示例中,Person类定义了nameage两个属性,并且有一个sayHello的方法。构造函数constructor在实例化类时进行初始化操作。

可以使用new关键字实例化一个类,并访问其属性和方法。

let person = new Person("John", 25);
console.log(person.name); // 输出:John
person.sayHello(); // 输出:Hello, my name is John.

1.3.2 继承与多态

继承是面向对象编程中的重要概念,它允许从现有的类派生出新的类,并继承父类的属性和方法。在 TypeScript 中,可以使用extends关键字实现类之间的继承。

class Student extends Person {studentId: string;constructor(name: string, age: number, studentId: string) {super(name, age);this.studentId = studentId;}study(): void {console.log(`${this.name} is studying.`);}
}

在上面的示例中,Student类继承自Person类,并添加了一个studentId属性和一个study方法。

通过继承,子类可以重用父类的属性和方法,并可以自定义新的属性和方法。

let student = new Student("John", 20, "12345");
console.log(student.name); // 输出:John
student.sayHello(); // 输出:Hello, my name is John.
student.study(); // 输出:John is studying.

多态是面向对象编程中的一个重要概念,它允许不同的对象对同一方法进行不同的实现。在 TypeScript 中,通过方法的重写可以实现多态性。

class Animal {sound(): void {console.log("The animal makes a sound.");}
}class Dog extends Animal {sound(): void {console.log("The dog barks.");}
}class Cat extends Animal {sound(): void {console.log("The cat meows.");}
}let animal: Animal = new Animal();
animal.sound(); // 输出:The animal makes a sound.let dog: Animal = new Dog();
dog.sound(); // 输出:The dog barks.let cat: Animal = new Cat();
cat.sound(); // 输出:The cat meows.

在上面的示例中,Animal是一个基类,DogCat是它的子类。它们都有一个名为sound的方法,但是每个子类都对该方法进行了不同的实现。

通过将对象声明为基类类型,可以实现多态性,即使具体的类型是子类,也可以调用基类中定义的方法,根据对象的实际类型进行不同的行为。

1.3.3 访问修饰符(public、private、protected)

访问修饰符用于控制类成员的访问权限,通过它们可以限制成员的可访问性。

  • public:默认的访问修饰符,表示成员可以在任何地方访问。
  • private:表示成员只能在定义它的类内部访问。
  • protected:表示成员可以在定义它的类及其子类中访问。
class Person {public name: string;private age: number;protected gender: string;constructor(name: string, age: number, gender: string) {this.name = name;this.age = age;this.gender = gender;}
}

在上面的示例中,name属性是公共的,可以在任何地方进行访问。age属性是私有的,只能在定义它的类内部访问。gender属性是受保护的,可以在定义它的类及其子类中访问。

访问修饰符也可以应用于类的方法。

class Person {public sayHello(): void {console.log("Hello!");}private whisperSecret(): void {console.log("This is a secret.");}protected showAge(): void {console.log("I am 25 years old.");}
}

在上面的例子中,sayHello方法具有公共的访问修饰符,可以在任何地方进行调用。whisperSecret方法具有私有的访问修饰符,只能在类内部进行调用。showAge方法具有受保护的访问修饰符,可以在定义它的类及其子类中进行调用。

let person = new Person();
person.sayHello(); // 输出:Hello!// Error: Property 'whisperSecret' is private and only accessible within class 'Person'.
person.whisperSecret();// Error: Property 'showAge' is protected and only accessible within class 'Person' and its subclasses.
person.showAge();

通过访问修饰符,可以控制属性和方法的访问范围,提高代码的封装性和安全性,并在需要时允许子类进行继承和重写。

1.4 模块化编程

1.4.1 导入与导出模块

在 TypeScript 中,可以使用模块化编程来组织和管理代码。模块是独立的代码单元,可以包含变量、函数、类等。

导出模块:要在一个模块中导出变量、函数、类或其他定义,可以使用export关键字。

export const PI = 3.14;export function double(num: number): number {return num * 2;
}export class Circle {radius: number;constructor(radius: number) {this.radius = radius;}getArea(): number {return Math.PI * this.radius ** 2;}
}

在上面的示例中,通过export关键字将PI常量、double函数和Circle类导出为模块的公共接口。

导入模块:要在另一个模块中使用导出的变量、函数或类,可以使用import关键字进行导入。

import { PI, double, Circle } from "./math";console.log(PI); // 输出:3.14console.log(double(5)); // 输出:10let circle = new Circle(3);
console.log(circle.getArea()); // 输出:28.26

在上面的示例中,使用import关键字从./math模块中导入了PI常量、double函数和Circle类。然后就可以在当前模块中使用它们。

1.4.2 命名空间与模块的区别

命名空间和模块都用于组织和管理代码,但它们有一些区别。

命名空间:是一种在全局作用域下组织代码的方式,用于避免命名冲突。通过namespace关键字可以定义一个命名空间。

namespace MyNamespace {export const PI = 3.14;export function double(num: number): number {return num * 2;}export class Circle {radius: number;constructor(radius: number) {this.radius = radius;}getArea(): number {return Math.PI * this.radius ** 2;}}
}

在上面的示例中,MyNamespace是一个命名空间,包含了PI常量、double函数和Circle类。

要在另一个命名空间或模块中使用命名空间中的内容,可以使用命名空间的名称进行访问。

console.log(MyNamespace.PI); // 输出:3.14console.log(MyNamespace.double(5)); // 输出:10let circle = new MyNamespace.Circle(3);
console.log(circle.getArea()); // 输出:28.26

模块:是 TypeScript 中推荐的组织代码的方式,它提供了更强大的封装和代码重用。通过module关键字可以定义一个模块。

// math.ts
export const PI = 3.14;export function double(num: number): number {return num * 2;
}export class Circle {radius: number;constructor(radius: number) {this.radius = radius;}getArea(): number {return Math.PI * this.radius ** 2;}
}

在上面的示例中,math.ts是一个模块,包含了PI常量、double函数和Circle类。

要在另一个模块中使用模块中的内容,可以使用import关键字进行导入。

// app.ts
import { PI, double, Circle } from "./math";console.log(PI); // 输出:3.14console.log(double(5)); // 输出:10let circle = new Circle(3);
console.log(circle.getArea()); // 输出:28.26

与命名空间相比,模块更加灵活和可扩展,它支持更多的模块化特性,如导入和导出、默认导出等。因此,模块是 TypeScript 中更常用和推荐的代码组织方式。

总结

命名空间和模块都可以用于组织和管理代码,但它们有一些区别:

  • 命名空间是一种在全局作用域下组织代码的方式,用于避免命名冲突。
  • 模块是 TypeScript 中推荐的组织代码的方式,提供了更强大的封装和代码重用。

命名空间使用namespace关键字定义,可以通过命名空间的名称访问其中的内容。

模块使用module关键字定义,可以使用import关键字导入其他模块中的内容。

对于新的项目,推荐使用模块来组织和管理代码,它提供了更好的可扩展性和代码管理能力。

二、TypeScript 在实践中的应用

2.1 静态类型检查与编译时错误检测

2.1.1 TypeScript 编译器的工作原理

TypeScript 编译器是一种将 TypeScript 代码转换为 JavaScript 代码的工具。它通过以下几个步骤来实现这一过程:

  1. 词法分析(Lexical Analysis):将源代码分割成一个个的词法单元(tokens),如变量名、关键字、运算符等。词法分析器(Lexer)根据语言规范定义的词法规则来进行分析。

  2. 语法分析(Syntax Analysis):将词法单元组合成一个个的语法单元,如表达式、语句、函数等。语法分析器(Parser)根据语言规范定义的语法规则来进行分析,并构建一个抽象语法树(Abstract Syntax Tree,AST)。

  3. 语义分析(Semantic Analysis):对抽象语法树进行语义检查,包括变量声明的正确性、类型匹配等。语义分析器检查代码是否符合 TypeScript 的类型系统和语法规范。

  4. 类型检查(Type Checking):根据变量和函数的类型注解以及上下文推断,进行类型检查。类型检查器(Type Checker)验证代码中的类型是否一致,并提供类型提示和错误检测。

  5. 代码生成(Code Generation):根据语义分析和类型检查的结果,生成相应的 JavaScript 代码。生成的代码可以是 ES3、ES5、ES6 等不同版本的 JavaScript。

2.1.2 静态类型检查的优势

静态类型检查是 TypeScript 的一个主要特性,它在编译时期进行类型检查,有以下几个优势:

  1. 提前发现错误:静态类型检查可以在编译时期发现类型错误,避免在运行时期才发现隐含的类型问题。这有效地减少了调试和排查问题的时间,提高了代码的可靠性。

  2. 更好的代码维护性:通过类型注解和类型检查,可以提供更好的代码自我文档化能力,让代码更易于理解和维护。强类型的约束也能减少不必要的类型转换和异常情况的处理。

  3. 智能的开发工具支持:静态类型信息能够为开发工具(如代码编辑器、IDE)提供丰富的上下文信息,包括代码自动补全、类型推导、代码导航等功能,提高开发效率和代码质量。

  4. 更好的团队协作:静态类型检查可以规范代码的编写风格和接口定义,帮助团队成员遵循统一的规范,降低沟通成本,提高协作效率。

尽管静态类型检查会增加一定的开发成本,但它能够提供更安全、更可靠的代码,减少潜在的错误和问题。因此,在大型项目和团队开发中,静态类型检查是非常有价值的工具。

2.2 编写可维护的代码

2.2.1 类型注解与代码可读性

在 TypeScript 中,通过类型注解可以为变量、函数参数、函数返回值等添加类型信息。类型注解不仅可以提供给编译器进行类型检查,还可以提高代码的可读性和可维护性。

代码可读性

类型注解可以使代码更易于理解和阅读。通过类型注解,读者可以清楚地了解变量的预期类型,避免了对上下文的猜测。

// 未使用类型注解
function calculateArea(radius) {return Math.PI * radius * radius;
}// 使用类型注解
function calculateArea(radius: number): number {return Math.PI * radius * radius;
}

在上面的示例中,第二个函数使用了类型注解,明确表示了radius参数和函数返回值的类型,使代码更具可读性。

代码可维护性

类型注解还可以提高代码的可维护性。类型信息可以提供给开发工具,如代码编辑器和 IDE,以提供更好的代码补全、类型检查和错误提示。这样可以帮助开发人员更轻松地理解和修改代码,减少出错的机会。

// 使用类型注解
function calculateArea(radius: number): number {return Math.PI * radius * radius;
}calculateArea(5); // 编辑器会提示参数类型错误,应为提供了类型注解calculateArea("5"); // 编辑器会提示参数类型错误,应为提供了类型注解

在上面的示例中,如果在调用calculateArea函数时提供了错误的参数类型,编辑器会立即提示错误,帮助开发人员发现并修复问题。

2.2.2 代码重构与 IDE 支持

TypeScript 的静态类型检查和 IDE 的支持为代码重构提供了很大的便利。IDE 可以根据代码的语义和类型信息提供智能的重构工具,帮助开发人员进行代码重构和优化。

常见的代码重构操作包括函数提取、变量重命名、类型转换等。IDE 可以提供自动重命名、提取函数、提取常量等功能,避免手动修改大量重复的代码。

例如,在重命名变量时,IDE 可以自动更新所有引用该变量的代码,确保修改的一致性:

// 重命名前
let age = 25;
console.log(age);// 重命名后
let userAge = 25;
console.log(userAge);

IDE 还可以提供代码检查和错误提示,帮助开发人员在重构过程中发现潜在的问题,并提供修复建议。

通过利用 TypeScript 的静态类型检查和 IDE 的强大支持,开发人员可以更轻松地进行代码重构,提高代码的可维护性和可读性。

2.3 使用 TypeScript 框架与工具

2.3.1 Angular 框架的 TypeScript 支持

Angular 是一个基于 TypeScript 的前端框架,它提供了完整的 TypeScript 支持。使用 TypeScript 可以增加代码的可维护性和可读性,并提供静态类型检查和智能代码提示等功能。

在 Angular 中,可以使用 TypeScript 来定义组件、服务和指令等。通过使用类型注解和接口来明确数据类型,可以减少错误和提供更好的开发体验。

以下是一个使用 TypeScript 编写的 Angular 组件的示例:

import { Component } from '@angular/core';@Component({selector: 'app-my-component',templateUrl: './my-component.component.html',styleUrls: ['./my-component.component.css']
})
export class MyComponent {name: string = 'John Doe';age: number = 25;constructor() {this.greet();}greet(): void {console.log(`Hello, ${this.name}!`);}
}

在上面的示例中,我们使用 TypeScript 定义了一个名为MyComponent的 Angular 组件。通过使用类型注解,我们明确了name属性的类型为stringage属性的类型为number。我们还定义了一个greet方法,该方法在组件初始化时被调用,并将问候语打印到控制台。

2.3.2 使用 TypeScript 进行 React 开发

React 是另一个常用的前端框架,与 TypeScript 也有很好的兼容性。使用 TypeScript 可以为 React 应用程序提供更强大的类型检查和开发工具的支持。

在使用 TypeScript 进行 React 开发时,可以使用类型注解来定义组件的 Props 和 State 类型,以及函数组件的返回类型。这样可以确保数据的正确性,减少错误和调试时间。

以下是一个使用 TypeScript 编写的简单的 React 组件的示例:

import React, { useState } from 'react';interface CounterProps {initialValue: number;
}const Counter: React.FC<CounterProps> = ({ initialValue }) => {const [count, setCount] = useState(initialValue);const increment = () => {setCount(prevCount => prevCount + 1);};return (<div><p>Count: {count}</p><button onClick={increment}>Increment</button></div>);
};export default Counter;

在上面的示例中,我们定义了一个名为Counter的 React 函数组件。通过使用接口CounterProps,我们指定了组件的initialValue属性的类型。使用useState钩子来管理组件的状态,并使用箭头函数来定义用于增加计数器的increment函数。

2.3.3 编写测试用例与类型声明文件

使用 TypeScript 可以编写更准确的测试用例(Test Cases),以增加代码的可靠性。编写类型安全的测试代码可以防止错误的数据类型传递以及其他潜在的问题。

此外,当使用 TypeScript 开发库或框架时,编写类型声明文件(Type Declaration Files)也是很重要的一步。类型声明文件提供了对 JavaScript 库或框架的类型信息,使得在 TypeScript 项目中使用这些库时能够获得类型检查和代码提示的好处。

例如,当使用 TypeScript 开发一个与外部库集成的库时,可以编写相应的类型声明文件,以描述该库的类型和接口。这样可以为使用该库的开发人员提供更好的开发体验。

总而言之,使用 TypeScript 进行框架和工具的开发可以提供更好的类型检查、代码提示和开发体验。它可以增加代码的可维护性和可读性,并减少潜在的错误。同时,编写准确的测试用例和类型声明文件也是使用 TypeScript 进行开发的重要方面。

三、最佳实践与技巧

3.1 类型声明文件的编写与维护

类型声明文件(Type Declaration Files)是用来描述 JavaScript 库或模块的类型信息的文件。编写和维护好的类型声明文件可以为 TypeScript 项目提供更好的类型检查和代码提示。

以下是一些类型声明文件的编写与维护的最佳实践与技巧:

  1. 安装 @types 包:许多 JavaScript 库已经有对应的类型声明文件可供使用,它们通常以 @types 前缀命名。在安装第三方库时,可以检查是否已有对应的类型声明文件,如果有,则可以直接安装该类型声明文件,例如:npm install @types/library-name

  2. 创建自定义类型声明文件:如果找不到某个库的类型声明文件,或者想要修改现有的类型声明文件以适应特定需求,可以手动创建自定义的类型声明文件。通常,自定义类型声明文件的命名规则为 library-name.d.ts,比如 my-library.d.ts

  3. 使用全局声明:当调用全局对象或变量时,可以使用全局声明来告诉 TypeScript 这些对象或变量的类型。可以通过在全局声明文件中使用 declare 关键字来定义全局变量、函数和命名空间。

  4. 对类型进行维护:随着 JavaScript 库的版本变化,类型声明文件也需要随之更新和维护。有时候库的 API 可能发生变化,或者存在 bug 需要修复。当使用第三方库时,及时关注库的更新和发布的类型声明文件版本是很重要的。

  5. 测试类型声明文件:在编写或修改类型声明文件时,可以编写相应的测试用例来验证类型是否正确。可以创建一个专门的测试目录,编写以 .test-d.ts 结尾的测试类型声明文件,使用测试工具进行类型检查。

  6. 使用工具辅助编写:使用工具如 dts-gentsd 或者编辑器插件等可以辅助生成初始的类型声明文件,提高编写的效率和准确性。

3.2 使用泛型提高代码的重用性

泛型(Generics)是 TypeScript 中一个强大的特性,它可以在函数、类和接口中以参数化的方式使用类型。使用泛型可以提高代码的重用性和灵活性,使得代码更加通用和可扩展。

以下是一些使用泛型提高代码重用性的最佳实践和技巧:

  1. 函数泛型:在定义函数时,可以使用泛型来指定函数的参数类型或返回值类型。这样函数可以适用于不同类型的输入,并且在调用时不需要进行类型断言。
function identity<T>(value: T): T {return value;
}let result = identity<string>("Hello");
  1. 类泛型:类可以使用泛型来定义类的属性、方法和构造函数等的类型。这样可以创建可复用的类,并在实例化时指定具体的类型。
class Container<T> {private value: T;constructor(value: T) {this.value = value;}getValue(): T {return this.value;}
}let container = new Container<number>(42);
let value = container.getValue();
  1. 接口泛型:接口也可以使用泛型,以便在实现时指定类型。这样可以创建通用的接口,并在实现接口时指定具体的类型。
interface List<T> {add(item: T): void;get(index: number): T;
}class ArrayList<T> implements List<T> {private items: T[] = [];add(item: T): void {this.items.push(item);}get(index: number): T {return this.items[index];}
}let list = new ArrayList<number>();
list.add(1);
list.add(2);
let value = list.get(1); // value 的类型为 number
  1. 泛型约束:有时候需要对泛型进行约束,以限制可以使用的类型。可以使用 extends 关键字来约束泛型的类型范围。
interface Lengthwise {length: number;
}function getLength<T extends Lengthwise>(obj: T): number {return obj.length;
}let result = getLength("Hello"); // result 的类型为 number,因为字符串有 length 属性
  1. 多重泛型参数:可以在函数、类或接口中同时使用多个泛型参数,以处理多个类型的数据。
function pair<T, U>(value1: T, value2: U): [T, U] {return [value1, value2];
}let result = pair<string, number>("Hello", 42); // result 的类型为 [string, number]

使用泛型可以提高代码的灵活性和可重用性,使代码更加通用和类型安全。

3.3 扩展 JavaScript 库与第三方模块的声明

当使用 JavaScript 库或第三方模块时,为了获得更好的类型检查和代码提示,可以使用类型声明文件来扩展它们。

以下是一些扩展 JavaScript 库与第三方模块声明的最佳实践和技巧:

  1. 声明文件的获取与安装:首先检查是否有对应的类型声明文件可用,可以使用 @types 前缀的包进行安装。如果不存在对应的类型声明文件,可以尝试搜索社区维护的类型声明文件。

  2. 编写自定义声明文件:如果找不到合适的类型声明文件,可以手动编写自定义的声明文件。可以创建一个以 .d.ts 结尾的文件,并在其中编写对应库或模块的类型声明。

  3. 使用 declare 关键字:在类型声明文件中,可以使用 declare 关键字来告诉 TypeScript 有关库或模块的类型信息。可以声明全局变量、函数、类、接口等。

  4. 提交社区维护的声明文件:如果编写了通用的声明文件,可以将其贡献给社区维护的类型声明仓库,如 DefinitelyTyped。这样可以让其他开发人员受益,并帮助提高整个生态系统的质量。

  5. 更新与维护:随着库或模块的版本变化,类型声明文件也需要相应地更新与维护。及时关注库的更新,并与社区保持同步,确保类型声明文件的准确性和完整性。

在使用 JavaScript 库或第三方模块时,通过扩展它们的类型声明文件,可以实现更好的类型检查、代码提示和开发体验。这对于构建和维护 TypeScript 项目来说是非常有价值的。

四、总结

通过本文的介绍,我们了解到了 TypeScript 在前端开发中的重要作用和应用实践。TypeScript 提供了丰富的数据类型、接口和函数定义,帮助我们更准确地编写代码,并通过类型检查提高代码质量和可读性。它能与现有的 JavaScript 代码无缝集成,并具有跨浏览器和跨平台的兼容性。使用 TypeScript,我们可以在开发过程中更好地组织、维护和扩展代码,提高开发效率和团队协作能力。所以,不论是个人开发还是团队合作,TypeScript 都是值得探索和应用的重要工具。

相关文章:

TypeScript 在前端开发中的应用实践

TypeScript 在前端开发中的应用实践 TypeScript 已经成为前端开发领域越来越多开发者的首选工具。它是一种静态类型的超集&#xff0c;由 Microsoft 推出&#xff0c;为开发者提供了强大的静态类型检查、面向对象编程和模块化开发的特性&#xff0c;解决了 JavaScript 的动态类…...

商业密码应用安全性评估量化评估规则2023版更新点

《商用密码应用安全性评估量化评估规则》&#xff08;2023版&#xff09;已于2023年7月发布&#xff0c;将在8月1日正式执行。相比较2021版&#xff0c;新版本有多处内容更新&#xff0c;具体包括5处微调和5处较大更新。 微调部分&#xff08;5处&#xff09; 序号2021版本202…...

【软件测试】单元测试工具---Junit详解

1.junit 1.1 junit是什么 JUnit是一个Java语言的单元测试框架。 虽然我们已经学习了selenium测试框架&#xff0c;但是有的时候测试用例很多&#xff0c;我们需要一个测试工具来管理这些测试用例&#xff0c;Junit就是一个很好的管理工具&#xff0c;简单来说Junit是一个针对…...

【算法基础:搜索与图论】3.4 求最短路算法(Dijkstrabellman-fordspfaFloyd)

文章目录 求最短路算法总览Dijkstra朴素 Dijkstra 算法&#xff08;⭐原理讲解&#xff01;⭐重要&#xff01;&#xff09;&#xff08;用于稠密图&#xff09;例题&#xff1a;849. Dijkstra求最短路 I代码1——使用邻接表代码2——使用邻接矩阵 补充&#xff1a;稠密图和稀疏…...

【Matlab】基于卷积神经网络的数据分类预测(Excel可直接替换数据)

【Matlab】基于卷积神经网络的数据分类预测(Excel可直接替换数据) 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码6.完整代码7.运行结果1.模型原理 基于卷积神经网络(Convolutional Neural Network,CNN)的数据分类预测是一种常见的深度学习方法,广泛应用于图像识…...

【C++ 重要知识点总结】自定义类型-枚举和联合

复杂类型 除了类之外还有Union、Enum连个特殊的类型。 Union 概念 union即为联合&#xff0c;它是一种特殊的类。通过关键字union进行定义&#xff0c;一个union可以有多个数据成员。 union Token{char cval;int ival;double dval; };用法 互斥赋值。在任意时刻&#xff0c…...

Centos MySql安装,手动安装保姆级教程

1.删除原有的mariadb&#xff0c;不然mysql装不进去 查询MAriaDB命令 rpm -qa|grep mariadb 删除 rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64 &#xff08;yum -y remove mysql 如需要清除服务器上以前安装过的MySQL可执行此命令&#xff0c;执行前一…...

电脑C盘空间大小调整 --- 扩容(扩大/缩小)--磁盘分区大小调整/移动

概述&#xff1a; 此方法适合C盘右边没有可分配空间&#xff08;空闲空间&#xff09;的情况&#xff0c;D盘有数据不方便删除D盘分区的情况下&#xff0c;可以使用傲梅分区助手软件进行跨分区调整分区大小&#xff0c;不会损坏数据。反之可直接使用系统的磁盘管理工具进行调整…...

centos7设置网桥网卡

安装bridge-utils yum install bridge-utils修改ens33 网卡 TYPEEthernet BOOTPROTOnone DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITyes IPV6_AUTOCONFyes IPV6_DEFROUTEyes IPV6_FAILURE_FATALno NAMEens33 UUID04b97484-25c8-45c7-8c8c-e335e8080e10 DEVICEens33 ONBOOTye…...

TCP模型和工作沟通方式

我们如何与客户沟通&#xff1f;理科生和技术人员可能在沟通技巧方面有所欠缺。 那么我们如何理解和掌握沟通的原则和技巧呢&#xff1f;我发现TCP网络交互模型很好的描述了沟通的原则和要点。下面我们就从TCP来讲沟通的过程。 TCP的客户端就像客户&#xff08;甲方&#xff…...

Langchain 的 ConversationSummaryBufferMemory

Langchain 的 ConversationSummaryBufferMemory ConversationSummaryBufferMemory 在内存中保留最近交互的缓冲区&#xff0c;但不仅仅是完全刷新旧的交互&#xff0c;而是将它们编译成摘要并使用两者。但与之前的实现不同的是&#xff0c;它使用令牌长度而不是交互次数来确定何…...

【Rust 基础篇】Rust 通道实现单个消费者多个生产者模式

导言 在 Rust 中&#xff0c;我们可以使用通道&#xff08;Channel&#xff09;来实现单个消费者多个生产者模式&#xff0c;简称为 MPMC。MPMC 是一种常见的并发模式&#xff0c;适用于多个线程同时向一个通道发送数据&#xff0c;而另一个线程从通道中消费数据的场景。本篇博…...

HTTP协议各版本介绍

HTTP协议是一种用于传输Web页面和其他资源的协议。 下面详细介绍一下HTTP的各个版本&#xff1a; 1.HTTP/0.9 这是最早的HTTP版本&#xff0c;于1991年发布。它非常简单&#xff0c;只能传输HTML格式的文本&#xff0c;并且不支持其他类型的资源、请求头和状态码。 2.HTTP/1…...

玩转ChatGPT:Custom instructions (vol. 1)

一、写在前面 据说GPT-4又被削了&#xff0c;前几天让TA改代码&#xff0c;来来回回好几次才成功。 可以看到之前3小时25条的限制&#xff0c;现在改成了3小时50条&#xff0c;可不可以理解为&#xff1a;以前一个指令能完成的任务&#xff0c;现在得两条指令&#xff1f; 可…...

黄东旭:The Future of Database,掀开 TiDB Serverless 的引擎盖

在 PingCAP 用户峰会 2023 上&#xff0c; PingCAP 联合创始人兼 CTO 黄东旭 分享了“The Future of Database”为主题的演讲&#xff0c; 介绍了 TiDB Serverless 作为未来一代数据库的核心设计理念。黄东旭 通过分享个人经历和示例&#xff0c;强调了数据库的服务化而非服务化…...

Linux环境搭建(XShell+云服务器)

好久不见啊&#xff0c;放假也有一周左右了&#xff0c;简单休息了下&#xff08;就是玩了几天~~&#xff09;&#xff0c;最近也是在学习Linux&#xff0c;现在正在初步的学习阶段&#xff0c;本篇将会简单的介绍一下Linux操作系统和介绍Linux环境的安装与配置&#xff0c;来帮…...

-bash: /bin/rm: Argument list too long

有套数据库环境&#xff0c;.aud文件太多导致/u01分区使用率过高&#xff0c;rm清理时发现报错如下 [rootdb1 audit]# rm -rf ASM1_ora_*202*.aud -bash: /bin/rm: Argument list too long [rootdb1 audit]# rm -rf ASM1_ora_*20200*.aud -bash: /bin/rm: Argument list too…...

5个步骤完成Linux 搭建Jdk1.8环境

1&#xff1a;首先&#xff0c;在Linux系统中创建一个目录&#xff0c;用于存放JDK文件。可以选择在/opt目录下创建一个新的文件夹&#xff0c;例如/opt/jdk。 sudo mkdir /opt/jdk 2&#xff1a;将下载的jdk-8u381-linux-x64.tar.gz文件复制到新创建的目录中。 sudo cp jdk…...

【JAVASE】运算符

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 运算符 1. 什么是运算符2. 算术运算符3.…...

Emacs之改造搜索文件fd-dired(基于fd命令)(一百二十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...