TS进阶高级用法以及高级概念(看一篇即可)

245 阅读6分钟

干货无需过多废话,直接上内核

  1. 泛型(Generics) : 泛型允许你为类、接口和方法定义可重用的类型参数化组件。
function identity<T>(arg: T): T {
  return arg;
}
  1. 装饰器(Decorators) : 装饰器是一种特殊类型的声明,它可以被附加到类声明、方法、访问器或属性上,用于修改它们的行为。
function logClass(target: Function) {
  console.log('Class decorated:', target);
}

@logClass
class MyClass {}
  1. 混入(Mixins) : 混入是一种重用类功能的方式,可以将多个类的功能组合到一个类中。
class Mixin {
  mixinMethod() {
    console.log('Mixin method');
  }
}

class MyClass extends Mixin {
  myClassMethod() {
    console.log('My class method');
  }
}

const myObj = new MyClass();
myObj.mixinMethod(); 
  1. 类型守卫(Type Guards) : 类型守卫用于在运行时检查类型,以便在TypeScript中执行类型缩小操作。
function isString(value: any): value is string {
  return typeof value === 'string';
}

function processValue(value: string | number) {
  if (isString(value)) {
    console.log(value.toUpperCase()); 
  }
}
  1. 映射类型(Mapped Types) : 映射类型允许你从现有的类型创建新的类型,通过对每个属性进行转换。
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

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

type ReadonlyPerson = Readonly<Person>;
type PartialPerson = Partial<Person>;
  1. 条件类型(Conditional Types) : 条件类型允许你根据条件表达式从两种或多种类型中选择一种类型。
type IsString<T> = T extends string? true : false;

type Result = IsString<string>; 
  1. 联合类型(Union Types) : 联合类型允许一个变量是多种类型之一。
function logId(id: number | string) {
    console.log(`ID: ${id}`);
}
  1. 交叉类型(Intersection Types) : 交叉类型将多个类型组合成一个类型,适用于将多个对象的特性合并。
interface Person {
    name: string;
    age: number;
}

interface Worker {
    jobTitle: string;
}

type Employee = Person & Worker;

const employee: Employee = {
    name: "张三",
    age: 30,
    jobTitle: "工程师",
};
  1. 索引类型(Index Types) :索引类型用于获取对象类型的特定属性类型。
type Person = {
    name: string;
    age: number;
};

type PersonName = Person['name'];  // string
  1. 类型推断(Type Inference) : TypeScript可以根据上下文推断出类型。
const num = 10;  // TypeScript 推断 num 为 number
  1. 模板字面量类型(Template Literal Types) : 使用模板字面量类型可以创建新的字符串类型。
type EventName = "click" | "mouseover";
type EventHandler = `on${Capitalize<EventName>}`;

const handler: EventHandler = "onClick";  // 正确
// const invalidHandler: EventHandler = "onHover";  // 错误
  1. 接口(Interfaces) :接口用于定义对象的结构,可以被类实现,也可以用于类型检查。
interface Animal {
    name: string;
    makeSound(): void;
}

class Dog implements Animal {
    name: string;
    
    constructor(name: string) {
        this.name = name;
    }

    makeSound() {
        console.log("汪汪!");
    }
}
  1. 类(Classes) :TypeScript 支持面向对象编程,可以使用类来创建对象。
class Person {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`你好,我是 ${this.name},今年 ${this.age} 岁。`);
    }
}
  1. 命名空间(Namespaces) :命名空间用于将相关的代码组织在一起,避免污染全局范围。
namespace Geometry {
    export class Circle {
        constructor(public radius: number) {}

        area() {
            return Math.PI * this.radius ** 2;
        }
    }
}

const circle = new Geometry.Circle(5);
console.log(circle.area());
  1. 模块(Modules) :模块是将代码组织成独立文件的方式,可以使用 export 和 import 关键字进行模块间的导入导出。
// shapes.ts
export class Rectangle {
    constructor(public width: number, public height: number) {}
}

// app.ts
import { Rectangle } from './shapes';

const rect = new Rectangle(10, 20);
  1. 类型声明文件(.d.ts) :类型声明文件用于为 JavaScript 库提供类型信息,以便 TypeScript 用户能够获得类型检查和自动补全的支持。
// jquery.d.ts
declare module "jquery" {
    function $(selector: string): any;
    export = $;
}
  1. 类型泛化(Type Assertion) :类型断言允许开发者手动指定某个值的类型,通常用于从一个类型转变成另一个类型。
const someValue: unknown = "这是一个字符串";

// 使用类型断言
const strLength: number = (someValue as string).length;
  1. 三斜线指令(Triple-Slash) 指令 :TypeScript 中的一种特殊注释格式,通常用于在代码中引入类型声明或模块。它们以三个斜杠开始,后面跟着特定的指令。
  • 1. /// <reference path="..." />

这个指令用于引入其他 TypeScript 文件的类型定义。在一个文件中,你可以使用这个指令来引用其他文件,使得当前文件能够使用那些文件中定义的类型。

