Reputation: 1755
I have a situation where I could have a number of optional, t-shirt size props added to an object. Is there a way to define a type and set it as the optional key's type in an interface?
type Size = `xxs` | `xs` | `s` | `m` | `l` | `xl` | `xxl`;
interface Sizes {
[key: Size]: string;
^^^
}
The above example presents me with the following error:
An index signature parameter type cannot be a union type. Consider using a mapped object type instead.ts(1337)
I then found this question, and modified my code as follows:
type Size = `xxs` | `xs` | `s` | `m` | `l` | `xl` | `xxl`;
interface Sizes {
[key in Size]: string;
^^^^^^^^^^^
}
But am then presented with the following errors:
A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)
Cannot find name 'key'.ts(2304)
I've obviously missed something fairly crucial in my implementation. Any explanation would be much appreciated.
Upvotes: 2
Views: 3459
Reputation: 329198
A mapped type is not an interface. You can make it a type alias instead of an interface:
type TSizes = { [K in Size]?: string };
Note that I put the ?
in there which makes the properties optional, as you presumably want.
Once you have such a named type you can make an interface that extends it (since all of its property names are statically known):
interface ISizes extends TSizes { }
Or you can use the built-in Partial
and Record
utility types to do both at the same time:
interface Sizes extends Partial<Record<Size, string>> { }
Then, Sizes
is an interface with optional property keys from Size
whose values are of type string
:
const s: Sizes = {
s: "foo",
m: "bar",
xxl: "bazzz"
}
Okay, hope that helps; good luck!
Upvotes: 7