将动态对象映射到打字稿函数参数
Mapping a dynamic object to typescript function params
我正在尝试制作一个列表(下面的示例)并将其映射到函数的参数。
我想象参数是一个对象,其键与参数名称匹配,值是每个参数的选项(无论是必需的还是更重要的,什么类型)。
interface Command<T> { // Theese are the parts that I'm curious about,
args: T, // idk if its even possible, or if there's another solution.
run(...args: T) { //
}
}
const Arguments = {
name: {
type: "string",
required: true,
},
duration: {
type: "number",
required: false,
}
};
const cmd: Command<typeof Arguments> = {
args: Arguments,
run(name, duration) {
console.log(`banned ${name} for ${duration ?? "forever"}`);
}
}
如果可能或者您对如何实现这一点有任何想法,请告诉我,因为我已经为此苦苦挣扎了好几天。
由于您想按原样推断对象的类型,我们将使用 as const
断言。
然后我们需要创建一个 ObjectToParams<T>
类型别名,它允许我们进行此转换并为输出对象实现所需的约束格式 assignable
const Arguments = {
name: {
type: "string",
required: true,
},
duration: {
type: "number",
required: false,
}
} as const;
type ObjectToParams<T extends { [k: string]: { type: string } }>
= { args: T, run: (args: { [k in keyof T]: T[k]["type"] }) => void }
const cmd: ObjectToParams<typeof Arguments> = {
args: Arguments, // Get inference for Arguments
run({name, duration}) {
console.log(`banned ${name} for ${duration ?? "forever"}`);
}
}
你需要更多的魔法才能做到这一点。首先是描述参数对象类型的类型:
type ArgsObject = Record<string, { type: "string" | "number" | "boolean"; required: boolean }>;
然后我们将字符串映射到它们的实际类型(即“字符串”到 string
):
type MapToType = {
string: string;
number: number;
boolean: boolean;
// more if needed
};
最后我们映射参数并获取每个参数的类型:
type ArgsToContext<Args extends ArgsObject> = {
[K in keyof Args]: MapToType[Args[K]["type"]] | IsOptional<Args[K]>;
};
其中 IsOptional
定义为:
type IsOptional<T extends ArgsObject[string]> = T["required"] extends true ? never : undefined;
这是因为如果需要,它给我们 never
,而 Type | never
总是 Type
。但是,如果不需要(可选),它会为我们提供 undefined
,从而得到所需的 Type | undefined
这里 playground 展示了这一点。
我正在尝试制作一个列表(下面的示例)并将其映射到函数的参数。 我想象参数是一个对象,其键与参数名称匹配,值是每个参数的选项(无论是必需的还是更重要的,什么类型)。
interface Command<T> { // Theese are the parts that I'm curious about,
args: T, // idk if its even possible, or if there's another solution.
run(...args: T) { //
}
}
const Arguments = {
name: {
type: "string",
required: true,
},
duration: {
type: "number",
required: false,
}
};
const cmd: Command<typeof Arguments> = {
args: Arguments,
run(name, duration) {
console.log(`banned ${name} for ${duration ?? "forever"}`);
}
}
如果可能或者您对如何实现这一点有任何想法,请告诉我,因为我已经为此苦苦挣扎了好几天。
由于您想按原样推断对象的类型,我们将使用 as const
断言。
然后我们需要创建一个 ObjectToParams<T>
类型别名,它允许我们进行此转换并为输出对象实现所需的约束格式 assignable
const Arguments = {
name: {
type: "string",
required: true,
},
duration: {
type: "number",
required: false,
}
} as const;
type ObjectToParams<T extends { [k: string]: { type: string } }>
= { args: T, run: (args: { [k in keyof T]: T[k]["type"] }) => void }
const cmd: ObjectToParams<typeof Arguments> = {
args: Arguments, // Get inference for Arguments
run({name, duration}) {
console.log(`banned ${name} for ${duration ?? "forever"}`);
}
}
你需要更多的魔法才能做到这一点。首先是描述参数对象类型的类型:
type ArgsObject = Record<string, { type: "string" | "number" | "boolean"; required: boolean }>;
然后我们将字符串映射到它们的实际类型(即“字符串”到 string
):
type MapToType = {
string: string;
number: number;
boolean: boolean;
// more if needed
};
最后我们映射参数并获取每个参数的类型:
type ArgsToContext<Args extends ArgsObject> = {
[K in keyof Args]: MapToType[Args[K]["type"]] | IsOptional<Args[K]>;
};
其中 IsOptional
定义为:
type IsOptional<T extends ArgsObject[string]> = T["required"] extends true ? never : undefined;
这是因为如果需要,它给我们 never
,而 Type | never
总是 Type
。但是,如果不需要(可选),它会为我们提供 undefined
,从而得到所需的 Type | undefined
这里 playground 展示了这一点。