从对象数组中推导对象类型

huangapple go评论57阅读模式
英文:

Deriving object type from array of objects

问题

给定这个类型:

type ItemMetadata = {
    name: string;
    valueType: "string" | "number" | "boolean";
};

我想构建一个参数数组:

const MyMetaDatas = [
    {
        name: "The String",
        valueType: "string"
    },
    {
        name: "The Number",
        valueType: "number"
    }
] as const satisfies readonly ItemMetadata[];

并且给定这个只读的 MyMetaDatas 数组,我想构建一个对象,如下所示:

const MyParameters = {
   "The String": "string",
   "The Number": "number"
}

我想将这个对象类型命名为一个带有一个参数的泛型,如 ParametersFor<T extends readonly ItemMetadata[]> = { ...TODO }

尝试 #1

/**
 * 试图从 ItemMetadata 的 const 数组生成 { name: valueType } 映射对象
 */
type ItemParameters<T extends readonly ItemMetadata[]> = {
    [K in T[number]["name"]]: T[number]["valueType"];
};
/**
 * 不太好,因为 T[number]["valueType"] 查看数组中的所有项,它会生成所有选项的 OR 类型...
type MyParams = {
  "The String": "string" | "number" | "boolean";
  "The Number": "string" | "number" | "boolean";
}
 */
type MyParams = ItemParameters<typeof MyMetaDatas>;

这确实正确地生成了对象的左侧,但右侧的 T[number]["valueType"] 查看了数组中的所有项,它会生成所有选项的 OR 类型。

尝试 #2

尝试了多种方法,得到了这个结果。这不会生成一个对象,而是生成一个包含一个键(name)和一个值(valueType)的元组。

type ParamsUsingSingle<T extends readonly ItemMetadata[]> = {
    [K in keyof T]: {
        [TName in T[K]["name"]]: T[K]["valueType"];
    }
};
type MyParamsUsingSingle = ParamsUsingSingle<typeof MyMetaDatas>;
/**
 * 接近,但现在我需要以某种方式将它们合并... 或者扁平化
 */
const x: MyParamsUsingSingle = [
    {
        "The String": "string"
    },
    {
        "The Number": "number",
    }
]

但现在我需要以某种方式将它们合并...

你能帮我解决这个任务吗?

英文:

Given this type:

type ItemMetadata = {
    name: string;
    valueType: &quot;string&quot; | &quot;number&quot; | &quot;boolean&quot;;
};

I want to construct an array of parameters

const MyMetaDatas = [
    {
        name: &quot;The String&quot;,
        valueType: &quot;string&quot;
    },
    {
        name: &quot;The Number&quot;,
        valueType: &quot;number&quot;
    }
] as const satisfies readonly ItemMetadata[];

And given this - readonly MyMetaDatas array, I would like to consturct an object which looks like this

const MyParameters = {
   &quot;The String&quot;: &quot;string&quot;,
   &quot;The Number&quot;: &quot;number&quot;
}

And I want to "type" this object type as a generic with 1 parameter - typeof MyMetadatas - e.g. ParametersFor&lt;T extends readonly ItemMetadata[]&gt; = { ...TODO }

Attempt #1

/**
 * Attempt on generating { name: valueType } map object, from const array of ItemMetadata
 */
type ItemParameters&lt;T extends readonly ItemMetadata[]&gt; = {
    [K in T[number][&quot;name&quot;]]: T[number][&quot;valueType&quot;];
};
/**
 * Not nice because T[number][&quot;valueType&quot;] looks at all of the items in the array
type MyParams = {
  &quot;The String&quot;: &quot;string&quot; | &quot;number&quot; | &quot;boolean&quot;;
  &quot;The Number&quot;: &quot;string&quot; | &quot;number&quot; | &quot;boolean&quot;;
}
 */
type MyParams = ItemParameters&lt;typeof MyMetaDatas&gt;;

This does procudce the left side of the object correctly, but T[number] at the right side looks at the all of the items in the array, it produces OR types of all of the options...

Attempt #2
Tried multiple things, came up with this. This does not produce object, it produces a tuple of ojbects, where there is only one key - [name], and one value - [valueType].

type ParamsUsingSingle&lt;T extends readonly ItemMetadata[]&gt; = {
    [K in keyof T]: {
        [TName in T[K][&quot;name&quot;]]: T[K][&quot;valueType&quot;];
    }
};
type MyParamsUsingSingle = ParamsUsingSingle&lt;typeof MyMetaDatas&gt;;
/**
 * Close but now I would have to merge them into a signle object somehow.. Or to Flatten
 */
const x: MyParamsUsingSingle = [
    {
        &quot;The String&quot;: &quot;string&quot;
    },
    {
        &quot;The Number&quot;: &quot;number&quot;,
    }
]

But now I need to merge them somehow...

Can anyone help me with this task?

Here are my numberous attempts in Typescript playground

答案1

得分: 1

你在你的第一次尝试中方向是正确的,但是你不应该只写 T[number]['valueType'],因为它会给出所有可能性的联合类型。相反,你应该使用key remapping

type GetParameters<T extends readonly ItemMetadata[]> = {
  [K in T[number] as K["name"]]: K["valueType"];
};

用法:

// type Params = {
//     "The String": "string";
//     "The Number": "number";
// }
type Params = GetParameters<typeof MyMetaDatas>

playground

英文:

You are in the right direction with your first attempt, however, you shouldn't just write T[number][&#39;valueType&#39;], since it will give a union of all possibilities. Instead, you should use key remapping:

type GetParameters&lt;T extends readonly ItemMetadata[]&gt; = {
  [K in T[number] as K[&quot;name&quot;]]: K[&quot;valueType&quot;];
};

Usage:

// type Params = {
//     &quot;The String&quot;: &quot;string&quot;;
//     &quot;The Number&quot;: &quot;number&quot;;
// }
type Params  = GetParameters&lt;typeof MyMetaDatas&gt;

playground

huangapple
  • 本文由 发表于 2023年5月25日 19:51:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76331956.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定