键入强大的实用程序以将 KV 对数组转换为原始字典
Type strong utility to convert array of KV pairs to original dictionary
我有一个函数 dictToKv
可以将字典转换为具有 key
和 value
属性的对象数组:
/** 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>
实用程序,我可以明确说明 return 的类型并且此 return 值不可迭代但保留所有类型文字信息:
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 的推理来实现非联合类型吗?
这可以用 key remapping in mapped types 相当直接地完成,像这样:
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
不同的怪癖有关.
我有一个函数 dictToKv
可以将字典转换为具有 key
和 value
属性的对象数组:
/** 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>
实用程序,我可以明确说明 return 的类型并且此 return 值不可迭代但保留所有类型文字信息:
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 的推理来实现非联合类型吗?
这可以用 key remapping in mapped types 相当直接地完成,像这样:
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
不同的怪癖有关.