2017-05-12 47 views
3

想要在訪問對象的屬性之前創建包含某個對象並執行空/未定義檢查的T>類<。 當然,應該輸入結果(可能是< null>或Maybe < TRes>)。使用keyof定義方法的返回類型

下面是一個例子:

class Maybe <T> { 
    constructor(public value: T) {} 
    static of <P> (obj: P): Maybe <P> { 
    return new Maybe(obj); 
    } 

    map <TResult> (fn: (arg: T) => TResult): Maybe <TResult> { 
    return this.isNothing ? nothing : new Maybe(fn(this.value)); 
    } 

    public get < P extends keyof T > (name: P): Maybe <T[P]> { 
    return this.isNothing ? nothing : Maybe.of(this.value[name]); 
    } 
    get isNothing(): boolean { 
    return this.value == null; 
    } 
} 
let nothing = new Maybe(null); 

Everethisng在這裏工作精細化和類型化。對於的exaple:

class Test { 
    a = { 
    id: 1, 
    name: "test1" 
    }; 
    f = (foo: string) => { 
    return new Test(); 
    }; 
}; 

let t = new Test(); 

console.log(Maybe.of(t).get('a').get('name').value); // ok 

但與定義「應用」功能的問題,將接受對象的屬性的名稱,實際上是一個函數,執行該功能,並返回也許< TResult>。

// T[fName]: (...args)=> TResult 
    public apply < P extends keyof T > (fnName: P, ...args: any[]) /*: Maybe<Tresult> */ { 
    if (!this.isNothing) { 
     let res = null; 
     let fn = this.value[fnName]; 

     if (isF(fn)) { 
     return Maybe.of(fn(...args)); 
     } 

    } 
    return nothing; 
    } 

無法找到定義「應用」調用結果的解決方案。

let fResult = Maybe.of(t).apply('f', 'foo'); 
// fResult is Maybe<any> , expected to be Maybe<Test> 

是否有人知道如何定義「應用」結果的類型?甚至在TS 2.3中這可能嗎?

這裏有相同的代碼在操場上的鏈接: TS playground

感謝

+0

我不清楚你的問題是什麼。在你的遊樂場代碼中,你忘記了聲明類型參數'Tresult'。沒關係,我誤解了,我看到了這個問題。 –

+1

我認爲這個問題可以縮小一點,但並不是很清楚你想要什麼。看起來你只是問在使用'keyof'時如何得到一個方法的返回類型。 – Rob

+0

我不確定這是不可能的,沒有一種檢測方式,並確保'f'確實是一個函數的名稱(以某種方式表達'P延伸keyof T,TR,T [P]擴展(... args:任何[])=> TR')。 – y2bd

回答

0

首先,讓我拋開Maybe<T>類,並介紹了一些基本操作,一步一個腳印的時間。

您可以使用泛型函數來檢查對象是否爲空,然後返回該對象的某些屬性。該函數的返回類型可以從物業的類型推斷:

function maybeGet<T, N extends keyof T>(t: T | undefined, n: N) { 
    return t ? t[n] : undefined; 
} 

Test

class Test { 
    a = { 
    id: 1, 
    name: "test1" 
    }; 
    f = (foo: string) => { 
    return new Test(); 
    }; 
}; 

let t = new Test(); 

,你可以使用它像這樣

const a = maybeGet(t, 'a'); 

乃至型的a被推斷爲{ id: number; name: string; }

然後,你可以定義泛型類型別名描述返回類型爲R一些功能:

type FR<R> = (...args: any[]) => R; 

,然後定義泛型函數,將採取另一種功能,並調用它,如果它不是空。該函數的返回類型從返回類型其參數的推斷:

function maybeApplyFunction<R>(f: FR<R> | undefined, ...args: any[]) { 
    return f ? f(...args) : undefined; 
} 

const r = maybeApplyFunction(t.f, 'foo'); // r has type 'Test' 

可以明確兩者結合在一起,不會有問題

const tf = maybeApplyFunction(maybeGet(t, 'f'), 'foo'); 
// tf has type `Test` 

的問題是在一個通用兩者結合操作。

使用映射類型,你可以嘗試定義類型別名具有函數返回R作爲使用屬性

type FM<N extends string, R> = {[n in N]: FR<R>}; 

寫泛型函數的對象

function maybeApplyMemberFunction<N extends string, R>(o: FM<N, R>, n: N, ...args: any[]) { 
    return o ? o[n](...args) : undefined; 
} 

,它會即使在某些情況下工作

class T1 { f() { return new T1() } }; 

const b = maybeApplyMemberFunction(new T1(), 'f'); 
// b has type T1 

但是,它會n爲您的測試OT的工作,因爲打字稿由於某種原因,將使用keyof TN,並堅持認爲,Test所有屬性必須是可調用:

const tm = maybeApplyMemberFunction(t, 'f'); 

// Argument of type 'Test' is not assignable to parameter of type 
// 'FM<"a" | "f", Test>'. 
    // Types of property 'a' are incompatible. 
    // Type '{ id: number; name: string; }' is not assignable to type 'FR<Test>'. 
     // Type '{ id: number; name: string; }' provides no match for the signature 
     // '(...args: any[]): Test'. 

code in playground

如果限制N苛求文本類型它的工作原理:

function maybeApplyMemberFunctionF<N extends 'f', R>(o: FM<N, R>, n: N, ...args: any[]) { 
    return o ? o[n](args) : undefined; 
} 
const t1 = maybeApplyMemberFunctionF(t, 'f'); 

不幸的是打字稿沒有辦法指定N應該是0的精確文字類型的說法。

相關問題