繁体   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