Collection of TypeScript type challenges

90 阅读7分钟

TypeScript 类型挑战的集合

由于在学习源码的时候,有些typescript看不懂,所以通过练习来学习typescript类型。以下是最近练习的记录!

官网
vscode插件:image.png

前置知识

extends

学习自 blog.csdn.net/qq_34998786…

// 首先来看下extends 咋用 
// 举个例子
  type A1 = 'x' extends 'x' ? string : number; // string
  type A2 = 'x' | 'y' extends 'x' ? string : number; // number
  
  type P<T> = T extends 'x' ? string : number;
  type A3 = P<'x' | 'y'> // string|number
  type A4 = P<never> // never
  //never 会比较特殊,则
  type P1<T> = T extends 'x' ? string : never;
  type A5 = P1<'x' | 'y'> // string

对于使用extends关键字的条件类型(即上面的三元表达式类型),如果extends前面的参数是一个泛型类型,当传入该参数的是联合类型,则使用分配律计算最终的结果。分配律是指,将联合类型的联合项拆成单项,分别代入条件类型,然后将每个单项代入得到的结果再联合起来,得到最终的判断结果。 never会比较特殊,never被认为是空的联合类型,也就是说,没有联合项的联合类型,所以还是满足上面的分配律,然而因为没有联合项可以分配,所以P的表达式其实根本就没有执行,所以A2的定义也就类似于永远没有返回的函数一样,是never类型的。

infer

用于获取类型

Easy

Pick

通过从中 T 选取属性 K 集来构造类型

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

Readonly

构造一个类型,并将T的所有属性设置为只读,这意味着构造类型的属性不能重新分配

type MyReadonly<T> ={
readonly [p in keyof T] = T[P]
}

TupleToObject

给定一个数组,将其转换为对象类型,并且键值在提供的数组中

type TupleToObject<T extends any[]> = {
	[P in T[number]]:T[P]
}

First of Array

实现一个泛型,该泛型 First 采用 Array T 并返回其第一个元素的类型。

1.type First<T extends any[]> = T[number] extends never?never:T[0]
2.type First<T extends any[]> = T extends [] ? never : T[0]
3.type First<T extends any[]> = T extends [infer A, ...infer rest ]?A:never

Length of Tuple

对于给定的元组,您需要创建一个泛型 Length ,选择元组的长度

type Length<T> = T extends {length:infer R}?R:never

Exclude

从可 U 分配给的类型中 T 排除

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

Awaited

如果我们有一个像 Promise 这样的包装类型。我们如何获得包装类型内部的类型?

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

If

实现接受条件 C 、真值和假值 T F 的 util 类型 If<C, T, F> 。 C 应为任一 true 类型 false T , F 可以是任何类型。

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

Concat

在类型系统中实现 typescript Array.concat 函数。类型采用两个参数。输出应该是一个新的数组,其中包含按 ltr 顺序的输入

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

Includes

在类型系统中实现 typescript Array.includes 函数。类型采用两个参数。输出应为布尔值 true 或 false .

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

type Includes<T extends readonly any[], U> = IsEqual<{
  [P in keyof T as T[P] extends U ? 0 : never]: T[P]
}, { 0: U }>

Push

实现 的 Array.push 通用版本

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

Unshift

实现的类型 Array.unshift 版本

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

Parameters

实现内置参数泛型而不使用它

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

Medium

Get Return Type

在不使用它的情况下实现内置 ReturnType 泛型。

type MyReturnType<T extends (...args:any[])=>unknown> = T extends (...args:any)=>infer U?U:never

Omit

通过从中 T 选取所有属性然后删除 K 来构造类型,看的solution:用as非常棒

type MyOmit<T, K extends keyof T> ={
  [P in keyof T as P extends K ? never: P  ] : T[P]
}

Readonly 2

K 指定应 T 设置为“只读”的属性集。如果未提供,它 K 应使所有属性只读,就像普通 Readonly 属性一样。

