TS类型体操练习笔记-Easy

91 阅读11分钟

type-challenges项目的一些练习记录 当做字典来用

MyPick

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

注释

这个类型别名 MyPick 的作用是从类型 T 中挑选出某些属性,并构造一个新的类型。它类似于 TypeScript 内置的 Pick 类型。

类型参数

  • T: 一个对象类型,你可以从中挑选属性。
  • K extends keyof T: 一个联合类型,表示 T 类型中的一组键(即属性名)。

关键点

  • K extends keyof T 限制了 K 必须是 T 的键之一。这确保了你只能挑选存在于 T 中的属性。

映射类型

映射类型是 TypeScript 中的一种高级类型,它允许你创建基于另一个类型的新类型。我们这里就是在使用映射类型。

分解映射类型 { [P in K]: T[P] }
  1. [P in K]: - 这是一个索引签名,它表示对于联合类型 K 中的每一个属性 P,我们都将其包含在新类型中。 - 具体来说,P 将遍历 K 中的所有属性。
  2. T[P]: - 我们通过 T[P] 获取 T 类型中属性 P 的类型。 - 这意味着新类型中的属性 P 将拥有与 T 中相同的类型。

MyReadonly

type MyReadonly<T> = {
  readonly [K in keyof T]: T[K]
}

注释

这个类型别名 MyReadonly 的作用是将对象类型 T 中的所有属性设置为只读(readonly)。它类似于 TypeScript 内置的 Readonly 类型。

类型参数

  • T: 一个对象类型,你希望将其属性设置为只读。

映射类型与关键字

这个类型别名利用了 TypeScript 的映射类型和 readonly 修饰符来实现目标。

分解映射类型 { readonly [K in keyof T]: T[K] }
  1. readonly: - readonly 关键字用于将属性设置为只读。只读属性在初始化之后不能再被修改。
  2. [K in keyof T]: - 这是一个索引签名,用于遍历 T 类型的键(即属性名)。 - keyof T 生成一个联合类型,包含 T 类型的所有键。 - K in keyof T 意味着对于 T 中的每一个键 K,我们都将其包含在新类型中。
  3. T[K]: - 我们通过 T[K] 获取 T 类型中属性 K 的类型。 - 这意味着新类型中的属性 K 将拥有与 T 中相同的类型,但是现在它们是只读的。

TupleToObject

type TupleToObject<T extends readonly (string | number | symbol)[]> = {
  [P in T[number]]: P
}

注释

这个类型别名 TupleToObject 用于将一个包含字符串、数字或符号的元组转换成对象类型,元组中的每个元素将成为对象中的键和值。

类型参数

  • T extends readonly (string | number | symbol)[]:
  • 这是一个泛型参数 T,表示一个只读数组(元组),其中的元素类型可以是字符串、数字或符号。这确保了 T 中的元素可以有效地用作对象的键。

映射类型与索引类型

这个类型别名利用了 TypeScript 的映射类型和索引类型来实现目标。

关键点
  1. [P in T[number]]: - T[number] 是 TypeScript 中的一种语法,用于获取元组 T 中所有元素的联合类型。
    • 举例来说,如果 T['a', 'b', 'c'],那么 T[number] 就是 'a' | 'b' | 'c'。 - [P in T[number]] 是一个遍历联合类型的映射类型。对于 T 中的每一个元素 P,我们都将其作为对象的键。
  2. { [P in T[number]]: P }: - 对象的键和值都为 P,即元组中的每一个元素既是新对象中的键,也是该键对应的值。

First

// answer1
type First<T extends any[]> = T extends [] ? never : T[0]

// answer2
// type First<T extends any[]> = T['length'] extends 0 ? never : T[0]

// answer3
// type First<T extends any[]> = T extends [infer A, ...infer rest] ? A : never

注释-answer1

类型参数

  • T extends any[]:
  • 这是一个泛型参数 T,它必须是一个数组类型。数组中的元素可以是任何类型(即 any[])。

条件类型

TypeScript 中的条件类型类似于 JavaScript 中的三元表达式 condition ? trueExpression : falseExpression,它在类型层次上进行条件判断和选择。

分解条件类型
  1. T extends []: - 这里的 extends 用于检查类型兼容性。 - T extends [] 用于判断 T 是否为一个空数组类型,即没有任何元素的数组。
  2. ? never: - 如果 T 确实是一个空数组类型,那么返回 never 类型。 - never 类型表示的是那些永不存在的值。在这里,它被用来表示空数组没有第一个元素。
  3. : T[0]: - 如果 T 不是一个空数组类型,则返回 T[0],即数组 T 的第一个元素的类型。

