Reputation: 6145
I currently have this mapping type, and it works great in a lot of situations:
type WithKeyPrefix<P extends string, T extends {}> = {
[K in Extract<keyof T, string> as `${P}/${K}`]: T[K];
};
However, I can't get it to reject objects which contain non-string keys. I've tried a couple different solutions so far, but nothing that works as I'd like it to:
type WithKeyPrefix<P extends string, T extends {}> = {
[K in Extract<keyof T, string> as `${P}/${K}`]: T[K];
} & {
[K in Exclude<keyof T, string>]: never;
};
type WithKeyPrefix<P extends string, T extends Record<string, unknown>> = {
[K in Extract<keyof T, string> as `${P}/${K}`]: T[K];
};
The first makes no apparent difference. The second produces this error when T
is an interface with limited keys:
TS2344: Type 'Foo' does not satisfy the constraint 'Record<string, unknown>'.
Index signature is missing in type 'Foo'.
Is there a way to constrain the type of T
in the way I want?
Bonus question: Why is Extract<keyof T, string>
needed even when T
is constrained as Record<string, unknown>
?
I came across this question already, but the solutions there are effectively workarounds which are perfectly valid in the context of that question, but cannot work in my situation; hence the new question.
Upvotes: 0
Views: 448
Reputation: 8370
To reject objects with non-string properties you may rewrite your type as:
type WithKeyPrefix<
P extends string,
T extends object & (keyof T extends string ? {} : "Must have only string keys"),
> = {
[K in Extract<keyof T, string> as `${P}/${K}`]: T[K];
};
Credits for string only keys constraint go to this great answer
As for Extract<keyof T, string>
it's required because something that extends Record<string, any>
is not guaranteed to have only string
keys.
Upvotes: 1