打字稿代理通用

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>

这不仅更短而且解决了很多问题

  • 可选参数保持可选
  • 保留参数名称
  • 适用于任意数量的参数