TypeScript 类型系统详解(二)

177 阅读4分钟

在 TypeScript 中,类型系统是确保代码质量的关键特性之一。通过类型注解和类型推断,开发者可以构建出既灵活又安全的应用。本文对 TypeScript 类型系统的深入探讨,是系列文章的第二篇。

1. 其它需要手动显式指定类型的情况

  1. 变量声明时初始化延后:当变量在声明时不立即初始化时,TypeScript可能无法推断其类型。例如:

    let foundWord: boolean;
    for (let i = 0; i < words.length; i++) {
      if (words[i] === 'green') {
        foundWord = true; // 显式指定类型为boolean
      }
    }
    
  2. 变量类型无法正确推断:当变量的类型不能被TypeScript正确推断时,需要显式指定类型。例如:

    let numbers = [-10, -1, 12];
    let numberAboveZero: boolean; // 显式指定类型为boolean
    for (let i = 0; i < numbers.length; i++) {
      if (numbers[i] > 0) {
        numberAboveZero = true;
      }
    }
    

2. 函数返回值类型约束

为函数的返回值指定类型可以防止错误,提高代码的健壮性。例如:

const add = (a: number, b: number): number => {
 return a + b;
};

3. 函数返回值为nevervoid

never用于表示函数不会返回任何值,通常用于错误处理;void表示函数没有返回值。例如:

const logger = (message: string): void => {
 console.log(message);
};
const throwError = (message: string): never => {
 throw new Error(message);
};

4. 带类型约束的形参解构

在函数参数中使用解构赋值时,可以显式指定解构后的变量类型。例如:

const logWeather = ({ date, weather }: { date: Date, weather: string }): void => {
 console.log(date);
 console.log(weather);
};

5. 数组类型约束

当需要确保数组中元素的类型一致性时,可以显式指定数组的类型。例如:

let numbers: number[] = [-10, -1, 12]; // 显式指定数组元素类型为number

数组类型约束的好处

  • 防止向数组中添加不兼容的值。
  • 在使用mapforEachreduce等数组函数时获得类型帮助。
  • 保持数组的灵活性,允许数组包含不同类型的元素。

6. 元组(Tuple)

元组用于将不同类型的数据成组,并且考虑其顺序。例如:

const pepsi: [string, boolean, number] = ['brown', true, 40];

但通常不推荐使用元组,因为接口(interface)可以实现相同的功能,并且具有更强的语义。

7. 使用type来简化代码

通过使用type别名,我们可以简化代码并提高可读性(因为有的时候有些类型约束真的很长,所以重复写的话可读性很差)。例如:

type Drink = [string, boolean, number];
const pepsi: Drink = ['brown', true, 40];
const sprite: Drink = ['clear', true, 40];
const tea: Drink = ['brown', false, 0];

8. 接口(Interfaces)

接口在TypeScript中用于定义对象的结构,是实现代码复用的强大工具。接口可以定义属性和方法,例如:

interface Vehicle {
  name: string;
  year: Date;
  broken: boolean;
  summary(): string;
}

const oldCivic = {
  name: 'civic',
  year: new Date(),
  broken: true,
  summary: function(): string {
    return `Name: ${this.name}`;
  }
};

function printVehicle(vehicle: Vehicle): void {
  console.log(vehicle.summary());
}

// 调用函数以打印oldCivic的摘要信息
printVehicle(oldCivic);

在上述代码中,printVehicle函数接受一个实现了Vehicle接口的对象作为参数。传入的对象只需要具有接口要求的所有属性即可通过类型检测。如果对象中的属性多于接口定义的属性,则不会报错,这提供了一定的灵活性

第 2 篇内容提纲

1. 手动显式指定类型的情况

  • 变量声明后初始化:变量声明时不立即赋值,需显式指定类型。
  • 类型推断失败:变量类型无法被TypeScript正确推断,需明确类型。

2. 函数返回值类型约束

  • 防止错误:通过指定返回值类型,提高代码健壮性。

3. 特殊返回值类型nevervoid

  • never:用于错误处理,表示函数不返回任何值。
  • void:表示函数没有返回值。

4. 带类型约束的形参解构

  • 函数参数解构:在函数参数中使用解构赋值时,显式指定类型。

5. 数组类型约束

  • 元素类型一致性:确保数组中元素类型统一。
  • 好处
    • 防止添加不兼容值。
    • 使用数组函数时获得类型帮助。
    • 保持数组灵活性。

6. 元组(Tuple)

  • 数据类型和顺序:用于组合不同类型的数据并考虑顺序。
  • 不推荐使用原因:接口可实现相同功能,语义更强。

7. 使用type简化代码

  • 类型别名:简化复杂类型约束,提高代码可读性。

8. 接口(Interfaces)

  • 定义对象结构:用于实现代码复用。
  • 属性和方法定义:接口定义对象应具备的属性和方法。
  • 灵活性:传入对象只需满足接口要求的属性,多于定义的属性不会报错。