[英]Class wrapping a function: get correct typings and access to number of arguments (in typescript)
我編寫了一個包含指定函數的小類,並為每個函數參數提供了一個驗證模式列表。 這個類有一個call
函數,它應該使用與輸入函數相同的參數,驗證它們,調用輸入函數,並將結果包裝到rxjs / Observable中。 因此,構造函數應檢查函數method.length
指定的參數數量是否與驗證模式的數量相同。
原始的javascript版本應該如下所示:
class Wrapper {
method;
validationPatterns;
constructor(method, validationPatterns) {
if (method.length !== validationPatterns.length) {
// throw error
}
this.method = method;
this.validationPatterns = validationPatterns;
}
validate(...args) {
this.validationPatterns.forEach(pattern => {
// apply validation pattern
});
}
call(...args) {
// validate arguments
this.validate(...args);
// run method and wrap its result e.g. in an Observable
const methodResult = this.method(...args);
return Observable.of(methodResult);
}
}
現在我搜索了很多東西並嘗試了不同的東西來將正確的類型應用到這個類。 但我找不到一種可以讓我這樣做的方法
method.length
)。 因此,無法使用false驗證構造類實例。 我嘗試了以下事項:
<T extends Function>
優點:
缺點:
T
提取參數類型和返回類型,並將它們應用於validate()
和call()
。 他們仍然必須使用any
/ any[]
類型。 代碼示例:
class Wrapper<F extends Function> {
constructor(public method: F, public validationPatterns: any[]) {
if (method.length !== validationPatterns.length) {
// throw error
}
}
validate(...args: any[]): void {
// ...
}
call(...args: any[]): any {
// ...
}
}
優點:
缺點:
[a, b]
這需要額外的語法(例如括號[a, b]
)。 但這應該是可以接受的。 method.length == 1
。 因此,為了完整打字,我們必須承認功能缺點,這對我來說聽起來不太好。 代碼示例:
class Wrapper<T, U> {
constructor(public method: (arg: T) => U, public validationPatterns: any[]) {
// CANNOT check correct number of validation patterns
}
validate(args: T): void {
// ...
}
call(args: T): Observable<U> {
// ...
}
}
由於包裝函數通常只有一些參數,在另一種方法中,我們可以嘗試使用函數重載。 但是,到目前為止,我只找到了有關重載單個函數的信息。 我想,我真正想要的是一個重載類,其中call()
和validate()
的簽名取決於構造函數重載。
我沒有使用打字稿進行重載的經驗,但我想具有獨立重載函數的代碼示例可能如下所示:
class Wrapper<F extends Function, T1, T2, T3, U> {
constructor(method: (arg1: T1, arg2: T2, arg3: T3) => U, validationPatterns: any[]);
constructor(method: (arg1: T1, arg2: T2) => U, validationPatterns: any[]);
constructor(method: (arg1: T1) => U, validationPatterns: any[]);
constructor(method: () => U, validationPatterns: any[]);
constructor(public method: F, public validationPatterns: any[]) {
// ...
}
validate(arg1: T1, arg2: T2, arg3: T3): void;
validate(arg1: T1, arg2: T2): void;
validate(arg1: T1): void;
validate(): void;
validate(...args: any[]): void {
// ...
}
call(arg1: T1, arg2: T2, arg3: T3): Observable<U>;
call(arg1: T1, arg2: T2): Observable<U>;
call(arg1: T1): Observable<U>;
call(): Observable<U>;
call(...args: any[]): Observable<U> {
// ...
}
}
<T extends Function>
),它具有完整功能但缺少類型。 謝謝!
你可以重載類,但它有點復雜,你需要單獨聲明重載並為每個重載定義一個構造函數:
class WrapperImpl {
constructor(public method: Function, public validationPatterns?: Function[]) {
// ...
}
validate(...args: any[]): void {
// ...
}
call(...args: any[]): Observable<any> {
return new Observable<any>();
}
}
type KeysOfUnion<T> = T extends any ? keyof T: never;
type IsValid<T, TResult> = KeysOfUnion<T> extends never ? never : TResult;
const Wrapper: {
new <U> (m: ()=> U, ) : {
validate(): void
call(): Observable<U>
}
new <T1, U> (m: (a: T1)=> U, validationPatterns: [IsValid<T1, (a: T1)=>boolean>]) : {
validate(a: T1): void
call(a: T1): Observable<U>
}
new <T2, T1, U> (m: (a: T1, a2: T2)=> U, validationPatterns: [IsValid<T1, (a: T1)=>boolean>, IsValid<T2, (a: T2)=>boolean>]) : {
validate(a: T1, a2: T2): void
call(a: T1, a2: T2): Observable<U>
}
// Add more as needed
} = WrapperImpl
let w = new Wrapper(() => "");
w.call() // return Observable<string>
let w2 = new Wrapper((n: number) => "", [n=> true]);
w2.call(10) // return Observable<string>
在實現中我不會過多地使用泛型,你需要使用非常通用的類型(即Function
和any
)泛型對你沒有多大幫助。 調用將被正確輸入和檢查,這是重要的部分。
我假設驗證是函數,你也可以得到推理和類型安全。 我不得不使用一些條件類型魔法來讓編譯器為每個參數數量選擇正確的重載,但它似乎運行良好。 如果您有任何問題,請告訴我。
當與具有可選參數的函數一起使用時,這種方法仍然至少有一個缺點,因為需要可選參數:
function withOpt(n?: number) { return ""}
let w3 = new Wrapper(withOpt, [n=> true]);
w3.call(1) // 1 is required.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.