/// <reference path="moduleA.ts" />
/// <reference path="moduleB.ts" />
  • 2. /// <reference lib="..." />

这个指令用于引入 TypeScript 的内置库,比如 DOM、ES5、ES6 等。这在需要使用特定库的类型时非常有用。

/// <reference lib="es2015" />
  • 3. /// <reference types="..." />

这个指令用于引入类型定义文件。通常在使用第三方库时,类型定义文件(例如从 DefinitelyTyped 提供的)可以通过这个指令来使用。

/// <reference types="node" />
  1. 函数重载(Function Overloading) :函数重载(Function Overloading)是指可以使用相同的函数名来定义多个不同的函数签名。这使得一个函数可以根据不同的参数类型或数量来执行不同的操作。函数重载在处理多种类型的输入时非常有用。 函数重载的要点(相同函数名(重载的函数具有相同的名称,但具有不同的参数类型或参数数量)不同的签名(每个重载定义都称为“签名”,它描述了函数接受的参数类型和数量)具体的实现(重载函数的实际实现(即函数体)通常使用联合类型来处理所有可能的输入类型,在实现逻辑中需要根据参数类型进行相应的处理)
// 函数重载签名
function handleInput(input: string): string; // 重载签名 1
function handleInput(input: number): number; // 重载签名 2

// 函数实现
function handleInput(input: any): any {
    if (typeof input === "string") {
        return `输入的是字符串: ${input}`;
    } else if (typeof input === "number") {
        return input * 2;
    }
}

// 调用重载函数
const result1 = handleInput("Hello"); // 结果: 输入的是字符串: Hello
const result2 = handleInput(10);       // 结果: 20
// **重载签名**:前两行定义了重载签名。每个签名都指定了不同的参数类型(`string` 和 `number`)
// 实际的函数实现必须涵盖所有可能的输入类型。在此示例中,使用 `typeof` 操作符来判定输入的类型,并根据类型执行不同的操作
  1. 协变(Covariance)和逆变(Contravariance) :协变(Covariance)和逆变(Contravariance)是指类型之间的关系,主要用于描述函数参数和返回值在类型系统中的兼容性。这些概念通常涉及到函数签名和泛型。
    1. 协变(Covariance)

协变是指如果类型 A 是类型 B 的子类型,则 A 可以替代 B。在实际应用中,协变通常体现在函数的返回值类型上。例如,如果一个函数返回类型是 B,而我们有一个返回类型为 A 的子类函数,那么这个子类函数可以用作父类函数的返回类型。

class Animal {
    name: string;
}

class Dog extends Animal {
    bark() {
        console.log("汪汪!");
    }
}

function getAnimal(): Animal {
    return new Dog(); // 协变,返回类型 Dog 是 Animal 的子类型
}

const dog: Animal = getAnimal(); // 合法
    1. 逆变(Contravariance)

逆变是指如果类型 A 是类型 B 的子类型,则 B 可以替代 A。逆变通常与函数参数类型有关。如果一个函数接受参数类型 A,那么一个接受参数类型 B(其中 B 是 A 的父类)的函数可以用作 A 类型的参数。

class Animal {
    name: string;
}

class Dog extends Animal {
    bark() {
        console.log("汪汪!");
    }
}

// 这里的参数是 Animal,意味着可以接受任何 Animal 类型的子类
function handleAnimal(animal: Animal) {
    console.log(animal.name);
}

// 参数是 Dog,Dog 是 Animal 的子类
function handleDog(dog: Dog) {
    console.log(dog.name);
}

// 逆变,接受 Animal 类型的函数可以接受 Dog 类型的函数
let handler: (animal: Animal) => void = handleDog; // 合法
handler(new Dog()); // 合法

// 但反过来就不行,因为 handleAnimal 期望 Animal,这里类型不匹配
// handleDog = handleAnimal; // 错误

小结

  • 协变指的是返回值类型的兼容性,允许使用子类返回父类期望的类型。
  • 逆变指的是参数类型的兼容性,允许使用父类作为子类期望的参数类型。
  1. 高级类型工具(Advanced Type Tools) :TypeScript 的高级类型工具为开发者提供了强大的类型系统,能够更加灵活地处理复杂的数据结构和类型。
  • 1. Partial

Partial<T> 将类型 T 的所有属性变为可选属性。这在需要部分更新对象时非常有用。

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

type PartialUser = Partial<User>;

const userUpdate: PartialUser = {
    name: "张三"
}; // 合法
  • 2. Required

Required<T> 将类型 T 的所有属性变为必需属性。用来确保对象中不能有可选属性。

interface User {
    id: number;
    name?: string;
}

type RequiredUser = Required<User>;

const user: RequiredUser = {
    id: 1,
    name: "李四" // name 现在是必需的
}; // 合法
  • 3. Readonly

Readonly<T> 将类型 T 的所有属性变为只读属性。对象的属性不能被修改。

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

type ReadonlyUser = Readonly<User>;

const user: ReadonlyUser = {
    id: 1,
    name: "王五"
};

// user.id = 2; // 错误,不能修改只读属性
  • 4. Record<K, T>

Record<K, T> 用于构造一个对象类型,其属性名是类型 K,属性值是类型 T。常用于映射。

type UserRoles = 'admin' | 'user' | 'guest';

type RolePermissions = Record<UserRoles, string[]>;

const permissions: RolePermissions = {
    admin: ['create', 'read', 'update', 'delete'],
    user: ['read'],
    guest: ['read']
};
  • 5. Omit<T, K>

Omit<T, K> 用于从类型 T 中剔除属性 K

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

type UserWithoutEmail = Omit<User, 'email'>;

const user: UserWithoutEmail = {
    id: 1,
    name: "李四"
}; // 合法
  • 6. Pick<T, K>

Pick<T, K> 用于从类型 T 中选择一些属性 K,并构造一个新的类型。

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

type PickedUser = Pick<User, 'name' | 'email'>;

const user: PickedUser = {
    name: "赵六",
    email: "zhaoliu@example.com"
}; // 合法
  • 7. Exclude<T, U>

Exclude<T, U> 用于从类型 T 中排除类型 U

type Original = 'a' | 'b' | 'c' | 'd';
type Excluded = Exclude<Original, 'a' | 'b'>; // 'c' | 'd'
  • 8. Extract<T, U>

Extract<T, U> 用于提取类型 T 中可以赋值给类型 U 的部分。

type Original = 'a' | 'b' | 'c' | 'd';
type Extracted = Extract<Original, 'a' | 'b'>; // 'a' | 'b'
  • 9. NonNullable

NonNullable<T> 用于从类型 T 中排除 null 和 undefined

type MaybeString = string | null | undefined;
type NonNullableString = NonNullable<MaybeString>; // string
  • 10. ReturnType

ReturnType<T> 用于获取函数 T 的返回类型。

function getAge(): number {
    return 30;
}

type Age = ReturnType<typeof getAge>; // Age 是 number
  • 11. InstanceType

InstanceType<T> 用于获取构造函数类型 T 的实例类型。

class Person {
    constructor(public name: string) {}
}

type PersonInstance = InstanceType<typeof Person>;

const person: PersonInstance = new Person("李四"); // 合法
  • 12. ThisType

ThisType<T> 是一个特殊的工具类型,用于在函数指针类型中指定 this 的类型。

interface User {
    name: string;
    greet(this: User): string;
}

const user: User = {
    name: "王五",
    greet() {
        return `你好,我是 ${this.name}`;
    }
};

console.log(user.greet()); // 输出: 你好,我是 王五
  • 13.  keyof

keyof 是一个索引类型查询操作符,它返回一个类型的所有键组成的联合类型。这在需要灵活访问对象属性时非常有用。

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

// 使用 keyof 获取 User 的键
type UserKeys = keyof User; // "id" | "name" | "email"

// 示例函数,接受 UserKeys 类型的参数
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user: User = { id: 1, name: "王五", email: "wangwu@example.com" };
const userName = getValue(user, "name"); // "王五"
  • 14. infer

infer 是用于条件类型中的一种特性,可以在条件表达式中推断类型。当你想要从一个类型中提取出某一部分的类型时,使用 infer 可以非常方便。

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function exampleFunction() {
    return 42;
}

type ResultType = ReturnType<typeof exampleFunction>; // ResultType 是 number
  • 15. Promise<T>

Promise 的泛型使得你可以明确指定 Promise 返回值的类型。这在处理不同类型的异步操作时非常有用。

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

function fetchUser(id: number): Promise<User> {
    return new Promise<User>((resolve, reject) => {
        // 模拟异步操作
        setTimeout(() => {
            const user: User = { id, name: "李四" };
            resolve(user); // 成功解析 User 类型
        }, 1000);
    });
}

// 使用 fetchUser 函数
fetchUser(1)
    .then((user) => {
        console.log(`用户 ID: ${user.id}, 名称: ${user.name}`);
    })
    .catch((error) => {
        console.error(`获取用户时出错: ${error}`);
    });

联合类型(Union Type)和交叉类型(Intersection Type)的区别

联合类型(Union Type)

  • 联合类型允许一个变量可以是多种类型中的一种。例如,可以将一个变量定义为 string | number,这意味着该变量可以是字符串类型或数字类型。
function log(value: string | number) {
  console.log(value);
}

log("Hello"); // 有效
log(100);  // 也有效

交叉类型(Intersection Type)

  • 交叉类型用于将多个类型合并为一个类型。它代表一个既符合所有类型的实例。例如,可以将一个变量定义为 A & B,这意味着该变量既是类型 A 的实例,也是类型 B 的实例。
interface A {
  name: string;
}

interface B {
  age: number;
}

const person: A & B = {
  name: "Alice",
  age: 30,
};

总结:

  • 联合类型用于表示一个值可以是几种类型之一。
  • 交叉类型用于表示一个值必须同时符合多个类型的要求。

以上便是ts好用并且可能不常见的用法了。。。