typescript 类型体操 之 62-medium-type-lookup

349 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

前言

在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,在上一篇文章中,我们完成了中等的第九题,今天来做中等的第十题 62-medium-type-lookup

下面这个是类型体操github仓库:

type-challenges/type-challenges: Collection of TypeScript type challenges with online judge (github.com)

62-medium-type-lookup

image.png

import type { Equal, Expect } from '@type-challenges/utils'

interface Cat {
  type: 'cat'
  breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}

interface Dog {
  type: 'dog'
  breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
  color: 'brown' | 'white' | 'black'
}

type Animal = Cat | Dog

type cases = [
  Expect<Equal<LookUp<Animal, 'dog'>, Dog>>,
  Expect<Equal<LookUp<Animal, 'cat'>, Cat>>,
]

从README和测试用例中能够得出,我们需要实现一个工具函数他能够通过一个对象里面的type属性来区分这个对象,并且从一个联合类型中返回指定的这个type的对象。

通过 JS 对比学习

我们需要实现一个函数,利用数组来代替联合类型,数组中存放着对象,通过对象内的type属性来决定返回哪一个数组内的对象。

function lookUp(objArr:{type:string}[],type:string){
  for(const obj of objArr){
    if(obj.type = type){
      return obj
    }
  }
}

image.png

实现 LookUp

通过 JS 的实现先有一个思路,我们需要去遍历输入的联合类型,并且去比对联合类型中的对象里面的 type 属性和 传入的第二个参数是否相同,是的话就返回这个对象。

这两步使用条件链和 infer 关键字就能够很简单的实现,主要是要了解一下,对于一个对象,它内部含有 type 属性。那么一切含有 type 属性的对象都都会是它的子类。

type LOOKUP2 = {
  type:'1'
  name:'2'
} extends {type:string}
  ?true
  :false
// type LOOKUP2 = true

作为 {type: string} 的子类,那么就只是要求这个对象中一定要有 type 这个属性,如果包含有其他属性,也是没有关系的,这就涉及到 TS 类型系统对应的鸭子类型,感兴趣的伙伴可以通过之前的文章了解一下。

那么,在我们知道了要如何去判断一个对象中是否含有 type,那么就可以通过 infer 来获取这个 type 具体的值,并且和传入的 字符串 进行判断,那么这道题就变得是否简单了。

type LookUp<U, T> = U extends { type: infer R }
  ? R extends T
    ? U
    : never
  : never;

这样我们的测试 case 就能够全部通过了。

知识点

关于上述提到了部分的知识点:

  1. TS 类型系统之鸭子类型
  2. infer 关键字
  3. 条件链

本文主要的要点在于通过对于对象属性的一个描述来约束传入的对象一定都含有type属性,以及获取对象中的这个type属性具体的值,关于鸭子类型可以阅读一下之前的文章:

Typescript的结构化类型系统 - 掘金 (juejin.cn)

总结

今天我们做完了中等的第十题,题型比较简单,对于 TS 中的类型系统有一定的了解的话,很容易就能够解出来这道题了。