Length

type Length<T extends readonly any[]> = T['length']

注释

类型参数

  • T extends readonly any[]:
  • 这是一个泛型参数 T,表示它必须是一个只读数组类型。数组中的元素可以是任何类型(即 any[])。

数组长度属性

在 TypeScript 中,数组类型有一个预定义的属性 length,它代表数组的长度。我们可以通过类型系统访问这个属性来获取数组的长度类型。

分解类型
  1. T['length']: - 这是在类型级别上访问数组 Tlength 属性。 - 对于任意数组类型 TT['length'] 的值是一个数字字面量类型,表示该数组的长度。

MyExclude

type MyExclude<T, U> = T extends U ? never : T

注释

类型参数

  • T: 待处理的类型,可以是任何类型的联合类型。
  • U: 要从 T 中排除的类型,也可以是任何类型的联合类型。

条件类型

TypeScript 中的条件类型类似于 JavaScript 中的三元表达式 condition ? trueExpression : falseExpression,它在类型层次上进行条件判断和选择。

分解条件类型
  1. T extends U: - 这里的 extends 用于检查类型兼容性。 - T extends U 判断 T 是否可以赋值给 U,即 T 是否是 U 的子类型。如果 TU 的子类型(或 T 中的某些子类型是 U),则条件为真。
  2. ? never: - 如果 T 中的某个成员可以赋值给 U,那么在这个条件类型中,该成员将被排除掉,即返回 never 类型。 - never 类型表示的是那些永不存在的值。在这里,它被用来表示应该从结果中排除这些成员。
  3. : T: - 如果 T 中的某个成员不能赋值给 U,则保留该成员并返回它的类型。

MyAwaited

type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer U>
  ? U extends PromiseLike<any>
    ? MyAwaited<U>
    : U
  : never

注释

类型参数

  • T extends PromiseLike<any>: 该类型参数 T 必须是一个类 Promise 类型的对象,即 T 必须实现 then 方法(例如 Promise 或自定义的 Promise-like 对象)。

条件类型和推断

TypeScript 中的条件类型允许在类型系统中进行条件判断和选择,而推断(infer)则用于从复杂类型中提取某部分类型。

分解条件类型和推断
  1. T extends PromiseLike<infer U>: - 首先检查 T 是否是一个类 Promise 类型。如果是,则将 PromiseLike 的解析类型赋值给类型变量 U。 - infer U 是用来提取 PromiseLike 的泛型参数的。这允许我们获取到 Promise 或类 Promise 对象内部所包装的类型。
  2. U extends PromiseLike<any>: - 如果 U 本身也是一个类 Promise 类型,那么继续递归地解析它。 - 使用 MyAwaited<U> 来继续展开此类 Promise 的内部值,直到达到最里面的非 Promise 值为止。
  3. : U: - 如果 U 不是类 Promise 类型,那么就返回 U
  4. : never: - 如果 T 不是类 Promise 类型,返回 never。这意味着传入的 T 必须是类 Promise 类型,否则类型不合法。

If

type If<C, T, F> = C extends true ? T : F

注释

类型参数

  • C: 条件类型,用于判断是否为 true
  • T: 如果 Ctrue 时返回的类型。
  • F: 如果 Cfalse 时返回的类型 。

条件类型

TypeScript 中的条件类型类似于 JavaScript 中的三元表达式 condition ? trueExpression : falseExpression,它在类型层次上进行条件判断和选择。

分解条件类型
  1. C extends true: - extends 用于检查类型兼容性。在这里,它用于检查 C 是否可以赋值给 true。 - 也就是说,判断 C 是否为 true,如果是,那么条件就为真。
  2. ? T: - 如果 Ctrue,那么返回类型 T
  3. : F: - 如果 Cfalse,那么返回类型 F

Concat

type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]

注释

类型参数

  • T extends readonly any[]:
  • 这是泛型参数 T,必须是一个只读数组类型。readonly 表示该数组不可变。
  • 数组中的元素可以是任何类型(即 any[])。
  • U extends readonly any[]:
  • 这是泛型参数 U,也必须是一个只读数组类型,同样地,元素可以是任何类型。

展开运算符

