I want to create a Typescript Type for an array of objects. In this array of objects, I require only one object to have a property set to true.
You can probably solve it just with this typescript example, but I will provide a long and detailed explanation.
Let's say (just an example!) I want to recreate a <select> tag (or a dropdown list).
My custom select has two have at least two options and I always want to have only one object to be active. I have three models:
abstract class SimpleDropDownListElement {
constructor(public label: string, public value: any) {}
}
class DropdownListElement extends SimpleDropDownListElement {
constructor(public label: string, public value: any, public active?: false) {
super(label, value);
}
}
class DropdownActiveListElement extends SimpleDropDownListElement {
active = true;
constructor(public label: string, public value: any) {
super(label, value);
}
}
I want to have an array with at least one (or more) DropdownListElement(s) and one (always one - e.g. never 0 or 2+) DropdownActiveListElement. Any order (object with active set to true can be everywhere in the array).
So my idea was to create a type like this:
type DropDownOptionsArray = [DropdownActiveListElement, DropdownListElement,
...Array<DropdownListElement>];
And that works, however, I need to have the object with the active property set to true as the first element of my array.
So my idea was to reverse the array (not very smart), but I still get problems if the array holds more than 3 values.
type Reverse<Tuple> = Tuple extends [infer A, ...infer B]? [...Reverse<B>, A] : [];
const dropInvertedWithInvertedType: Reverse<DropDownOptionsArray> =
[new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a')];
const dropInvertedWithInvertedType1: Reverse<DropDownOptionsArray> =
[new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b')]; // errors
Then I started to go crazy with rest elements (hoping for TS v4 to help me with some magic):
type DropDownOptionsArray = [...[DropdownActiveListElement],
...Array<DropdownListElement>, ...Array<DropdownListElement>];
// OK
const twoEntries: DropDownOptionsArray = [new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b')];
const fourEntries: DropDownOptionsArray = [new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b'), new DropdownListElement('b', 'b'),
new DropdownListElement('b', 'b')];
// should not error - but errors
const twoEntriesRandomPos: DropDownOptionsArray = [new DropdownListElement('b', 'b'),
new DropdownActiveListElement('a', 'a'), new DropdownListElement('b', 'b')];
const twoEntriesRandomPos: DropDownOptionsArray = [new DropdownListElement('b', 'b'),
new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a')];
// should error
const twoActiveEntries: DropDownOptionsArray = [new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a')];
const noActiveEntry : DropDownOptionsArray = [new DropdownListElement('b', 'b')]; // should have a different error
Writing overloads verbosely is not feasible, we could have an array of 20+ elements.
To summarize, I would need this Type:
- array of objects
- holds at least two or more objects
- only one object has to have property
active = true(all other objects may haveactive = false) - the object with property
active = truecan be placed at any index in the array (from index0to indexarray.length - 1)
Thank you!!
{selected: number, options: ListElement[]}Where selected is the index of the active element?