Edit: Solved. Answer edited below
I would like to create a Svelte dropdown component that accepts multiple kinds of possible items
<MySelect options={...}
. I've considered several possibilities:
Simplest: Raw string value. The "text" of the dropdown item is the same as the value
options=["Item 1", "Item 2", "Item 3"]
Customizable text vs label
options=[{text: string, value: anything}, {...}]
Grouped + Simplest. Grouped select
options=[ { text: "Group 1", options: ["Item 1", "Item 2", ...]}
Grouped + Customizable
options=[ { text: "Group 1", options: [{text: string, value: anything}, {...}]}
I have come up with:
/** * Definitions for a single item in the list. */export type SimpleOption = string;export type LabeledSimpleOption<T> = { text: string; value: T };export type Option<T> = SimpleOption | LabeledSimpleOption<T>;export type GroupedOption<T> = { text: string; options: Array<Option<T>> };/** * Definitions for an array of items. */export type SimpleOptions<T> = Array<Option<T>>;export type GroupedSimpleOptions<T> = Array<GroupedOption<T>>;export type Options<T> = Option<T> | GroupedOption<T>;
Expectations:
Would be great to not have to specify the type.
Would be great if it could be inferred automatically
let options: Options<string> = [ { text: "Group 1", options: [ { value: "1", text: "One" }, // OK { value: "2", fsfsd: "Two" }, // Not OK { value: "3", text: 3 }, // Not OK ] }];let options: Options<string> = ["Item 1", "Item 2"] // OKlet options: Options<number> = [1, 2] // NOT OKlet options: Options<number> = [ { value: "1", text: 1 }, // OK { value: "2", fsfsd: 2 }, // Not OK { value: "3", text: "3" }, // Not OK]
Currently
let options: Options<string>
acts as if options had "any" as a type. I can assign anything and it's going to be accepted.I need to be able to type the value returned as well as a single option itself.
const items = [...]; // Assuming items signature is compatible with Options<T><MySelect options={items} valueAsItem={false} // Meaning item value is Items[i].value itemSelect={(selectedItem) => ...} // selectedItem must be of type Items[i].value<MySelect options={items} valueAsItem={true} // Meaning item value is the item itself: Items[i] itemSelect={(selectedItem) => ...} // selectedItem must be of type Items[i]
Later I would like to be able to dynamically set the label/value attribute name using props. Meaning
let items = [ { attr: "1", label: 1 }, ...]<MySelect options={items} valueAsItem={true} labelField="label" valueField="attr" itemSelect={(selectedItem) => ...} // selectedItem would be "1" (type of string)
I'm wondering if it is even possible to achieve everything in TypeScript or if I should rely on any
type instead.
Edit 1
Edit: I have been requested to provide "working" demonstration code.I didn't mean to go so far as to involve Svelte/Components code asthe question is purely a Typescript challenge. I've created a Svelte playground for the demonstration.Type: src/types/MySelect.d.tsUsage: src/routes/+page.svelteURL: https://codesandbox.io/p/devbox/svelte-dropdown-test-mq3tpp