I want to write a TypeScript type that "reduces" an input array of objects to a record object, with the object keys taken from a property of the input array members.
This will be used for const inputs and therefore the resulting object should have correct types for each member.
This is my current attempt, but the ExtractName<Args[N]>
is not working.
How can I solve this?
type TypedArguments<Args extends readonly ArgumentSpec[]> = {
// what I would like is to output an object type with
// fixed string keys taken from each member of the (const) input
readonly [N in keyof Args as ExtractName<Args[N]>]: ExtractValue<Args[N]>
// this works but output is an array
// readonly [N in keyof Args]: ExtractValue<Args[N]>
};
type ExtractName<
T extends ArgumentSpec
> = T['name']
type ExtractValue<
T extends ArgumentSpec
> =
T['multiple'] extends true ? string[] : string
export interface ArgumentSpec {
name: string
multiple: boolean
}
function mapArguments<Args extends readonly ArgumentSpec[]>(args: Args): TypedArguments<Args> {
// content here is not important, this would do some parsing of
// input and then do the as TypedArguments<Args> at the end
return args.reduce(
(arg: ArgumentSpec, agg) => ({ ...agg, [arg.name]: arg.multiple ? ['foo', 'bar'] : 'baz' }),
{} as any
) as unknown as TypedArguments<Args>
}
const mapped = mapArguments([
{ name: 'key1', multiple: true },
{ name: 'key2', multiple: false }
] as const)
// Now I want this to work
mapped.key1.map((x: string) => x.toUpperCase())
mapped.key2.toUpperCase()
// when using the commented-out line in the TypedArguments type instead,
// then this works. I'd like an object though, not an array.
// mapped[0] is string[]
// mapped[1] is string
// mapped[0].map(x => x.toUpperCase())
// mapped[1].toUpperCase()
See also in Typescript Playground
Expected this to work as outlined above. Did not find a TypeScript construct that would make it work.