TypeScript 中的展开运算符(spread operator)... 可以用于在类型系统中将元组或数组类型展开为单个元素。通过使用展开运算符,我们能够将两个数组类型合并为一个新的数组类型。

Includes

type IsEqual<T, U> =
  (<G>() => G extends T ? 1 : 2) extends
  (<G>() => G extends U ? 1 : 2)
    ? true
    : false

type Includes<Value extends readonly any[], Item> =
  Value extends [infer First, ...infer Rest]
    ? IsEqual<First, Item> extends true
      ? true
      : Includes<Rest, Item>
    : false

注释-IsEqual

工作原理

IsEqual 类型别名用于判断两个类型 TU 是否相等。它通过创建匿名函数类型并利用条件类型来实现这一点。

  1. <G>() => G extends T ? 1 : 2: - 这是一个泛型箭头函数,它接受一个泛型参数 G。 - 如果 G 能够赋值给类型 T,则返回类型 1,否则返回类型 2
  2. <G>() => G extends U ? 1 : 2: - 这是另一个泛型箭头函数,工作原理同上,但这里是检查 G 是否能够赋值给 U
  3. 比较两个函数类型: - (<G>() => G extends T ? 1 : 2) extends (<G>() => G extends U ? 1 : 2): - 如果这两个函数类型能够互相赋值,则说明 TU 是相等的,返回 true。 - 否则返回 false

注释-Includes

工作原理

Includes 类型别名用于检查数组类型 Value 中是否包含某个类型 Item。它使用递归泛型和类型推断来实现这一点。

  1. Value extends [infer First, ...infer Rest]: - 这里使用了类型推断(infer)来将数组 Value 分解为首元素 First 和剩余元素 Rest。 - 如果 Value 能被分解,则进入三元操作符,否则返回 false
  2. IsEqual<First, Item> extends true - 使用前面定义的 IsEqual 类型来比较 FirstItem。 - 如果 FirstItem 相等,则返回 true
  3. Includes<Rest, Item>: - 如果 FirstItem 不相等,则递归地检查剩余的元素 Rest

Push

type Push<T extends readonly any[], U> = [...T, U]

注释

类型参数

  • T extends readonly any[]:
  • 这是泛型参数 T,必须是一个只读数组类型。readonly 表示该数组不可变。
  • 数组中的元素可以是任何类型(即 any[])。
  • U:
  • 这是要添加到数组 T 末尾的元素,它的类型没有限制,可以是任何类型。

展开运算符(Spread Operator)

TypeScript 中的展开运算符 ... 可以用于在类型系统中将元组或数组类型展开为单个元素。通过使用展开运算符,我们能够将数组类型 T 的所有元素与新元素 U 合并到一个新的数组类型中。

Unshift

type Unshift<T extends readonly any[], U> = [U, ...T]

注释

类型参数

  • T extends readonly any[]:
  • 这是泛型参数 T,必须是一个只读数组类型。 readonly 表示该数组不可变。
  • 数组中的元素可以是任何类型(即 any[])。
  • U:
  • 这是要添加到数组 T 开头的元素,它的类型没有限制,可以是任何类型。

展开运算符(Spread Operator)

TypeScript 中的展开运算符 ... 可以用于在类型系统中将元组或数组类型展开为单个元素。通过使用展开运算符,我们能够将新元素 U 与数组类型 T 的所有元素合并到一个新的数组类型中,以保证 U 出现在数组的开头。

MyParameters

type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never

注释

类型参数

  • T extends (...args: any[]) => any:
  • 这是泛型参数 T,必须是一个函数类型。
  • (...args: any[]) => any 表示该类型必须是一个接受任意数量和类型参数并返回任意类型值的函数。

条件类型和类型推断

TypeScript 中的条件类型类似于 JavaScript 中的三元表达式 condition ? trueExpression : falseExpression。在这里,我们结合使用条件类型和类型推断来提取函数的参数类型。

分解条件类型
  1. T extends (...args: infer P) => any: - 条件类型用于检查 T 是否符合提供的模式。 - (...args: infer P) 使用 infer 关键字来推断函数参数列表的类型,并将其赋值给类型变量 P。 - 如果 T 符合这个模式,那么 P 将包含 T 的参数列表类型。
  2. ? P : never: - 如果条件为真(即 T 符合函数类型并成功推断出参数类型),则返回被推断的参数类型 P。 - 否则返回 never,表示 T 不是一个有效的函数类型。