TypeScript基础知识(十)高级类型和工具类型

TypeScript基础知识(十)高级类型和工具类型_第1张图片

文章目录

    • 交叉类型和联合类型的区别
    • 条件类型(Conditional Types)
    • 映射类型(Mapped Types)
    • 可辨识联合(Discriminated Unions)
    • keyof 和 typeof 操作符
    • Partial、Required 和 Readonly 工具类型
    • 索引类型(Index Types)

交叉类型和联合类型的区别

在 TypeScript 中,交叉类型和联合类型是两种不同的类型。

交叉类型使用 & 符号将多个类型合并成一个新的类型。它表示一个对象必须同时具备所合并的多个类型的特性。换句话说,交叉类型可以被看作是多个类型的并集,要同时满足多个类型的要求。例如:

type A = { name: string };
type B = { age: number };

type C = A & B;
let person: C = { name: "Alice", age: 20 };

上面的代码中,类型 C 是类型 A 和类型 B 的交叉类型,它要求一个对象必须既有 name 属性又有 age 属性。

联合类型使用 | 符号将多个类型定义在一起。它表示一个对象可以是多个类型中的任意一种类型。换句话说,联合类型可以被看作是多个类型的交集,满足其中一个类型即可。例如:

type D = A | B;
let person: D = { name: "Bob" };
person = { age: 30 };

上面的代码中,类型 D 是类型 A 和类型 B 的联合类型,它允许一个对象只有 name 属性或者只有 age 属性。

总结来说,交叉类型要求对象具备多个类型的特性,联合类型允许对象具备多个类型中的任意一种特性。你可以根据具体的需求选择使用交叉类型还是联合类型。

条件类型(Conditional Types)

条件类型是 TypeScript 中的一种高级类型,它允许根据条件选择不同的类型。

条件类型的语法为 T extends U ? X : Y,其中 T 是待检查的类型,U 是要判断的类型,XY 是根据条件选择的类型。

条件类型可以用于很多场景,下面是几个常见的应用举例:

  1. 类型过滤:可以根据某个条件过滤出类型中的符合条件的部分类型。比如,我们可以使用 Exclude 来从类型 T 中排除掉可以分配给类型 U 的部分。这对于实现高级的类型过滤很有用。

  2. 类型映射:可以根据某个条件映射类型中的部分成员类型。比如,我们可以使用 ReturnType 来获取函数类型的返回值类型。这对于实现高级的类型映射很有用。

  3. 类型推导:可以根据某个条件推断出类型中的其他部分类型。比如,在某些场景下,可以根据函数参数的类型来推断出函数返回值的类型。

条件类型是 TypeScript 中强大的工具,可以用于提供更精确和灵活的类型声明,同时也可以实现一些高级的类型操作和模式。这些特性使得 TypeScript 在静态类型检查领域有着很大的优势。

条件类型(Conditional Types)是 TypeScript 中的一个高级类型特性,它允许我们根据条件选择不同的类型。

条件类型的语法形式为 T extends U ? X : Y,其中 T 是待检查的类型,U 是要进行检查的条件,X 是满足条件时的返回类型,Y 是不满足条件时的返回类型。

下面是一个例子,演示了如何使用条件类型:

type IsString<T> = T extends string ? true : false;

type A = IsString<"hello">;  // true
type B = IsString<42>;  // false

上面的例子中,我们定义了一个条件类型 IsString,它判断传入的类型是否为 string 类型,如果是则返回 true,否则返回 false。通过 IsString<"hello">IsString<42> 的调用,我们可以得到相应的结果。

条件类型的应用非常广泛,可以用于类型的过滤、映射和推导等场景。它可以根据不同的条件在类型层面进行判断和操作,使我们能够编写更灵活和健壮的类型逻辑。例如,在函数类型中根据参数类型的不同来进行不同的处理。

不过要注意,条件类型的嵌套和复杂使用可能会导致类型系统的复杂性增加,如果使用不当可能会造成编译时的性能问题。因此,在使用条件类型时需要谨慎考虑其复杂性和性能影响。

映射类型(Mapped Types)

映射类型是 TypeScript 中的一种高级类型,它可以根据一个已知的类型,创建一个新的类型。

映射类型的语法为 { [K in Keys]: NewType },其中 K 是一个类型变量,Keys 是一个已知类型的属性名集合,NewType 是根据每个属性名生成的新类型。

