【四. 掌握TypeScript高级特性:泛型与类型声明文件】

4. TypeScript 高级特性

4.1 泛型

定义:它能让我们在定义函数、类或者接口的时候,使用一个 “类型变量” 来代表还没确定的类型。这个 “类型变量” 就像是一个占位符,等我们实际使用的时候,再把具体的类型填进去

4.1.1 泛型函数

泛型函数:可以理解为是一种特殊的函数,它在定义的时候会带有一个或多个类型参数。这些类型参数用尖括号 <> 括起来,放在函数名的后面

function identity<T>(arg: T): T {
  return arg;
}

// 使用泛型函数
let output1 = identity<string>("myString"); // 指定T为string类型
let output2 = identity<number>(100); // 指定T为number类型

代码解释:identity 函数就是一个泛型函数,它有一个类型参数 T。这个 T 可以代表任意类型。当我们调用这个函数时,通过在尖括号中指定具体的类型,比如 string 或者 number,来告诉 TypeScript 这次调用中 T 具体代表什么类型

4.1.2 泛型接口

泛型接口:泛型接口就是在定义接口的时候,使用类型参数。通过这种方式,接口可以适应不同的类型,增强接口的通用性

interface b<T> {
  (arg: T): T;//接口定义一个函数接受一个参数 arg,其类型为 T,规定要函数返回类型也是 T 
}

function identity<T>(arg: T): T {
  return arg;b
}b

let a: b<number> = identity;//将 identity 函数赋值给 a
//此时a必须是一个接收一个 number 类型的参数,并返回一个 number 类型的值函数

代码解释:b 是一个泛型接口,它有一个类型参数 T。这个接口定义了一个函数类型,该函数接受一个类型为 T 的参数,并返回一个类型为 T 的值。然后,我们定义了一个普通的泛型函数 identity,它符合 b 接口的定义。最后,我们创建了一个 a 变量,它的类型是 b,这意味着 a 这个函数只能处理 number 类型的数据

4.1.3 泛型类

泛型类:泛型类的定义方式是在类名后面加上尖括号,并在尖括号中指定类型参数。类中的属性和方法可以使用这些类型参数来定义它们的类型

class b<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let a = new b<numbera>();
a.zeroValue = 0;
a.add = function(x, y) { return x + y; };

代码解释:b 是一个泛型类,它有一个类型参数b T。类中有两个属性:zeroValue 属性,其类型为 Tadd 方法,它接受两个类型为 T 的参数,并返回一个类型为 T 的值。当我们创建 a 实例时,指定 Tnumber 类型,这样 zeroValue 就被限制为 number 类型,add 方法a也只能处理 number 类型的加法运算

4.1.4 泛型约束

泛型约束:泛型约束用于限制类型参数的取值范围,确保类型参数满足某些条件。通过使用 extends 关键字,可以让类型参数继承某个接口或类,从而保证该类型参数必须具备某些属性或方法

interface b{
  length: number;
}
b
function a<T extends b>(arg: T): T {
  console.log(arg.length); // 因为T必须有length属性,所以可以安全使用
  return arg;
}

a("hello"); // 字b符串有length属性,正常
// a(3); // 报错,数字没有length属性

b代码解释:我们定义了一个接口 b,它b只有a一个 lengtbh 属性。然后,我们创建了一个泛型函数 a,并使用 Ta extends b 对类型参a数 T 进行约束,这意味着 T 必须是实现了 b 接口的类型。当我们调用 a 函数时,传入的参数必须具有 length 属性,否则就会报错,当然也可以有其他的属性

4.1.5 泛型参数的默认类型

泛型参数的默认类型:我们还可以为泛型参数指定默认类型。这样,在使用泛型时如果没有显式指定类型参数,就会使用这个默认类型

function a<T = string>(length: number, value: T): T[] {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = value;
  }
  return result;
}

let array1 = a(3, "x"); // T默认为string类型
let arraay2 = a<number>(3, 1); // 显式指定T为number类型

