در تایپاسکریپت، چگونه میتوانم یک تابع عمومی ایجاد کنم که پارامترهای یک تابع را با توجه به نام کلاس و تابع آن استنباط کند؟
اساساً، من میخواهم یک تابع لایهای برای تمام فراخوانیهای RPC که انجام میدهم فراهم کنم. این کار برای این است که بتوانم اطلاعات خاصی درباره هر فراخوانی RPC بدون استفاده از یک میانافزار ثبت کنم. من میخواهم بتوانم پارامتر متدی که فراخوانی میشود را با استفاده از rpc[serviceName][method] در TypeScript به دست آورم.
این پیادهسازی فعلی من است که در آن params به اندازه کافی مشخص نیست:
async rpcWrapper<Service extends keyof IRpc>(
serviceName: Service,
method: keyof IRpc[Service],
params: Object,
) {
return rpc[serviceName][method]({ ...params });
}
من همچنین سعی کردهام این کار را انجام دهم اما با خطا مواجه شدهام:
async rpcWrapper<Service extends keyof IRpc, Method extends keyof IRpc[Service]>(
serviceName: Service,
method: Method,
params: Parameters<Method>, // Type 'Method' does not satisfy the constraint '(...args: any) => any'.
) {
return rpc[serviceName][method]({ ...params });
}
رابط IRPC
interface IRpc {
ExampleService: ExampleService;
ExampleService2: ExampleService2;
ExampleService3: ExampleService3;
}
نوع یک ExampleService
export declare class ExampleService {
public Login(req: LoginReq): Promise<LoginResp>;
public Login(ctx: ClientInvokeOptions, req: LoginReq): Promise<LoginResp>;
public Logout(req: LogoutReq): Promise<CommonResp>;
public Logout(ctx: ClientInvokeOptions, req: LogoutReq): Promise<CommonResp>;
}
export interface LoginReq {
username: string;
email: string;
}
آنچه میخواهم
rpcWrapper("ExampleService", "Login", { })
// Autocomplete tells me that I can fill in username and email
پاسخ شماره ۱
تشخیص نوع درست گاهی دشوار است. در تابع شما نوع Method فقط کلید یک متد از یک سرویس است و نمیتوانید به پارامترهای یک PropertyKey دسترسی داشته باشید. به عنوان مثال Parameters<"Login">
async rpcWrapper<Service extends keyof IRpc, Method extends keyof IRpc[Service]>(
serviceName: Service,
method: Method,
// Method is just the method key of your function
params: Parameters<Method>, // Type 'Method' does not satisfy the constraint '(...args: any) => any'.
) {
return rpc[serviceName][method]({ ...params });
}
برای بهدست آوردن روش درست سرویس خود، باید به این روش به متد دسترسی پیدا کنید IRPc[Service][Method] //<- اما این باعث میشود ناشناخته باشد بنابراین شما باید به نحوی بررسی کنید که IRPc[Service][Method] یک تابع معتبر است. میتوانید این کار را با نوشتن یک نوع کمکی انجام دهید که بررسی کند آیا جنریک ارائه شده از هر تابعی پیروی میکند یا نه type CastFn<T>=T extends AnyFn ? T : never اکنون میتوانید پارامترهای متد خود را به این شکل دسترسی داشته باشید Parameters<CastFn<IRPc[Service][Method]>>.
interface IRpc {
ExampleService: ExampleService;
ExampleService2: ExampleService2;
}
interface LoginResp {
message: string
}
export declare class ExampleService {
public Login(input: { req: LoginReq, ctx?: ClientInvokeOptions }): Promise<LoginResp>;
public Logout(input: { req: LogoutReq, ctx?: ClientInvokeOptions }): Promise<CommonResp>;
}
export declare class ExampleService2 {
public Study(req: StudyReq): Promise<LoginResp>;
}
export interface StudyReq {
canStudy: boolean;
}
export interface LoginReq {
username: string;
email: string;
}
interface LogoutReq {
username: string;
}
interface CommonResp { }
interface ClientInvokeOptions { }
declare const rpc: IRpc;
type AnyFn = (...args: any[]) => any
type CastFn<T> = T extends AnyFn ? T : never
function rpcWrapper<
ServiceKey extends keyof IRpc,
MethodKey extends keyof IRpc[ServiceKey],
ClassMethod extends AnyFn = CastFn<IRpc[ServiceKey][MethodKey]>
>(
serviceName: ServiceKey,
method: MethodKey,
...params: Parameters<ClassMethod>
) {
return (rpc[serviceName][method] as ClassMethod)(...params);
}
type X = Parameters<IRpc["ExampleService"]["Login"]>
// Can put anything
rpcWrapper("ExampleService", "Login", { a: "a" }) // invalid
// I should be restricted to this
rpcWrapper("ExampleService", "Login", { req: { username: "haha", email: "[email " } }) //valid```
