簡體   English   中英

在打字稿中使用擴展類型進行類型推斷

[英]Type inference using extended type in typescript

我有一個工廠,它應該根據提供的枚舉鍵返回一個函數。 這是在 switch case 中完成的,那里的所有函數都有不同的有效載荷。

我試圖實現的目標是為該函數提供這樣的類型,它會隱式驗證有效負載的所有類型和工廠的返回類型。 請參閱下面的示例:

// the list of keys that are used for creation
enum keys {
    key1 = 1,
    key2 = 2
}
// here are interfaces, that tie each key with payload, that should be provided.
// Actually, payload is way more complex (another interfaces) but let's keep it simple
interface Test1 {
    key: keys.key1;
    payload: string;
}
interface Test2 {
    key: keys.key2;
    payload: number;
}

// this is the type, that comes to the function below
type tests = Test1 | Test2;


interface ReturnTypeOfTest1 { returnedObject1: number; }
interface ReturnTypeOfTest2 { returnedObject2: string; }
// this is how I'm configuring what should be returned depending on the key 
// the return type is set to "ResourceModel", which infers a key from a function payload
// and identifies what exactly it should return
interface ModelOfTest {
    [keys.key1]: ReturnTypeOfTest1;
    [keys.key2]: ReturnTypeOfTest2;
}
type ResourceModel<R extends keys> = ModelOfTest[R];


ResourceModel類型是基於另一個 stackoverflow 問題Typescript: Return type of function based on input value (enum) 創建的

使用上面的類型,我可以驗證有效負載的類型,但不驗證返回類型:


function getTest(t: tests): any{
    switch (t.key) {
        case keys.key1: {
            const data = t.payload; // is string
            return {returnedObject1: 123 };
        } case keys.key2: {
            const data = t.payload; // is number
            return {returnedObject2: '123' };

        }
    }
}
getTest({key: keys.key1, payload: '1' }); // ReturnTypeOfTest1 | ReturnTypeOfTest2

或者獲得正確的返回類型,但在 switch case 中丟失驗證:

function getTest<T extends tests>(t: T): ResourceModel<T['key']> {
    switch (t.key) {
        case keys.key1: {
            const d = t.payload; // string | number
            return {returnedObject1: 123}; // also an error here, because it wants me to return both ReturnTypeOfTest1 & ReturnTypeOfTest2
        } case keys.key2: {
            const d = t.payload; // string | number
            return null;

        }
    }
}
 getTest({key: keys.key2, payload: 1 }); // ReturnTypeOfTest2

有沒有辦法正確輸入這個東西? 真的很感激這方面的任何幫助。

這本質上是 TypeScript 中缺少的功能(請參閱microsoft/TypeScript#24085 ); 編譯器無法很好地推理未指定的泛型類型參數,例如TgetTest()的實現中。 當您調用getTest() ,指定T ,然后正確計算返回類型。 但是當你實現getTest()T仍然是一個未指定/未解析的類型參數,編譯器基本上放棄了。 具體來說,它不會嘗試使用基於控制流的類型分析來縮小T extends Test1 | Test2 T extends Test1 | Test2Test1Test2基於t.key的值。 這意味着目前,像這樣的泛型類型簽名對函數調用者比對函數實現者更有用。

也許有一天會在語言中解決這個問題。 目前,有一些解決方法。 在這種情況下,我最常使用的方法是向函數添加單個重載簽名 讓調用者看到對他們有用的通用重載簽名,而實現看到對它有用的非通用聯合類型簽名:

// call signature, seen by callers
function getTest<T extends tests>(t: T): ResourceModel<T['key']>;
// implementation signature, seen only by implementation
function getTest(t: tests): ResourceModel<keys> {
  switch (t.key) {
    case keys.key1: {
      const d = t.payload; // string 
      const ret: ResourceModel<keys.key1> = { returnedObject1: 123 };
      return ret;
    } case keys.key2: {
      const d = t.payload; // number
      const ret: ResourceModel<keys.key2> = { returnedObject2: '123' };
      return ret;
    }
  }
}

這仍然不是真正的類型安全; 如果您切換return語句,以便ReturnTypeOfTest1Test2返回,反之亦然,則上述內容不會抱怨。 但這已經接近我們所能做到的最好了,至少目前是這樣,因此您在實施時需要小心。

讓我們確保調用按預期工作:

getTest({ key: keys.key1, payload: "1" }); // ReturnTypeOfTest1
getTest({ key: keys.key2, payload: 1 }); // ReturnTypeOfTest2

看起來挺好的。 無論如何,我希望這會有所幫助; 祝你好運!

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM