推断 TypeScript 中 Object.fromEntries() 结果的形状 [英] Infer shape of result of Object.fromEntries() in TypeScript

查看:78
本文介绍了推断 TypeScript 中 Object.fromEntries() 结果的形状的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为 Object.fromEntries() 编写一个更好的定义,以推断结果对象的实际键和值.我有我认为可行的方法,但显然 infer U 的范围是我的方法无法找到 U.

I'm trying to write a better definition for Object.fromEntries() that infers the actual keys and values of the resulting object. I have what I thought would work, but apparently infer U is scoped in a way that my method can't find U.

interface ObjectConstructor {
  fromEntries<
    K extends PropertyKey,
    V,
    A extends ReadonlyArray<readonly [K, V]> = ReadonlyArray<readonly [K, V]>
  >(array: A): {
    [key in A[number][0]]: A[number extends infer U ? U : never][0] extends key ? A[U][1] : never;
  };
}

A[U][1] 中的 U 不可访问.

找不到名字U".

另一个问题是 A[number extends infer U ?U : never][0] extends key 似乎总是评估为 false.

Another issue is that A[number extends infer U ? U : never][0] extends key seems to always evaluate to false.

如何检索与 key 关联的数组的索引?我猜有一些 hacky 类型将数组包装在一个函数中并检查该函数是否扩展了可以推断索引的东西,但我的最佳尝试没有奏效.

How can I retrieve the index of the array associated with key? I'm guessing there's some hacky type that wraps the array in a function and checks to see if that function extends something that can infer the index, but my best attempt didn't work.

type ArrayIdxOf<A extends readonly unknown[], K> =
  (((i: keyof A) => A[typeof i]) extends ((i: number & infer U) => A[typeof i] extends K ? typeof i : never) ? (i: U) => U : never) extends 
    ((i: number) => infer U)
      ? U
      : never;

const a1 = ['foo', 'bar'] as const;
type A = ArrayIdxOf<typeof a1, 'foo'>; // type A = unknown

interface ObjectConstructor {
  fromEntries<
    K extends PropertyKey,
    V,
    A extends ReadonlyArray<readonly [K, V]> | Array<[K, V]>
  >(array: A): {
    [key in A[number][0]]: ArrayIdxOf<A, key> extends infer I ? I extends number ? A[I][1] : never : never;
  };
}

const a2 = [['foo', 'foo'], ['bar', false], [1, 2]] as const;
const x = Object.fromEntries(a2);
/**
 * const x: {
 *   foo: never;
 *   bar: never;
 *   1: never;
 * }
 */

如果可能,我还想消除Areadonly约束.

If at all possible, I also want to eliminate the readonly constraints of A.

TS Playground 虽然它使用 tsc 编译,但它不会在 Playground 中编译,所以我不知道我有多大用处会的.

TS Playground though while this compiles using tsc, it doesn't compile in the playground, so I don't know how useful it will be.

推荐答案

我可能会做类似的事情

declare global {
  interface ObjectConstructor {
    fromEntries<
      A extends ReadonlyArray<readonly [PropertyKey, any]>
    >(array: A): { [K in A[number][0]]: Extract<A[number], readonly [K, any]>[1] }
  }
}

请注意,readonly 不是约束,而是松散... string[] 可分配给 readonly string[] 但反之亦然.通过接受 readonly 数组,你接受了更多的东西,而不是更少.所以我会保留它们(这很好,因为 as const 倾向于产生 readonly 的东西.

Note that readonly isn't a constraint, but a loosening... string[] is assignable to readonly string[] but not vice versa. By accepting readonly arrays you are accepting more things, not less. So I'd leave them on (which is good because as const tends to produce readonly things).

这将适用于您的示例:

const x = Object.fromEntries(b);
/* const x: {
    foo: "foo";
    bar: false;
    1: 2;
} */


如果你想给编译器一个提示,它应该尝试将每个元组的第一个元素读取为 literal 类型,你可以给它这个修改后的定义:


If you want to give the compiler a hint that it should try to read the first element of each tuple as a literal type, you can give it this modified definition:

declare global {
  interface ObjectConstructor {
    fromEntries<
      P extends PropertyKey,
      A extends ReadonlyArray<readonly [P, any]>
    >(array: A): { [K in A[number][0]]: Extract<A[number], readonly [K, any]>[1] }
  }
}

这在您的 和 const 版本上的工作方式相同,但如果您直接传递数组文字也可以:

This will work the same on your as const version, but will also work if you pass the array literal directly:

const c = Object.fromEntries([['foo', 'foo'], ['bar', false], [1, 2]]);
/* const c: {
    foo: string;
    bar: boolean;
    1: number;
} */


您可以稍微调整推断,但基本方法是使用 Extract 实用程序类型以从数组中提取适当的元组.


You can tweak the inferences a bit, but the basic approach is to use the Extract utility type to pull the appropriate tuple out of the array.

无论如何,希望能给你一些指导.祝你好运!

Anyway, hope that gives you some direction. Good luck!

游乐场连结代码

这篇关于推断 TypeScript 中 Object.fromEntries() 结果的形状的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