1

我有一个函数可以将字典转换为具有和属性dictToKv的对象数组:keyvalue

/** used to help get type literals versus wide types */
type Narrowable = string | number | boolean | symbol | object | undefined | void | null | {};

/** just like Object.keys() but preserves keyof relationship */
function keys<T extends {}>(obj: T) { Object.keys(obj) as unknown as Array<keyof T> };

/** Helps shape an object into an array where key and value relationship is retained */
type KeyValueFrom<T extends object> = Array<{ [K in keyof T]: { key: K; value: T[K] } }[keyof T]>;

/** convert a dictionary to an array of objects with key and value props */
function dictToKv<N extends Narrowable, T extends Record<string, N>>(obj: T): KeyValueFrom<T> {
  return keys(obj).map(k => {
    return { key: k, value: obj[k] };
  });
}

在我的实用程序的帮助下,我KeyValueFrom<T>可以显式声明返回的类型,并且这个返回值是不可迭代的,但会保留所有类型的文字信息:

const obj = { id: 123, foo: "bar" } as const;
const kv1 = dictToKv(obj);

for(const kv of kv1) {
  if(kv.key === "id") {
    // strong types with no unions
    type cases = [
      Expect<Equal<typeof kv.key, "id">>,
      Expect<Equal<typeof kv.value, 123>>
    ]
  }
}

我遇到的问题是相反的。逆向的运行时方面很简单,但困难的部分似乎是创建一个类型实用程序来执行这种逆向转换。

到目前为止,我得到的是:

type DictFrom<T extends { key: string; value: unknown }[]> = 
   Record<T[number]["key"], T[number]["value"]>;

这确实正确键入了字典的键并维护了文字类型,但值现在是联合类型:

type Inverse = DictFrom<typeof kv1>;
type cases = [
  // Expect<Equal<Inverse, typeof obj>>
  Expect<Equal<Inverse, { foo: 123 | "bar"; id: 123 | "bar" }>>
];

有没有办法使用 Typescript 的推理来实现非联合类型?

打字稿游乐场

4

1 回答 1

1

这可以通过映射类型中的键重新映射相当简单地完成,如下所示:

type DictFrom<T extends { key: string; value: unknown }[]> = 
  { [R in T[number] as R["key"]]: R["value"] };

使用你KeyValueFrom来做到这一点:

interface Foo {
  id: number,
  foo: string
}

type FooKV = KeyValueFrom<Foo>;
/* type FooKV = ({
    key: "id";
    value: number;
} | {
    key: "foo";
    value: string;
})[] */

我们可以或多或少地撤消它DictFrom

type FooDict = DictFrom<FooKV>
/* type FooDict = {
    id: number;
    foo: string;
} */

这些在您的测试中是否被视为完全相等与诸如readonly或可选性和其他可能导致DictFrom<KeyValueFrom<T>>T.

Playground 代码链接

于 2021-07-27T20:44:49.643 回答