英文:
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<"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?
答案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,
};
英文:
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<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,
};
<sup>Playground link</sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论