简介

在这篇文章中,我们会讲解一些 Typescript 的高级用法以及一些常见的应用场景,包括:

  • 泛型
  • 类型别名与接口
  • 类型守卫
  • 高级类型
  • 装饰器

泛型

泛型是指在定义函数、接口或者类时,使用一个占位符表示某个类型,从而实现代码的重用性和类型的灵活性。

泛型函数

泛型函数可以接受任意类型的参数,并且可以在函数内部使用泛型来处理参数,例如:

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

// 使用泛型函数
console.log(identity<string>('hello')); // 输出 hello
console.log(identity<number>(123)); // 输出 123

在上面的示例中,identity 函数使用了一个类型参数 T,表示传入的参数类型和函数返回值类型相同。实际使用时,可以通过指定类型参数的方式来调用这个函数。

泛型接口

泛型接口可以更加灵活地定义对象的类型,例如:

interface Pair<T, U> {
  first: T;
  second: U;
}

// 使用泛型接口
let pair: Pair<string, number> = { first: 'one', second: 1 };
console.log(pair.first); // 输出 one
console.log(pair.second); // 输出 1

在上面的示例中,Pair 接口使用了两个类型参数 TU 来表示对象中属性的类型。实际使用时,可以通过指定类型参数的方式来定义对象的类型。

类型别名与接口

类型别名和接口可以用来定义复杂的类型,例如:

type Person = {
  name: string;
  age: number;
  getAddress(): string;
}

interface Animal {
  name: string;
  age: number;
  readonly gender: string;
  getAge(): number;
}

// 使用类型别名和接口定义一个函数
function printInfo(obj: Person | Animal) {
  // ...
}

在上面的示例中,我们使用了类型别名 Person 和接口 Animal 来定义对象的类型。这些类型可以用来定义函数参数或者返回值的类型,并且可以通过类型交叉和联合的方式进行组合。

类型守卫

类型守卫可以用来在运行时判断一个变量的类型,并且进行相应的处理。例如:

class Cat {
  meow() {
    console.log('meow');
  }
}

class Dog {
  bark() {
    console.log('bark');
  }
}

function makeSound(animal: Cat | Dog) {
  if ('meow' in animal) {
    // animal 是 Cat 类型
    animal.meow();
  } else {
    // animal 是 Dog 类型
    animal.bark();
  }
}

在上面的示例中,我们使用了 in 操作符来判断变量的类型,并且执行相应的操作。类型守卫还可以使用 typeof、instanceof、null、undefined、boolean、number、string 等操作符进行判断。

高级类型

Typescript 提供了一些高级类型,可以用来描述函数的参数和返回值类型、表达式的属性类型、数组和元祖的扩展操作等。

函数类型

函数类型可以用来描述函数的参数和返回值类型,例如:

type Func = (a: number, b: string) => boolean;

const add: Func = (a, b) => {
  return true;
}

在上面的示例中,我们使用了函数类型 Func 来描述函数参数和返回值的类型,在实际使用时可以通过 Func 来声明一个函数。

属性访问类型

属性访问类型可以用来表示对象中某个属性的类型,例如:

type PersonProps = {
  name: string;
  age: number;
}

type NameType = PersonProps['name']; // string

在上面的示例中,我们使用了属性访问类型 PersonProps['name'] 来表示 PersonProps 对象中 name 属性的类型。

数组操作类型

数组操作类型可以用来实现数组的扩展和操作,例如:

type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;

type Arr = ["foo", 42, true];
type Head = First<Arr>; // "foo"

在上面的示例中,我们使用了数组操作类型 First 来获取一个数组的第一个元素。这里使用了 Type Script 中的特殊语法,类似于结构体的形式。

装饰器

装饰器可以用来给类和类成员添加元数据或者修改行为。装饰器可以用来实现日志记录、权限控制、性能分析等功能。

类装饰器

类装饰器可以用来添加类的元数据或者修改类的行为,例如:

function addAge<T extends { new(...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    age: number = 18;
  }
}

@addAge
class Person {
  name: string = 'Tom';
}

console.log(new Person()); // 返回 { name: 'Tom', age: 18 }

在上面的示例中,我们使用了类装饰器 addAge 来给 Person 类添加了一个 age 属性,并且修改了 Person 类的行为。

方法装饰器

方法装饰器可以用来修改类中的方法行为,例如:

function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
  let originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(`Calling ${ propertyKey } with arguments: ${ JSON.stringify(args) }`);
    return originalMethod.apply(this, args);
  };
  return descriptor;
}

class MyClass {
  @log
  method(a: number, b: string) {
    console.log(`Hello ${ b }`);
  }
}

let myClass = new MyClass();
myClass.method(123, 'world');

在上面的示例中,我们使用了方法装饰器 log 来添加日志记录功能,在调用 MyClass.method 方法时,会打印函数名和参数列表,并且返回原始方法的调用结果。

总结

到这里,我们已经讲解了 Typescript 的一些高级用法和常见应用场景,涵盖了泛型、类型别名与接口、类型守卫、高级类型和装饰器等。希望这篇文章对各位 Typescript 开发者有帮助,也欢迎大家提出宝贵意见。

文章目录