typescript 类型体操 之 3-medium-omit

662 阅读3分钟

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

前言

在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,在上一篇文章中,我们介绍完了简单题的所有题,那么今天就要正式开始刷中等题了。

今天我们来做中等题的第一题和第二题,因为第一题比较简单,和之前简单题提过的知识点十分相似,所有不用过多的字符来介绍,今天的中等题第一题为 2-medium-return-type,第二题为 3-medium-omit

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

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

2-medium-return-type

我们首先还是先通过题目的README和测试case来看一下题目的要求

image.png

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

type cases = [
  Expect<Equal<string, MyReturnType<() => string>>>,
  Expect<Equal<123, MyReturnType<() => 123>>>,
  Expect<Equal<ComplexObject, MyReturnType<() => ComplexObject>>>,
  Expect<Equal<Promise<boolean>, MyReturnType<() => Promise<boolean>>>>,
  Expect<Equal<() => "foo", MyReturnType<() => () => "foo">>>,
  Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
  Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>
];

type ComplexObject = {
  a: [12, "foo"];
  bar: "hello";
  prev(): number;
};

const fn = (v: boolean) => (v ? 1 : 2);
const fn1 = (v: boolean, w: any) => (v ? 1 : 2);

结合题目给的README和测试case,我们能够看出,这道题的要求和我们之前的简单题的获取函数入参的类型是完全一样的,那么这里就只放下这道题的答案,具体的过程不清楚的可以看一下之前的文章。

typescript 类型体操 之 简单题完结 - 掘金 (juejin.cn)

type MyReturnType<T> = T extends (...arg: any) => infer R ? R : never;

3-medium-omit

今天的第二题需要我们实现 TS 的内置工具类型 Omit

image.png

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

type cases = [
  Expect<Equal<Expected1, MyOmit<Todo, "description">>>,
  Expect<Equal<Expected2, MyOmit<Todo, "description" | "completed">>>
];

// @ts-expect-error
type error = MyOmit<Todo, "description" | "invalid">;

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

interface Expected1 {
  title: string;
  completed: boolean;
}

interface Expected2 {
  title: string;
}

Omit 的作用和我们之前做过的简单题 Pick 是互补的,Pick 是选取对象中的某些属性,Omit 则是剔除掉对象中的某些属性。

利用JS进行对比学习

题目需要实现一个函数,这个函数会传入一个对象,在这个对象中有着一些属性,然后这个函数还会传入一个数组,这个数组当中保存着一些属性。

我们会剔除掉数组中含有的对象属性,并且返回一个新的对象。

function myPick(todo, keys){
    for(let key in keys){
        if(key in todo){
            delete todo[keys]
        }
    }
    return todo
}

实现 MyOmit

Omit 可以将它的作用分为两部分,并且这两部分都是我们之前认识的工具方法,一个就是 Pick 一个是 Exclude

Exclude 工具方法 和 Pick 工具方法

Exclude 工具方法的作用是从一个联合类型中剔除掉某些类型。

type AA = 1 | 2 | 3

type BB = Exclude<AA,1>
// type BB = 2 | 3

Pick 工具方法的作用是从某一个对象中选出某些属性。

type AAA = {
  a:1
  b:2
}

type BBB = Pick<AAA ,"b">
// type BBB = {
//   b: 2;
// }

那么是不是就会神奇的发现,Omit 的作用其实就是两个工具方法的一个整合,Pick 第二个参数会传入一个联合类型,然后我们可以使用 Exclude 来是这个联合类型变成传入对象的 key 的互补部分,这样子就能够实现选中与 Pick 互补的那部分对象属性

type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

这样子之后,测试 case 就能够全部通过了

知识点

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

  1. Exclude 工具方法
  2. Pick 工具方法

Exclude 工具方法的实现并不复杂,可以查看官方文档,Pick 工具方法我们在之前简单题就已经手写过了,可以查看之前的文章

typescript 类型体操 之 4-easy-pick - 掘金 (juejin.cn)

总结

今天第一次碰到了由两个工具类型组合实现的工具类型,灵活的使用工具类型,能够相互配合,实现很多原有的不支持的功能。