type-challenges-简单篇

61 阅读3分钟

做体操之前阅读:

  1. 官方文档

简单

实现 Pick

不使用 Pick<T, K> ,实现 TS 内置的 Pick<T, K> 的功能。

从类型 T 中选出符合 K 的属性,构造一个新的类型

例如:

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}

答案:

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

实现Readonly

不要使用内置的Readonly<T>,自己实现一个。

泛型 Readonly<T> 会接收一个 泛型参数,并返回一个完全一样的类型,只是所有属性都会是只读 (readonly) 的。

也就是不可以再对该对象的属性赋值。

例如:

interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property

答案:

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

元组转换为对象

将一个元组类型转换为对象类型,这个对象类型的键/值和元组中的元素对应。

例如:

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

答案:

// ps: type PropertyKey = string | number | symbol
type TupleToObject<T extends readonly PropertyKey[]> = {
  [P in T[number]]: P
}

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

第一个元素

实现一个First<T>泛型,它接受一个数组T并返回它的第一个元素的类型。

例如:

type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type head1 = First<arr1> // 应推导出 'a'
type head2 = First<arr2> // 应推导出 3

答案:

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

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

元组文档:www.typescriptlang.org/docs/handbo…

infer文档:www.typescriptlang.org/docs/handbo…

获取元组长度

创建一个Length泛型,这个泛型接受一个只读的元组,返回这个元组的长度。

例如:

type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5

答案:

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

实现 Exclude

实现内置的 Exclude<T, U> 类型,但不能直接使用它本身。

从联合类型 T 中排除 U 中的类型,来构造一个新的类型。

例如:

type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'

答案:

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

Awaited

假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。

例如:Promise<ExampleType>,请你返回 ExampleType 类型。

type ExampleType = Promise<string>

type Result = MyAwaited<ExampleType> // string

答案:

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

If

实现一个 IF 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 F。 C 只能是 true 或者 false, T 和 F 可以是任意类型。

例如:

type A = If<true, 'a', 'b'>  // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'

答案:

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

Concat

在类型系统里实现 JavaScript 内置的 Array.concat 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。

例如:

type Result = Concat<[1], [2]> // expected to be [1, 2]

答案:

type Tuple = readonly unknown[];

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

Includes

在类型系统里实现 JavaScript 的 Array.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false

例如:

type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`

答案:

// 这里,`{ [P in T[number]]: true }` 创建了一个映射类型,其中键是元组 `T` 中的所有元素,值为 `true`。
// 然后,我们使用键 `U` 索引到此映射类型。如果结果类型扩展了 `true`,则 `Includes<T, U>` 的结果为 `true`,否则结果为 `false`。

type Includes<T extends readonly any[], U> = {
  [P in T[number]]: true
}[U] extends true ? true : false;
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Last]
    ? 
        Equal<U, First> extends true
            ? true
            : Includes<Last, U>
    : false

Push

在类型系统里实现通用的 Array.push 。

例如:

type Result = Push<[1, 2], '3'> // [1, 2, '3']

答案:

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

Unshift

实现类型版本的 Array.unshift

例如:

type Result = Unshift<[1, 2], 0> // [0, 1, 2,]

答案:

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

Parameters

实现内置的 Parameters 类型,而不是直接使用它,可参考TypeScript官方文档

例如:

const foo = (arg1: string, arg2: number): void => {}

type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number]

答案:

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