javascript - Typescript Proxyify Generic
Tôi đang cố gắng bao bọc một giao diện cơ bản được cung cấp thông qua một kiểu tổng quát để mỗi hàm trong giao diện có giá trị trả về được sửa đổi.
Ví dụ:
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;
}
Tôi đã cố gắng sử dụng các kiểu ánh xạ kết hợp với generics nhưng không có kết quả. Điều gần nhất mà tôi đạt được là:
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>
Tuy nhiên, hệ thống gợi ý kiểu lại loại bỏ tên và kiểu đối số của BaseInterface.test. Có cách nào để tôi có thể thực hiện điều này sao cho tôi có thể bao bọc một giao diện cơ sở, chỉnh sửa kiểu trả về của hàm, và vẫn giữ nguyên các gợi ý kiểu của giao diện cơ sở ban đầu (tên đối số, kiểu và thứ tự) hay không?
Bất kỳ sự hướng dẫn nào đều được đánh giá cao. Cảm ơn.
Câu trả lời #1
Xem bên dưới để biết giải pháp 3.0
Bạn có thể sử dụng một phương pháp tương tự như câu trả về loại replace ở đây
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>
Vấn đề với cách tiếp cận này là khi sử dụng với các tham số tùy chọn, tham số tùy chọn trở thành bắt buộc (và có kiểu A | undefined). Đây là lý do tại sao lời gọi đến test là s.test(undefined, undefined); chứ không phải s.test();
Ngoài ra, tên các tham số không được giữ nguyên, điều này có thể gây ra vấn đề về khả năng đọc.
Chỉnh sửa
Kể từ khi câu hỏi gốc được trả lời, TypeScript đã cải thiện giải pháp có thể cho vấn đề này. Với việc thêm Tuples trong tham số rest và biểu thức spread, giờ đây chúng ta không cần phải có tất cả các overload:
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>
Không chỉ ngắn hơn mà nó còn giải quyết được một số vấn đề
- Các tham số tùy chọn vẫn là tùy chọn
- Tên đối số được giữ nguyên
- Hoạt động với bất kỳ số lượng đối số nào
