如何输入具有动态键和每个键具有不同值类型的对象?

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

How to type an object with dynamic keys and different value types for each key?

问题

I want to create a type for an object that allows me to use different keys depending on passed generic:

const withPrefix: User<"new"> = {
    new__firstname: "Tom",
    new__lastname: "Jennys",
    new__age: 15,
};

const withoutPrefix: User<undefined> = {
    firstname: "Tom",
    lastname: "Jennys",
    age: 15,
};

I created a utility type that returns the correct key depending on if there is a prefix or not:

type GetFullKey<
    TPrefix extends string | undefined,
    TKey extends string
> = TPrefix extends string ? `${TPrefix}__${TKey}` : TKey;

Then I use it like this to create a User object type:

export type User<TPrefix extends string | undefined> = {
    [key in GetFullKey<
        TPrefix,
        "firstname" | "lastname"
    >]: string;
    [key in GetFullKey<
        TPrefix,
        "age"
    >]: number;
};

It all works fine if I type each value as a string but when I want to type "age" as a number there is an error:

"A mapped type may not declare properties or methods."

How do I fix it?

英文:

I want to create a type for an object that allows me to use different keys depending on passed generic:

const withPrefix: User&lt;&quot;new&quot;&gt; = {
    new__firstname: &quot;Tom&quot;,
    new__lastname: &quot;Jennys&quot;,
    new__age: 15,
};

const withoutPrefix: User&lt;undefined&gt; = {
    firstname: &quot;Tom&quot;,
    lastname: &quot;Jennys&quot;,
    age: 15,
};

I created a utility type that returns the correct key depending on if there is a prefix or not:

type GetFullKey&lt;
    TPrefix extends string | undefined,
    TKey extends string
&gt; = TPrefix extends string ? `${TPrefix}__${TKey}` : TKey;

Then I use it like this to create a User object type:

export type User&lt;TPrefix extends string | undefined&gt; = {
    [key in GetFullKey&lt;
        TPrefix,
        &quot;firstname&quot; | &quot;lastname&quot;
    &gt;]: string;
    [key in GetFullKey&lt;
        TPrefix,
        &quot;age&quot;
    &gt;]: number;
};

It all works fine if I type each value as a string but when I want to type "age" as a number there is an error:

&quot;A mapped type may not declare properties or methods.&quot;

How do I fix it?

答案1

得分: 3

你不能在同一映射类型中拥有多个映射。你可以将其重新定义为一个具有复杂条件类型的单个映射类型,或者作为两个映射类型的交叉类型,但整个类型定义感觉有点奇怪。

像这样的方式更符合惯用法:

type User = {
   firstname: string;
   lastname: string;
   age: number;
};

type Prefixed<T, P extends string | undefined = undefined> = {
   [K in Extract<keyof T, string> as P extends string ? `${P}__${K}` : K]: T[K]
};

const withPrefix: Prefixed<User, 'new'> = {
    new__firstname: "Tom",
    new__lastname: "Jennys",
    new__age: 15,
};

const withoutPrefix: Prefixed<User> = {
    firstname: "Tom",
    lastname: "Jennys",
    age: 15,
};

Playground链接

英文:

You can't have multiple mappings in the same mapped type. You could redefine it as a single mapped type with a complex conditional type, or as an intersection of two mapped types, but the entire type definition feels a little strange.

Something like this feels more idiomatic:

type User = {
   firstname: string;
   lastname: string;
   age: number;
};

type Prefixed&lt;T, P extends string | undefined = undefined&gt; = {
   [K in Extract&lt;keyof T, string&gt; as P extends string ? `${P}__${K}` : K]: T[K]
};

const withPrefix: Prefixed&lt;User, &#39;new&#39;&gt; = {
    new__firstname: &quot;Tom&quot;,
    new__lastname: &quot;Jennys&quot;,
    new__age: 15,
};

const withoutPrefix: Prefixed&lt;User&gt; = {
    firstname: &quot;Tom&quot;,
    lastname: &quot;Jennys&quot;,
    age: 15,
};

<sup>Playground link</sup>

huangapple
  • 本文由 发表于 2023年4月13日 16:46:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76003442.html
匿名

发表评论

匿名网友

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

确定