I have the following type (simplified):
type ValueRepresents = {
boolean: true
number?: false
other?: false
} |
{
boolean?: false
number: true
other?: false
} |
{
boolean?: false
number?: false
other: true
}
My actual type has many more possible keys. Is there a way to generate this type from a list of possible keys to make it only valid to have one key with value set to true? Something like:
type ValueTypes = "boolean" | "number" | "other"
type ValueRepresents <T extends ValueTypes> = {
[k in ValueTypes]: k extends T ? true : false
}
const a: ValueRepresents<"boolean"> = {
boolean: true,
number: false,
other: false,
}
But I'm aiming for being able to use:
// should pass
const a: ValueRepresents = { boolean: true }
// should pass
const a2: ValueRepresents = {
boolean: true,
number: false,
}
// should error
const a3: ValueRepresents = {
boolean: true,
number: true,
}
// should error
const a4: ValueRepresents = {}
I also tried following this answer but was not yet successful with:
type ValueRepresents <T extends ValueTypes> = {
[k in Exclude<T, ValueTypes>]?: false
} & { [k in T]: true }
You can try creating an union type like this
type ValueTypes = "boolean" | "number" | "other"
type ValueRepresents = ({
[K in ValueTypes]: Partial<Record<Exclude<ValueTypes, K>, false>> & Record<K, true>
})[ValueTypes]
I don't think this expression has a specific name. I've seen similar examples used in the docs in Advanced Types, but I'll try to explain how it works.
type ValueRepresents = {
[K in ValueTypes]: Partial<Record<Exclude<ValueTypes, K>, false>> & Record<K, true>
}
creates type equivalent to:
type ValueRepresents = {
boolean: {
boolean: true;
number?: false;
other?: false;
};
number: {
boolean?: false;
number: true;
other?: false;
};
other: {
boolean?: false;
number?: true;
other: true;
};
};
and by adding the union type in square brackets [ValueTypes]
it extracts the values of those (all) keys in another union type equivalent to:
type ValueRepresents =
| {
boolean: true;
number?: false;
other?: false;
}
| {
boolean?: false;
number: true;
other?: false;
}
| {
boolean?: false;
number?: true;
other: true;
};