Reputation: 1716
First off, I want the following result:
type Wrapper<ID extends string> = { id: ID };
type WrapperWithPayload<ID extends string, Payload> = { id: ID, payload: Payload };
enum IDs {
FOO = "ID Foo",
BAR = "ID Bar",
BAZ = "ID Baz"
}
interface AssociatedTypes {
[IDs.FOO]: number;
[IDs.BAR]: number[];
}
type Result = MagicMapper<IDs, AssociatedTypes>
/*
* Result should have this type:
* {
* [IDs.FOO]: WrapperWithPayload<IDs.FOO, number>;
* [IDs.BAR]: WrapperWithPayload<IDs.BAR, number[]>;
* [IDs.BAZ]: Wrapper<IDs.BAZ>;
* }
*/
In other words, I want to supply some strings and a mapping, which maps a subset of those strings to another type. Then, I want to create a new type using the strings as keys. For each of the strings/keys, if a mapping exists, I want to use type A, and otherwise I want to use type B.
Right now, my approach looks like this:
type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
[key in T]: Mapping[key] extends never ? Wrapper<key> : WrapperWithPayload<key, Mapping[key]>;
};
And that one almost hits the goal:
type Result = {
"ID Foo": WrapperWithPayload<IDs.FOO, number>;
"ID Bar": WrapperWithPayload<IDs.BAR, number[]>;
"ID Baz": Wrapper<IDs.BAZ> | WrapperWithPayload<IDs.BAZ, any>;
}
The union on the Baz key is wrong. I think the error lies within the extends never
condition, but replacing it with, e.g., undefined makes things only worse:
type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
[key in T]: Mapping[key] extends undefined ? Wrapper<key> : WrapperWithPayload<key, Mapping[key]>;
};
type Result = {
"ID Foo": Wrapper<IDs.FOO>;
"ID Bar": Wrapper<IDs.BAR>;
"ID Baz": Wrapper<IDs.BAZ>;
}
Is there any way to make things work the way I need?
Upvotes: 3
Views: 1646
Reputation: 249536
You can change the test for the existence of the key in Mapping
to Mapping extends { [P in key]: infer U }
and everything will work as expected:
type Wrapper<ID extends string> = { id: ID };
type WrapperWithPayload<ID extends string, Payload> = { id: ID, payload: Payload };
enum IDs {
FOO = "ID Foo",
BAR = "ID Bar",
BAZ = "ID Baz"
}
interface AssociatedTypes {
[IDs.FOO]: number;
[IDs.BAR]: number[];
}
type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
[key in T]: Mapping extends { [P in key]: infer U } ? WrapperWithPayload<key, U>: Wrapper<key> ;
};
type Result = MagicMapper<IDs, AssociatedTypes>
// same as :
type Result = {
"ID Foo": WrapperWithPayload<IDs.FOO, number>;
"ID Bar": WrapperWithPayload<IDs.BAR, number[]>;
"ID Baz": Wrapper<IDs.BAZ>;
}
Upvotes: 1