代码解释:a 函数的类型参数 T 默认是 string 类型。当我们调用这个函数时,如果没有明确指定 T 的类型,比如 a(3, "x"),那么 T 就会使用默认的 string 类型。如果我们显式地指定了类型,比如 a(3, 1),那么 T 就会是我们指定的 number 类型

4.1.6 泛型工具类型a

4.1.6.1 Partial

Partial:TypeScript 内置的工具类型。会创建一个新类型,这个新类型的所有属性都是 T 类型中对应属性的可选属性。也就是说,T 中的每个属性都会变成可选的(添加 ? 标记)

interface User {
  name: string;
  age: number;
}

let partialUser: Partial<User> = { name: "John" }; // 可以只提供部分属性
4.1.6.2 Readonly

Readonly:创建一个新类型,这个新类型的所有属性都是只读的,不能被修改

interface User {
  name: string;
  age: number;
}

let readonlyUser: Readonly<User> = { name: "John", age: 30 };
// readonlyUser.age = 31; // 报错,属性是只读的
4.1.6.3 Pick

Pick:从 T 类型中挑选出 K 中指定的属性,创建一个新的类型

interface User {
  name: string;
  age: number;
  email: string;
}

type a= Pick<User, "name" | "email">;
let user: a= { name: "John", email: "[email protected]" };
4.1.6.4 Record

Record:创建一个对象类型,它的属性名是 K 类型(通常是字符串字面量类型),属性值是 T 类型

type Weekday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri";
type DayTemp = Record<Weekday, number>;

let temperatures: DayTemp = {
  Mon: 20,
  Tue: 22,
  Wed: 25,
  Thu: 23,
  Fri: 21
};

4.2 类型声明文件

  • 什么是类型声明文件?

    类型声明文件就像是 TypeScript 的 “翻译器”。你有一些纯 JavaScript 代码(比如某个第三方库),TypeScript 本身不认识这些代码的类型,这时候就需要类型声明文件来告诉 TypeScript:“这个函数接收两个数字,返回一个数字”

  • 为什么需要类型声明文件?

    • 让 TypeScript 能够对 JavaScript 代码进行类型检查

    • 提供自动补全和代码提示

    • 提高代码的可维护性

  • 类型声明文件的语法

    类型声明文件通常以.d.ts结尾,里面主要使用declare关键字

  // 声明一个函数
  declare function functionName(参数: 类型): 返回值类型;
  
  // 声明一个变量
  declare const variableName: 类型;
  
  // 声明一个类
  declare class ClassName {
    属性: 类型;
    方法(参数: 类型): 返回值类型;
  }
  
  // 声明一个模块
  declare module '模块名' {
    export const 导出变量: 类型;
    export function 导出函数(参数: 类型): 返回值类型;
  }
  • 例子:一个简单的 JavaScript 模块创建类型声明文件,然后用 TypeScript 使用这个模块
  // math.js
  export function add(a, b) {
    return a + b;
  }
  
  export function multiply(a, b) {
    return a * b;
  }
  
  // math.d.ts
  declare function add(a: number, b: number): number;
  declare function multiply(a: number, b: number): number;
  
  export { add, multiply };
  
  // app.ts
  import { add, multiply } from './math.js';
  
  // 正确使用
  const sum = add(5, 3); // sum类型自动推断为number
  console.log(sum); // 输出: 8
  
  const product = multiply(4, 2); // product类型自动推断为number
  console.log(product); // 输出: 8
  
  // 错误使用(TypeScript会报错)
  // const errorSum = add("5", 3); // 类型错误:参数类型不匹配

注意:

  1. JavaScript 文件是 math.js,则类型声明文件必须是 math.d.ts。原因:TypeScript 会自动关联同名的 .js.d.ts 文件

  2. 如果在 TypeScript 文件中导入 import { add } from './utils/math.js',则声明文件必须位于 ./utils/math.d.ts

你可能感兴趣的:(#,typescript,前端,前端框架,javascript)