更强大的使用typescript(1)

285 阅读4分钟
    /**
        @date 2021-12-11 更强大的使用typescript
        @description ts-type-challenge-简单题目
    */

壹(序)

TypeScript是JavaScript的超集,在此不做过多介绍。

为什么要使用TypeScript?

很简单,为了让项目变得更好,不管什么项目,最终追求的一定是功能,用户可不会在乎你的代码写得怎么样,再优美得像诗的代码,功能像 * ,bug百出的话什么都白搭。

但是从我们开发者的角度来讲,在功能运作良好的基础之上,更吸引我们的应该是代码质量,说到代码质量,不得不提到JavaScript这门语言,作为一门曾经的‘玩具语言’,在Web大前端蓬勃发展的今天成为了主流语言之一,但是JavaScript实在是太灵活了,开发者几乎是可以不受任何限制的使用它,如果你随心所欲的写Javascript,你的下一任可能顶不住一天就想提桶走人了。

TypeScript就能够在一定程度上限制一下JavaScript的灵活性,比如js中,可以把一个变量肆意玩弄,一会儿写成string,一会写成number,甚至写成object,但ts就不一样了,它更独立,更强大,它不会允许你随意改变它,说好是string就是string,是number是boolean是object是array都不行。

当然,ts还有很多其他的功能,不仅仅只是一个类型检查工具,它还有很多高级特性,虽然可能日常开发中不会用到,不过还是那句话,基本的了解还是需要的

今天带来的是TypeScript的challenges,可以帮助你提升对ts的使用和理解,这篇文章先从简单的题目做起。

开始之前需要对ts的一些操作符做一个基本了解:TypeScript强大的类型操作

贰(Pick)

题目:实现一个ts内置的Pick<T, K>

For example(第二题开始只写解答,如感兴趣可以自己去做一做)

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

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

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

需要实现什么:一个type,传入两个泛型(T, K),根据K去获取T中的属性,返回一个新的type;

怎么实现:K应该是T的属性名中的其中一种或几种(如上面的Todo是T,而K是'title' | 'completed',正好这两个属性是Todo中有的),所以我们需要在传入的泛型上就处理:MyPick<T, K extends keyof T>,然后根据K得到T中的属性:

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

叁(Readonly)

readonly其实就是让每个属性成为一个readonly的值,那么只要可以获取T中的所有属性并返回,再加上readonly就行了,那么如何获取T的属性呢?

使用 in and keyof 操作符,[K in keyof T],然后赋值为T[K],最后加上 readonly:

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

肆(Tuple To Object)

首先我们需要取到元组中的每一项,用T[number]就可以,但是这是一个值,并不是一个属性名,所以我们还需要使用in,最后返回的就是in得到的值:

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

伍(First of Array)

获取第一个值很简单,返回T[0]就行了,不过需要注意T可能是一个空数组:

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

陆(Length of Tuple)

获取元组长度,那就使用T['length'],但是需要限制T是一个数组,并且加上readonly

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

柒(Exclude)

将U中的值从T中剔除,那么只需要判断T是否在U中,在的话返回never,不在返回T

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

捌(Awaited)

可以使用infer推断一下,

type MyAwaited<T> = T extends Promise<infer P> ? P : T

不过还需要考虑Promise中嵌套Promise的情况,所以使用递归处理一下:

type MyAwaited<T> = T extends Promise<infer P>
  ? (P extends Promise<any>
    ? MyAwaited<P>
    : P)
  : T

玖(If)

对C做一个简单的判断就行:

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

拾(Concat)

首先需要保证T和U是数组,然后解构就行了:

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

拾壹(Includes)

判断U是否在T中,需要一项项的判断,所以用T[number]是行不通的,可以通过递归处理,先得到第一项和后面的所有[infer F, ...infer R],再比较第一项与U,true的话直接返回,false的话递归:

type Includes<T extends readonly any[], U> = T extends [infer F, ...infer R]
  ? (Equal<F, U> extends true ? true : Includes<R, U>)
  : false

拾贰(Push)

首先确保T是数组,然后直接返回:

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

拾叁(Unshift)

同push

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

拾肆(Parameters)

使用infer进行推断就行:

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