type Readonly2<T,K>={
[P in keyof T as P extends K ? never:P]:K[P]
}&{readonly [P in K ]:T[P}

TupleToUnion

实现一个泛型,该泛型 TupleToUnion 涵盖元组的值到其值联合。

type TupleToUnion<T extends any[]> =T[number] 

Chainable Options

在这个挑战中,你需要键入一个对象或一个类 - 无论你喜欢什么 - 提供两个函数 option(key, value) 和 get() .在 中 option ,可以通过给定的键和值扩展当前配置类型。我们应该通过 访问 get 最终结果。 下面是找到的最好的答案及解释:

  1. 可以使用 T = {} 来作为默认值,甚至默认参数与默认返回值,再通过递归传递 T 即可实现递归全局记录
  2. option 是一个函数接收两个值:K 和 V,为了约束 key 不可重复必须范型传入,value 是任意类型范型不做约束直接透传
  3. 先验证重复 key,实现传入相同 key 报错
  4. 然后发现案例3无法通过,案例3是传入了相同的 key 但类型不同,因此在 KextendskeyofT 后面增加验证只有类型相同才返回 never
  5. 最后直接 & 联合并不能将相同 key 的类型覆盖,因此用 Omit 去掉前一个类型中相同的 key
type Chainable<T = {}> = {
	option:<K extends T,V>(key:K extends keyof T ? never:K,value:V)=>Chainable<Omit<T,K>&Record<K,V>>
  get:()=>T
}

Last of Array

Implement a generic Last that takes an Array T and returns its last element. 实现一个泛型,该泛型 Last 采用 Array T 并返回其最后一个元素。

type Last<T extends any[]> =T extends [...any,infer U] ? U:never

Pop

Implement a generic Pop that takes an Array T and returns an Array without it's last element. 实现一个泛型,该泛型 Pop 采用数组 T 并返回一个没有最后一个元素的数组。

type Pop<T extends any[]> =T extends [...infer U,any]?U:[]

Promise.all

Type the function PromiseAll that accepts an array of PromiseLike objects, the returning value should be Promise where T is the resolved result array. 键入接受 PromiseLike 对象数组的函数 PromiseAll ,返回值应为 Promise where T 是解析的结果数组。

declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{ [K in keyof T]: T[K] extends Promise<infer U> | infer U ? U : T[K] }>

Type Lookup

Sometimes, you may want to look up a type in a union by its attributes. 有时,您可能希望按属性在联合中查找类型。 In this challenge, we would like to get the corresponding type by searching for the common type field in the union Cat | Dog. In other words, we will expect to get Dog for LookUp<Dog | Cat, 'dog'> and Cat for LookUp<Dog | Cat, 'cat'> in the following example. 在这个挑战中,我们希望通过搜索并集中 Cat | Dog 的公共 type 字段来获得相应的类型。换句话说,我们希望在以下示例中得到 Dog for 和 Cat for LookUp<Dog | Cat, 'dog'> LookUp<Dog | Cat, 'cat'> 。

type Lookup<U extends {type:string},T extends string> = U extends {type:T} ? U:never

TrimLeft

Implement TrimLeft which takes an exact string type and returns a new string with the whitespace beginning removed. 实现 TrimLeft ,它采用精确的字符串类型并返回一个删除空格开头的新字符串。

type Whitespace = " " | "\n" | "\t"
type TrimLeft<S extends string> = S extends `${Whitespace}${infer U}`? TrimLeft<U> : S

Trim

Implement Trim which takes an exact string type and returns a new string with the whitespace from both ends removed. 实现 Trim ,它采用精确的字符串类型并返回一个新字符串,其中两端的空格都被删除了。

type WhiteList = ' ' | "\n" | "\t"
type Trim<S extends string> = S extends `${WhiteList}${infer U}`|`${infer U}${WhiteList}`?Trim<U>:S

Capitalize

Implement Capitalize which converts the first letter of a string to uppercase and leave the rest as-is. 实现 Capitalize 将字符串的第一个字母转换为大写,其余字母保持原样。

type MyCapitalize<S extends string> = S extends `${infer U}${infer I}`? `${Uppercase<U>}${I}`:S

Replace

Implement Replace<S, From, To> which replace the string From with To once in the given string S 实现 Replace<S, From, To> 将字符串替换为 To 给定字符串 From S 中的一次

type Replace<S extends string, From extends string, To extends string> = From extends ""?S:S extends `${infer U}${From}${infer V}`?`${U}${To}${V}`:S

ReplaceAll

Implement ReplaceAll<S, From, To> which replace the all the substring From with To in the given string S 实现 ReplaceAll<S, From, To> ,将给定字符串 S 中的所有子字符串 From 替换为To

type ReplaceAll<S extends string, From extends string, To extends string>= From extends ""?S:S extends `${infer U}${From}${infer V}`?`${U}${To}${ReplaceAll<`${V}`,From,To>}`:S

Append Argument

For given function type Fn, and any type A (any in this context means we don't restrict the type, and I don't have in mind any type 😉) create a generic type which will take Fn as the first argument, A as the second, and will produce function type G which will be the same as Fn but with appended argument A as a last one. 对于给定的函数类型,和任何类型(在这种情况下,任何类型意味着我们不限制类型,而且我不考虑任何类型)创建一个泛型类型,该泛型类型将作为第一个参数,作为第二个参数,并将生成函数类型,该函数类型 Fn A 😉 G 将与最后一个相同 Fn , A 但附加参数 A 作为最后一个。 Fn

type AppendArgument<Fn extends (...args:any[])=>any, A> = Fn extends (...args:infer V)=>infer U?( ...args:[...V,A])=>U:never

Permutation

Implement permutation type that transforms union types into the array that includes permutations of unions. 实现将联合类型转换为包含联合排列的数组的排列类型。 下方是找到的最佳答案: 这里利用了extends 假如extends前面是泛型 则当传入该参数的是联合类型,则使用分配律计算最终的结果

type Permutation<T,K=T> = [T] extends [never]?[]:K extends K?[K,...Permutation<Exclude<T,K>>]:never 

Length of String

Compute the length of a string literal, which behaves like String#length. 计算字符串文本的长度,其行为类似于 String#length 。

type LengthOfString<T extends String,K extends any[]=[]>=T extends `${infer U}${infer V}`?LengthOfString<V,[...K,U]>:K["length"]

Flatten

In this challenge, you would need to write a type that takes an array and emitted the flatten array type. 在此挑战中,您需要编写一个接受数组并发出平展数组类型的类型。

type Flatten<T extends any[],K extends any[]=[]> =T extends [infer U,...infer V]?U extends [...infer A]?Flatten<[...A,...V],K>:Flatten<[...V],[...K,U]> :K

Append to Object Implement a type that adds a new field to the interface. The type takes the three arguments. The output should be an object with the new field. 实现向接口添加新字段的类型。该类型采用三个参数。输出应该是具有新字段的对象。

type AppendToObject<T, U extends string, V> = {
  [P in keyof T|U]:P extends U?V:P extends keyof T?T[P]:never
}