打字稿代理通用
Typescript Proxyify Generic
我正在尝试包装通过泛型类型提供的基本接口,以便接口中的每个函数都具有修改后的 return 值。
例如:
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
// to...
interface IBaseInterfaceModified {
test(a?: boolean, b?: number): Promise<boolean> | string;
anotherTest?(a?: number): Promise<number> | string;
}
我曾尝试将映射类型与泛型组合使用,但没有成功。我得到的最接近的是:
type TProxyify<T> = {
[K in keyof T]: TProxy<T[K]>;
};
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type TProxy<T> = {
(): ReturnType<T> | string;
};
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
const s = wrapType<IBaseInterface>();
// s.test() tooltip outputs -> test(): string | Promise<boolean>
但是,类型建议系统删除了 BaseInterface.test
参数名称和类型。无论如何我可以完成这个,以便我可以包装一个基本接口,修改函数 return 类型,并且仍然保持原始基本接口类型建议(参数名称、类型和顺序)不变?
非常感谢任何指导。谢谢
3.0 解决方案见下文
您可以使用类似的方法替换 return 键入答案
type TProxyify<T> = {
[K in keyof T]: AddReturnType<T[K], string>;
};
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type AddReturnType<T, TNewReturn> = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => R | TNewReturn :
IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => R | TNewReturn :
IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => R | TNewReturn :
IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => R | TNewReturn :
IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => R | TNewReturn :
IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => R | TNewReturn :
IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => R | TNewReturn :
IsValidArg<C> extends true ? (a: A, b: B, c: C) => R | TNewReturn :
IsValidArg<B> extends true ? (a: A, b: B) => R | TNewReturn :
IsValidArg<A> extends true ? (a: A) => R | TNewReturn :
() => R | TNewReturn
) : T
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
const s = wrapType<IBaseInterface>();
let ss = s.test(undefined, undefined); // will be string | Promise<boolean>
此方法的问题在于,当与可选参数一起使用时,可选参数变为必需参数(并且类型为 A | 未定义)。这就是为什么调用测试是 s.test(undefined, undefined);
而不是 s.test();
参数名称也未保留,这可能是可读性方面的问题。
编辑
自从回答了原始问题后,typescript 改进了这个问题的可能解决方案。添加 Tuples in rest parameters and spread expressions 我们现在不需要所有重载:
type TProxyify<T> = {
[K in keyof T]: AddReturnType<T[K], string>;
};
type ArgumentTypes<T> = T extends (... args: infer U ) => any ? U: never;
type AddReturnType<T, TNewReturn> = T extends (...args: any[])=> infer R ? (...a: ArgumentTypes<T>) => TNewReturn | R : T;
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
const s = wrapType<IBaseInterface>();
let ss = s.test(undefined, undefined); // will be string | Promise<boolean>
这不仅更短而且解决了很多问题
- 可选参数保持可选
- 保留参数名称
- 适用于任意数量的参数
我正在尝试包装通过泛型类型提供的基本接口,以便接口中的每个函数都具有修改后的 return 值。
例如:
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
// to...
interface IBaseInterfaceModified {
test(a?: boolean, b?: number): Promise<boolean> | string;
anotherTest?(a?: number): Promise<number> | string;
}
我曾尝试将映射类型与泛型组合使用,但没有成功。我得到的最接近的是:
type TProxyify<T> = {
[K in keyof T]: TProxy<T[K]>;
};
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type TProxy<T> = {
(): ReturnType<T> | string;
};
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
const s = wrapType<IBaseInterface>();
// s.test() tooltip outputs -> test(): string | Promise<boolean>
但是,类型建议系统删除了 BaseInterface.test
参数名称和类型。无论如何我可以完成这个,以便我可以包装一个基本接口,修改函数 return 类型,并且仍然保持原始基本接口类型建议(参数名称、类型和顺序)不变?
非常感谢任何指导。谢谢
3.0 解决方案见下文
您可以使用类似的方法替换 return 键入答案
type TProxyify<T> = {
[K in keyof T]: AddReturnType<T[K], string>;
};
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type AddReturnType<T, TNewReturn> = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => R | TNewReturn :
IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => R | TNewReturn :
IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => R | TNewReturn :
IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => R | TNewReturn :
IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => R | TNewReturn :
IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => R | TNewReturn :
IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => R | TNewReturn :
IsValidArg<C> extends true ? (a: A, b: B, c: C) => R | TNewReturn :
IsValidArg<B> extends true ? (a: A, b: B) => R | TNewReturn :
IsValidArg<A> extends true ? (a: A) => R | TNewReturn :
() => R | TNewReturn
) : T
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
const s = wrapType<IBaseInterface>();
let ss = s.test(undefined, undefined); // will be string | Promise<boolean>
此方法的问题在于,当与可选参数一起使用时,可选参数变为必需参数(并且类型为 A | 未定义)。这就是为什么调用测试是 s.test(undefined, undefined);
而不是 s.test();
参数名称也未保留,这可能是可读性方面的问题。
编辑
自从回答了原始问题后,typescript 改进了这个问题的可能解决方案。添加 Tuples in rest parameters and spread expressions 我们现在不需要所有重载:
type TProxyify<T> = {
[K in keyof T]: AddReturnType<T[K], string>;
};
type ArgumentTypes<T> = T extends (... args: infer U ) => any ? U: never;
type AddReturnType<T, TNewReturn> = T extends (...args: any[])=> infer R ? (...a: ArgumentTypes<T>) => TNewReturn | R : T;
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
const s = wrapType<IBaseInterface>();
let ss = s.test(undefined, undefined); // will be string | Promise<boolean>
这不仅更短而且解决了很多问题
- 可选参数保持可选
- 保留参数名称
- 适用于任意数量的参数