写在前面
最近在学 TypeScript, 对类型编程有点感兴趣?虽然自己可能对这方面还是一无所知。恰好发现LeetCode国区有这么道开源的面试题,就拿过来做了下。
简要题解
其实分析一下我们的需求就是两点: 1.将EffectModule类的函数签名的类型改了 2.将EffectModule类的非函数属性都去掉
对于第一点,我们可以直接根据题目的要求遍历实例的键,利用infer的推断能力拿到payload的属性还有函数签名里Promise和Action泛型里的值,拿到后根据题目要求换成需要的函数签名即可:
type Change<T> = {[K in keyof T]:
T[K] extends ((input: Promise<infer P>) => Promise<{payload: infer U;type:string}>)?
((input: P) => Action<U>):
T[K] extends ((action: Action<infer P>) => {payload: infer U;type:string})?
((action: P) => Action<U>):
never;}
const effectModule = new EffectModule();
type test = Change<typeof effectModule>;
经过Change的变换后我们得到的test类型其实是包含 count:never
和 message:never
两个属性,我们要去掉它,所以我们利用extends判断类型是不是函数从而拿出EffectModule类的非函数属性键的名字,然后利用Omit方法去掉这些非函数属性的键即可,具体实现如下:
type omitFuncKeys<T> = {[K in keyof T]: T[K] extends Function? never: K}[keyof T];
type final = Omit<Change<typeof effectModule>, omitFuncKeys<EffectModule>>;
完整代码:
import { expect } from "chai";
interface Action<T> {
payload?: T;
type: string;
}
class EffectModule {
count = 1;
message = "hello!";
delay(input: Promise<number>) {
return input.then(i => ({
payload: `hello ${i}!`,
type: 'delay'
}));
}
setMessage(action: Action<Date>) {
return {
payload: action.payload!.getMilliseconds(),
type: "set-message"
};
}
}
// 实现部分
type Change<T> = {[K in keyof T]:
T[K] extends ((input: Promise<infer P>) => Promise<{payload: infer U;type:string}>)?
((input: P) => Action<U>):
T[K] extends ((action: Action<infer P>) => {payload: infer U;type:string})?
((action: P) => Action<U>):
never;}
const effectModule = new EffectModule();
type omitFuncKeys<T> = {[K in keyof T]: T[K] extends Function? never: K}[keyof T];
type final = Omit<Change<typeof effectModule>, omitFuncKeys<EffectModule>>;
// 修改 Connect 的类型,让 connected 的类型变成预期的类型
type Connect = (module: EffectModule) => final;
const connect: Connect = m => ({
delay: (input: number) => ({
type: 'delay',
payload: `hello 2`
}),
setMessage: (input: Date) => ({
type: "set-message",
payload: input.getMilliseconds()
})
});
type Connected = {
delay(input: number): Action<string>;
setMessage(action: Date): Action<number>;
};
export const connected: Connected = connect(new EffectModule());
Written by Yiming Pan who lives and works in Hangzhou China. Welcome follow me on Github
Cannot load comments. Please check you network.