Reputation: 177
I have an interesting case where it would be very useful to dynamically define types from a central JSON data store. Let me explain.
I have a json file that contains a list of brands
// brands.json
{
"Airbus": {
"keywords": ["A320", "A380"],
"type": ["planes"]
},
"Jaguar": {
"keywords": ["fiesta"],
"origin": "UK",
"type": ["cars", "glasses"]
},
"Nissan": {
"keywords": ["qashqai"],
"type": ["cars"]
}
}
Then I have my type definitions:
import brands from "./brands.json
type BrandNames= keyof typeof brands ;
type Brands = {
[P in BrandNames]: {
keywords: string[];
origin?: string;
type: string[];
}
};
I have created a type CompanieNames which is automatically generated and is equal to "Airbus" | "Jaguar" | "Nissan"
.
So far so good...
Now, I want to create a type CarBrands, which will be equal to "Jaguar" | "Nissan"
.
Doing type CarBrands = Exclude<CompanieNames, "Airbus">;
would work, but this is not dynamic.
So instead, I need to filter out all keys from Brands
which have a nested type
property that doesn't contain the string "cars"
.
Is it possible to do that?
Upvotes: 1
Views: 57
Reputation: 250376
If the types of the string literal types were preserved, we could do what you want (ie extract only the brands that have car
in the types
field). Typescript will however widen the types of the values in the types
array to string
so this information is lost to us when we actually look at the type of the json object.
Just for fun, using the as const
in 3.4 (unreleased yet) to preserve all string literal types, this is what the solution would look like:
let data = {
"Airbus": {
"keywords": ["A320", "A380"],
"type": ["planes"]
},
"Jaguar": {
"keywords": ["fiesta"],
"origin": "UK",
"type": ["cars", "glasses"]
},
"Nissan": {
"keywords": ["qashqai"],
"type": ["cars"]
}
} as const;
type Data = typeof data;
type CarBrands = {
[P in keyof Data]: 'cars' extends Data[P]['type'][number] ? P : never
}[keyof Data]
Again the above does not work for imported json modules because typescript will not preserve the literal types in the string arrays and there is no way to tell it to do so at the time of writing.
Upvotes: 2