/**
@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