簡體   English   中英

TypeScript 鍵入 Object Output 與輸入相同的鍵

[英]TypeScript Typed Object Output With Same Keys as Input

我正在嘗試編寫一個 function 來解決 object 的所有 Promise 值:

const resolveObject = async (obj) => // pure JavaScript implementation
  Object.fromEntries( // turn the array of tuples with key-value pairs back into an object
    (await Promise.all(Object.values(obj))).map((el, i) => [ // Promise.all() the values, then create key-value pair tuple
      Object.keys(obj)[i], // tuple contains the original object key
      el, // tuple contains the resolved object value
    ]) // zips the split key/value arrays back into one object
  );

實施工作正常。 下面是一個使用示例:

/* example input */
const exampleInput = { // <- { foo: Promise<string>, bar: Promise<{ test: number }>, baz: boolean }
    foo: Promise.resolve("Hello World"),
    bar: Promise.resolve({
        test: 1234
    }),
    baz: false
}
const expectedOutput = resolveObject(exampleInput) // <- { foo: string, bar: { test: number }, baz: boolean }
/* expected output: strongly typed object with same keys and no Promise<> wrappers in signature */

這就是事情開始分崩離析的地方。 I'm expecting a strongly typed output with a similar signature as the input (just without the Promise wrappers), but instead, I get the following generic object output:

Promise<{ [k: string]: unknown; }>

所以我開始給resolveObject function添加類型注解:

const resolveObject = async <T>(
  obj: { [K in keyof T]: Promise<T[K]> }
): Promise<{ [K in keyof T]: T[K] }> => { ... }

現在我收到一個類型轉換錯誤: type '{ [k: string]: unknown; }' is not assignable to type '{ [K in keyof T]: T[K]; }' type '{ [k: string]: unknown; }' is not assignable to type '{ [K in keyof T]: T[K]; }'
我對 TypeScript 還很陌生,我真的不知道下一步該做什么(我知道如何診斷錯誤消息,但我很確定我的類型注釋/函數簽名有問題)。 我怎樣才能實現我正在尋找的東西?

如果您希望resolveObject()的 output 的類型取決於其輸入的類型,則需要給它一個通用的 function 簽名; TypeScript 中沒有任何工具可以推斷出 function 具有這樣的通用簽名。

預期的簽名是這樣的:

/* const resolveObject: <T extends object>(
  obj: { [K in keyof T]: T[K] | Promise<T[K]>; }
) => Promise<T> */

這意味着 output 類型是Promise<T>對於由輸入obj確定的某些泛型類型T 特別是, obj是一個映射類型,其中T在鍵K的每個屬性,即T[K] ,要么單獨存在( T[K] ),要么( 聯合| )包裝在PromisePromise<T[K]> )。 事實證明,編譯器能夠通過映射類型的推斷從obj的類型中找出T


不幸的是,編譯器能夠遵循您獲得的特定實現以驗證返回值是否符合類型簽名的希望並不大。 The typings for Object.fromEntries() and Object.keys() are not specific enough to infer what you want, and even if it could, the correlation between the index i of Object.keys() and Object.values() is not容易表示。 與其試圖弄清楚如何讓編譯器理解實現是類型安全的,不如非常小心地確保我們做得對,然后斷言我們已經這樣做了:

const resolveObject = async <T extends object>(
  obj: { [K in keyof T]: Promise<T[K]> | T[K] }
) =>
  Object.fromEntries(
    (await Promise.all(Object.values(obj))).map((el, i) => [
      Object.keys(obj)[i],
      el,
    ])
  ) as T; // assertion here

我們已經斷言Object.fromEntries(...)導致T類型的值。 如果我們犯了錯誤,那么編譯器不會注意到; 類型斷言將維護類型安全的負擔從編譯器轉移到了開發人員身上。 但是在這種情況下,我們在resolveObject()的實現中只有一個類型斷言,調用者不需要擔心它:

const exampleInput = {
  foo: Promise.resolve("Hello World"),
  bar: Promise.resolve({
    test: 1234
  }),
  baz: false
}
const expectedOutput = resolveObject(exampleInput);
/* const expectedOutput: Promise<{
    foo: string;
    bar: {
        test: number;
    };
    baz: boolean;
}> */

您可以看到expectedOutput的類型已被編譯器推斷為您所期望的:具有已知鍵和屬性類型的 object 類型的Promise<>

expectedOutput.then(x => console.log(x.bar.test.toFixed(2))) // 1234.00

看起來不錯。

Playground 代碼鏈接

暫無
暫無

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

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