TypeScript 简单手写题一网打尽

225 阅读4分钟

前言

typeof

类型上下文中获取变量或者属性的类型

interface Person { name: string; age: number; } 
const rubyliu: Person = { name: "rubyliu", age: 20 }; 
type Sem = typeof rubyliu; // type rubyliu = Person

keyof

将接口的键转换为联合类型

interface Person {
    name: string;
    age: number;
}
keyof Person // 'name'|'age'

in

遍历联合类型。 不能直接遍历接口,会报错!!!

interface Person {
    name: string;
    age: number;
}
[T in keyof]

infer

声明一个变量,通常是元组或函数中

const arr1: any[] = [1,'2',3];
[infer first,...infer rest]
(args: any[]) => any = (args: infer P) => any

extends

泛型约束,可以用来判断交集

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

TypeChallenges easy

Pick

从一个接口中选出几个类型

题目

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> = { [P in K]: T[P] }

Omit

从一个接口中去除几个泛型

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

type TodoPreview = MyOmit<Todo, 'description' | 'title'>

const todo: TodoPreview = {
completed: false,
}

答案

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

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]}

Tuple to Object

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

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'}

答案

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

解释

  • T 是一个元组或数组类型,例如 ['a', 'b', 'c']
  • T[number] 表示从 T 中提取所有元素的类型。对于元组 ['a', 'b', 'c']T[number] 就是 'a' | 'b' | 'c',即元组中所有元素的联合类型。

示例

type Tuple = ['a', 'b', 'c'];
type Elements = Tuple[number]; // 'a' | 'b' | 'c'

Array first Element

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

例如:

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

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

答案

type FirstArray<T extends any[]> = T extends [] ? never : T[0]

Length of Tuple

创建一个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 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> 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。

type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // string

答案

type MyAwaited<T extends Promise<any>> = T extends Promise<infer U>
  ? U extends Promise<any>
    ? MyAwaited<U> // 如果 U 仍然是 Promise,递归解开
    : U // 如果 U 不是 Promise,返回 U
  : never; // 如果 T 不是 Promise,返回 never

If

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

例如:

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 Concat<T extends any[], U extends any[]> = [...T,...U]

Equal

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

例如:

type Equal<X,Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false

NotEqual

export type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true

Includes

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

例如:

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

答案

type Includes<T extends readonly any[], U> = Equal<T[0],U> extends true ? true : T extends [T[0],...infer rest] ? Includes<rest,U> : false

Push

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

例如:

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

答案

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

Unshift

实现类型版本的 Array.unshift

例如:

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

答案

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

Parameters

实现内置的 Parameters 类型,而不是直接使用它,

用于提取函数类型 T 的参数类型,并将其作为一个元组类型返回

例如:

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 P) => any ? P : never