课程名称: 晋级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 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