Reputation: 1473
What is the best way to define an interface where one of the properties could be any of the properties on an existing object. In this case I have an object called fruits where I am simply storing a number of string constants and I want to define an interface (groceries) such that one of the properties (fruit) could be any of the string constants in the fruits object.
type Fruits = 'APPLE' | 'BANANA' | 'ORANGE';
const fruits = {
APPLE: 'APPLE',
BANANA: 'BANANA',
ORANGE: 'ORANGE'
}
interface IGroceries {
fruit: Fruits,
vegetables: string[]
}
const initialGroceriesState: IGroceries = {
fruit: fruits.APPLE,
vegetables: []
}
This is my best attempt but this will throw an error saying:
Type '{ fruit: string; vegetables: never[]; }' is not assignable to type 'IGroceries'.
Types of property 'fruit' are incompatible.
Type 'string' is not assignable to type 'Fruits'.
Upvotes: 0
Views: 273
Reputation: 222309
fruits
key values are inferred to strings. This results in error, because Fruits
is assignable to string type but not vice versa.
In order to avoid this, fruits
type should be specified explicitly:
const fruits: { [K in Fruits]: K } = {
APPLE: 'APPLE',
BANANA: 'BANANA',
ORANGE: 'ORANGE'
};
An another way is to use string enum, as another answer suggests. Depending on the case, this may or may not be desirable.
Upvotes: 0
Reputation: 37918
One of the options is switching to string enum:
enum Fruits {
APPLE = 'APPLE',
BANANA = 'BANANA',
ORANGE = 'ORANGE'
}
interface IGroceries {
fruit: Fruits,
vegetables: string[]
}
const initialGroceriesState: IGroceries = {
fruit: Fruits.APPLE,
vegetables: []
}
Another option is correcting the typing of the existing object:
type Fruits = 'APPLE' | 'BANANA' | 'ORANGE';
const fruits: { [key: string]: Fruits } = {
APPLE: 'APPLE',
BANANA: 'BANANA',
ORANGE: 'ORANGE'
}
interface IGroceries {
fruit: Fruits,
vegetables: string[]
}
const initialGroceriesState: IGroceries = {
fruit: fruits.APPLE,
vegetables: []
}
Upvotes: 3