为了账号安全,请及时绑定邮箱和手机立即绑定

【九月打卡】第4天 TypeScript学习 9-27章

标签:
征文 活动

课程名称: 晋级TypeScript高手,成为抢手的前端开发人才

课程章节: 9-27 面试题——百度复杂面试题

课程讲师: keviny79

课程内容:
本章节主要出了一道 复杂面试题

题目:抽离出类中方法,合并成一个对象类型

interface Action<T = any> {
  type: string;
  payload?: T;
}

class FoodModule {
  public static topic: string;
  public count!: number;

  constructor(count: number) {
    this.count = count;
  }
  delay(promise: Promise<number>) {
    return promise.then((second: number) => ({
      type: "delay",
      payload: `延迟 ${second} 秒`,
    }));
  }

  searchFoodByCity(action: Action<String>) {
    return {
      payload: action.payload,
      type: "searchFoodByCity",
    };
  }
}

// 实现1:根据测试12提供的条件,要求实现如下
// 实现1:FoodModule 参数类型和返回值类型被部分过滤,如下
type asyncMethodConnect<T, U> = (input: T) => Action<U>; //过滤后的 delay 方法类型
type syncMethodConnect<T, U> = (action: T) => Action<U>; //过滤后的 searchFoodByCity

// 实现2:【最终实现】--就是把这两个类型和原来的 FoodModule 类 合并成如下对象:
type Convert = {
  delay: asyncMethodConnect<number, string>;
  searchFoodByCity: syncMethodConnect<String, String>;
};

// 提示:综合应用了 infer,extends keyof 等技术,
// 首先从 获取 FoodModule 类的 delay 和 searchFoodByCity 方法的类型入手分别是:
type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>; // delay 方法类
type syncMethod<T, U> = (action: Action<T>) => Action<U>; // searchFoodByCity 方法类

注意:以下答案是我独自思考一小时后,想不通找老师要的答案后,自己再理解的答案

答案&解析:

interface Action<T = any> {
  type: string;
  payload?: T;
}

class FoodModule {
  public static topic: string;
  public count!: number;

  constructor(count: number) {
    this.count = count;
  }
  delay(promise: Promise<number>) {
    return promise.then((second: number) => ({
      type: "delay",
      payload: `延迟 ${second} 秒`,
    }));
  }

  searchFoodByCity(action: Action<String>) {
    return {
      payload: action.payload,
      type: "searchFoodByCity",
    };
  }
}

// 已知条件
// 类中要抽离的函数类型--之后要和类中方法进行比较
type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>; // delay 方法类
type syncMethod<T, U> = (action: Action<T>) => Action<U>; // searchFoodByCity 方法类
// 以下是要过滤(修改)的方法类型
type asyncMethodConnect<T, U> = (input: T) => Action<U>; //过滤后的 delay 方法类型
type syncMethodConnect<T, U> = (action: T) => Action<U>; //过滤后的 searchFoodByCity

// 1.抽离类中 delay 和 searchFoodByCity,组合成联合类型
type PickType = keyof Pick<FoodModule, "delay" | "searchFoodByCity">;

// 2.关键--方法类型进行比交
//  2-1 M 先和 类中的delay 方法类型进行比较,是就改为我们声明出过滤后的 delay 类型
//  2-2 上面2-1如果不是delay,M 就和 类中的searchFoodByCity 方法类型进行比较,是就改为我们声明出过滤后的 searchFoodByCity 类型
//  2-3 上面2-2如果不是searchFoodByCity,返回 无never
type FuncType<M> = M extends asyncMethod<infer T, infer U>
  ? asyncMethodConnect<T, U>
  : M extends syncMethod<infer T, infer U>
  ? syncMethodConnect<T, U>
  : never;

// 3.抽离出的方法进行组合为对象
//  3-1 [K in PickType]循环 一开始抽离出的 联合类型
//  3-2 FoodModule[K] 循环获取对应类中方法的类型
//  3-3 FuncType<FoodModule[K]> 获取到类中方法的类型后,进行比较,类型一样就修改成 过滤后的方法类型
type PickClassFuncType = {
  [K in PickType]: FuncType<FoodModule[K]>;
};

// 4.验证是否有用
let testObj: PickClassFuncType = {
  delay: () => {
    return {
      type: "aaa",
    };
  },

  searchFoodByCity: () => {
    return {
      type: "bbb",
    };
  },
};

老师答案:

interface Action<T> {
  payload?: T;
  type: string;
}

type delayFunc2 = (input: Promise<number>) => Promise<Action<string>>;
type delayFunc = EffectModule["delay"];
//type delayFunc3= M extends (input: Promise<T>) => Promise<Action<U>>

class EffectModule {
  count = 1;
  message = "hello!";

  delay(input: Promise<number>): Promise<Action<string>> {
    return input.then((i) => ({
      payload: `hello ${i}!`,
      type: "下单",
    }));
  }

  setMessage(action: Action<Date>) {
    return {
      payload: action.payload!.getMilliseconds(),
      type: "set-message",
    };
  }
}
// 已知条件:
type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>;
type asyncMethodConnect<T, U> = (input: T) => Action<U>;

type syncMethod<T, U> = (action: Action<T>) => Action<U>;
type syncMethodConnect<T, U> = (action: T) => Action<U>;

// 第一步:获取函数名组成的联合类型。
type MethodNameType<T> = {
  [F in keyof T]: T[F] extends Function ? F : never;
}[keyof T]; // 增加了[keyof T]的目的 表示获取冒号后面的函数名[F]组成的联合类型

// 测试: 结果为"delay" | "setMessage"
type MethodNameUnionType = MethodNameType<EffectModule>;

// 第二步:声明 dispatchType 类型
// 详细理解:
type dispatchType<M> = M extends asyncMethod<infer T, infer U>
  ? asyncMethodConnect<T, U>
  : M extends syncMethod<infer T, infer U>
  ? syncMethodConnect<T, U>
  : never;

// 第三步:分派结果:
//  其中:EffectModule[methodName] 来获取方法类型
//  理解: dispatchType< EffectModule["delay"]>
// EffectModule["delay"]=(input: Promise<T>) => Promise<Action<U>>
// 如果M extends asyncMethod<infer V, infer W> 泛型约束成立
// 那么dispatchType就是asyncMethodConnect类型

//  理解: dispatchType<EffectModule["setMessage"]>
//  EffectModule["setMessage"]=(input: Action<T>) => Action<U>>
//  extends syncMethod<infer V, infer W> 泛型约束成立
// dispatchType就是syncMethodConnect<V, W>类型
// 如果还不成立,就直接返回never

// 4.通用化
type DispatchResultComm<T> = {
  [methodName in MethodNameType<T>]: dispatchType<T[methodName]>;
};
type ResultType = DispatchResultComm<EffectModule>;

let result: ResultType = {
  delay(input: number): Action<string> {
    return {
      payload: `hello!`,
      type: "下单",
    };
  },
  setMessage(action: Date) {
    return {
      payload: action.getMilliseconds(),
      type: "set-message",
    };
  },
};
export {};

课程收获:
本节面试题对自身的学习情况有了充分的理解。
对infer,extends keyof 的组合使用更加深刻,期待后面的学习。

图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
10
获赞与收藏
5

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消