[英]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 ); 編譯器無法很好地推理未指定的泛型類型參數,例如T
在getTest()
的實現中。 當您調用getTest()
,指定T
,然后正確計算返回類型。 但是當你實現getTest()
, T
仍然是一個未指定/未解析的類型參數,編譯器基本上放棄了。 具體來說,它不會嘗試使用基於控制流的類型分析來縮小T extends Test1 | Test2
T extends Test1 | Test2
到Test1
或Test2
基於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
語句,以便ReturnTypeOfTest1
為Test2
返回,反之亦然,則上述內容不會抱怨。 但這已經接近我們所能做到的最好了,至少目前是這樣,因此您在實施時需要小心。
讓我們確保調用按預期工作:
getTest({ key: keys.key1, payload: "1" }); // ReturnTypeOfTest1
getTest({ key: keys.key2, payload: 1 }); // ReturnTypeOfTest2
看起來挺好的。 無論如何,我希望這會有所幫助; 祝你好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.