映射类型可以用于很多场景,下面是几个常见的应用举例:

  1. 可选属性:可以通过映射类型将一个类型中的所有属性变为可选属性。比如,可以使用 { [K in keyof T]?: T[K] } 来将类型 T 中的所有属性变为可选属性。

  2. 只读属性:可以通过映射类型将一个类型中的所有属性变为只读属性。比如,可以使用 { readonly [K in keyof T]: T[K] } 来将类型 T 中的所有属性变为只读属性。

  3. 属性过滤:可以通过映射类型将一个类型中的部分属性排除掉。比如,可以使用 { [K in keyof T & U]: T[K] } 来从类型 T 中排除掉在类型 U 中定义的属性。

映射类型的灵活性使得它可以用于很多场景,可以根据实际需求来创建新的类型。它是 TypeScript 中非常有用的工具之一,可以提高代码的可读性和灵活性。

下面是几个使用映射类型的 TypeScript 代码案例:

  1. 可选属性:
interface Person {
  name: string;
  age: number;
  email: string;
}

type OptionalPerson = { [K in keyof Person]?: Person[K] };

// 使用可选属性类型
const optionalPerson: OptionalPerson = {
  name: "John",
  age: 25
};
  1. 只读属性:
interface Person {
  name: string;
  age: number;
  email: string;
}

type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };

// 使用只读属性类型
const readonlyPerson: ReadonlyPerson = {
  name: "John",
  age: 25,
  email: "[email protected]"
};

// 下面的代码会报错,因为属性是只读的
readonlyPerson.name = "Jane";
  1. 属性过滤:
interface Person {
  name: string;
  age: number;
  email: string;
}

type FilteredPerson<U> = { [K in keyof Person & U]: Person[K] };

// 使用属性过滤类型
type AgeEmailPerson = FilteredPerson<"age" | "email">;

// 只包含 age 和 email 属性
const filteredPerson: AgeEmailPerson = {
  age: 25,
  email: "[email protected]"
};

通过这些例子,您可以更好地理解映射类型的用法和灵活性。您可以根据实际需求使用映射类型来创建新的类型,并提高代码的灵活性和可读性。

可辨识联合(Discriminated Unions)

可辨识联合(Discriminated Unions)是 TypeScript 中一种用于建模具有共同属性的一组相关类型的技术。它是一种结合联合类型和字面量类型的方法,通过一个共同的属性来区分不同的类型。

这个共同的属性一般被称为“标签”或“标识符”,它的值必须是字面量类型。通过使用可辨识联合,我们可以在类型系统中轻松识别出某个实例属于哪个具体类型,并进行相应的处理。

下面是一个简单的代码案例,使用可辨识联合来表示不同的形状:

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

interface Triangle {
  kind: "triangle";
  base: number;
  height: number;
}

type Shape = Circle | Square | Triangle;

function calculateArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    case "triangle":
      return (shape.base * shape.height) / 2;
  }
}

const circle: Circle = {
  kind: "circle",
  radius: 5
};

console.log(calculateArea(circle)); // 输出: 78.53981633974483

在上面的代码中,我们定义了 CircleSquareTriangle 这三个接口,它们都有一个 kind 属性作为标签来区分不同的形状。然后使用 Shape 类型作为可辨识联合类型来表示这些形状。

calculateArea 函数中,我们使用了 switch 语句来根据 shape.kind 的值来执行不同的计算逻辑。通过可辨识联合,我们可以在编译时就确定 shape 的具体类型,并在相应的分支中进行类型安全的操作。

最后,我们创建了一个 circle 实例,并将其传递给 calculateArea 函数来计算圆的面积。根据可辨识联合的特性,编译器能够推断出 circle 的类型为 Circle,从而执行正确的计算逻辑。

可辨识联合可以在很多场景中发挥作用,如处理不同的消息类型、实现状态机等。它是 TypeScript 中一项非常有用的高级类型技术。

keyof 和 typeof 操作符

keyof操作符用于获取类型的所有可索引属性的键的联合类型。它可以与泛型结合使用,以在编译时动态获取类型的键。

例如,考虑以下示例代码:

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

type PersonKeys = keyof Person; // "name" | "age" | "address"

function getProperty<T, K extends keyof T>(obj: T, key: K) {
    return obj[key];
}

const person: Person = {
    name: "John",
    age: 30,
    address: "123 Main St"
};

const name = getProperty(person, "name"); // 类型为 string
const age = getProperty(person, "age"); // 类型为 number
const address = getProperty(person, "address"); // 类型为 string
const invalidKey = getProperty(person, "invalid"); // 编译时错误:无效的属性键

console.log(name, age, address);

typeof操作符用于获取表达式的类型。它在以下两种情况下特别有用:

  1. 获取变量、函数或类的类型。
  2. 获取字面量、表达式或函数参数的类型。
const person = {
    name: "John",
    age: 30,
    address: "123 Main St"
};

type PersonType = typeof person; // { name: string; age: number; address: string; }

function getPropertyValue(obj: typeof person, key: keyof typeof person) {
    return obj[key];
}

const name = getPropertyValue(person, "name"); // 类型为 string
const age = getPropertyValue(person, "age"); // 类型为 number
const address = getPropertyValue(person, "address"); // 类型为 string
const invalidKey = getPropertyValue(person, "invalid"); // 编译时错误:无效的属性键

console.log(name, age, address);

Partial、Required 和 Readonly 工具类型

TypeScript中的Partial、Required和Readonly是几个非常有用的工具类型,它们允许您在编写类型时进行更灵活的操作。

  1. Partial工具类型:
    Partial将类型T的所有属性设置为可选。它创建了一个新的类型,该类型拥有与T相同的属性,但所有属性都是可选的。这对于需要创建只包含部分属性的类型非常有用。

示例代码:

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

const partialUser: Partial<User> = {
  name: "John",
  age: 25
};

在上面的示例中,Partial将User类型的所有属性设置为可选。所以,partialUser包含了一个部分属性的User对象。

  1. Required工具类型:
    Required将类型T的所有属性设置为必填项。它创建了一个新的类型,该类型拥有与T相同的属性,但所有属性都是必填的。这对于需要创建只包含必填属性的类型非常有用。

示例代码:

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

const requiredUser: Required<User> = {
  name: "John",
  age: 25,
  address: "123 Street"
};

在上面的示例中,Required将User类型的所有属性设置为必填项。所以,requiredUser包含了一个包含所有必填属性的User对象。

  1. Readonly工具类型:
    Readonly将类型T的所有属性设置为只读。它创建了一个新的类型,该类型拥有与T相同的属性,但所有属性都是只读的,无法修改。这对于需要创建只读属性的类型非常有用。

示例代码:

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

const readOnlyUser: Readonly<User> = {
  name: "John",
  age: 25
};

// 尝试修改只读属性会引发类型错误
readOnlyUser.name = "Jane"; // Error: Cannot assign to 'name' because it is a read-only property

在上面的示例中,Readonly将User类型的所有属性设置为只读。所以,readOnlyUser是一个只包含只读属性的User对象。

通过使用这些工具类型,可以灵活地创建更具体的类型,以适应需求,并提供更好的代码可读性和开发经验。

索引类型(Index Types)

索引类型(Index Types)是 TypeScript 中的一种高级类型,可以用于动态地访问和操作对象中的属性。它们允许我们使用类型变量来表示对象属性的名称,并根据这些属性名称来操作对象。

有三种主要的索引类型可在 TypeScript 中使用:keyoftypeofIndexed Access Types

  1. keyofkeyof 是一个索引类型查询操作符,用于获取对象类型的所有属性名称组成的联合类型。例如:
interface Person {
  name: string;
  age: number;
}

type PersonKeys = keyof Person; // 'name' | 'age'
  1. typeoftypeof 是一个类型查询操作符,用于获取变量的类型。它经常与 keyof 一起使用,可以用于获取对象类型的属性名称组成的联合类型。例如:
const person = { name: 'Alice', age: 30 };
type PersonKeys = keyof typeof person; // 'name' | 'age'
  1. Indexed Access Types:Indexed Access Types 是一种类型操作符,使用类似于属性访问操作符来获取对象属性的类型。我们可以使用字符串或类型变量来访问对象的属性。例如:
interface Person {
  name: string;
  age: number;
}

type PersonName = Person['name']; // string

这些索引类型允许我们在编写 TypeScript 代码时更灵活地操作对象的属性,并根据需要检查、访问或操作这些属性。

你可能感兴趣的:(Typescript,typescript,ubuntu,linux)