Reputation: 21
Given that I can use a Record
to make me not forget to type all options in an object
type Country = "uk" | "france" | "india";
export const data: Record<Country, boolean> = {
uk: true,
france: true,
// complains that india is not present, excellent!
};
how can I make so it complains the same way for arrays?
export const data = [
{value: "uk"},
{value: "france"},
// how to make typescript complain here that I forgot to add {value: "india"}?
];
Upvotes: 1
Views: 626
Reputation: 2444
I know it's ugly, but it's not factorial complexity and it does not require a helper function.
type Country = "uk" | "france" | "india";
const data = [
{ value: 'uk' },
{ value: 'france' },
{ value: 'india' }
] as const;
type test = IsExhaustive<typeof data>
type IsExhaustive<
T extends Country extends Values ? unknown : { error: [Country, 'does not extend', Values] },
Values = keyof {
[K in keyof T as T[K] extends {value: PropertyKey} ? T[K]['value'] : never]: never
}
> = T
Upvotes: 0
Reputation: 61
Based on this answer, I think that this solves your problem:
type Country = "uk" | "france" | "india";
type MapOfKeysOf<U extends string> = {
[key in U]: MapOfKeysOf<Exclude<U, key>>;
}
type ExhaustiveArrayOfObjects<Keys extends {[key: string]: {}}, T = {}, KeyName extends string = "value"> = {} extends Keys ? [] : {
[key in keyof Keys]: [T & Record<KeyName, key>, ...ExhaustiveArrayOfObjects<Keys[key], T, KeyName>];
}[keyof Keys]
export const data: ExhaustiveArrayOfObjects<MapOfKeysOf<Country>> = [
{
value: 'uk',
},
{
value: 'france',
},
{
value: 'india',
}
]
If you want, you can pass an extra type to extends or event customize the prop that will hold de value
type Country = "uk" | "france" | "india";
type MapOfKeysOf<U extends string> = {
[key in U]: MapOfKeysOf<Exclude<U, key>>;
}
type ExhaustiveArrayOfObjects<Keys extends {[key: string]: {}}, T = {}, KeyName extends string = "value"> = {} extends Keys ? [] : {
[key in keyof Keys]: [T & Record<KeyName, key>, ...ExhaustiveArrayOfObjects<Keys[key], T, KeyName>];
}[keyof Keys]
type Option = {
label: string,
id: Country
}
const data: ExhaustiveArrayOfObjects<MapOfKeysOf<Country>, Option, "id"> = [
{
label: "United Kingdom",
id: 'uk',
},
{
label: "France",
id: 'france',
},
{
label: "India",
id: 'india',
}
]
const option: Option = data[0]
You can check more on this playgroud
Upvotes: 2
Reputation: 51
I am using some utility functions from a library but the it is total achievable without using one. I actually got some help from stack overflow in order to come up with this solution haha... Credits to caTS Related question
import { List, Union } from "ts-toolbelt";
type GetData<TCountries extends string[]> = List.Length<TCountries> extends 0
? []
: List.Append<GetData<List.Pop<TCountries>>, {
value: List.Last<TCountries>
}>;
export const data : GetData<Union.ListOf<Country>> = [
{value: "uk"},
{value: "france"},
// how to make typescript complain here that I forgot to add {value: "india"}?
];
Upvotes: 0