Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add type inference for the options prop #189

Merged
merged 7 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 24 additions & 13 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,20 +441,31 @@ The above list of events are [Svelte `dispatch` events](https://svelte.dev/tutor

## 🦺   TypeScript

TypeScript users can import the types used for internal type safety:
The type of `options` is inferred automatically from the data you pass. E.g.

```ts
const options = [
{ label: `foo`, value: 42 }
{ label: `bar`, value: 69 }
]
// type Option = { label: string, value: number }
const options = [`foo`, `bar`]
// type Option = string
const options = [42, 69]
// type Option = number
```

```svelte
Copy link
Contributor Author

@joelmukuthu joelmukuthu Dec 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't put an example here because demonstrating type-safety would probably require a screenshot. These can be used if needed:
Screenshot from 2022-12-29 10-51-08
Screenshot from 2022-12-29 10-51-16
Screenshot from 2022-12-29 10-51-23

Note that the import line doesn't say import MultiSelect from 'svelte-multiselect'; since this was a test I did locally.

<script lang="ts">
import MultiSelect from 'svelte-multiselect'
import type { Option, ObjectOption } from 'svelte-multiselect'

const myOptions: ObjectOption[] = [
{ label: 'foo', value: 42 },
{ label: 'bar', value: 69 },
]
// an Option can be string | number | ObjectOption
const myNumbers: Option[] = [42, 69]
</script>
The inferred type of `Option` is used to enforce type-safety on derived props like `selected` as well as slot components. E.g. you'll get an error when trying to use a slot component that expects a string if your options are objects (see [this comment](https://github.com/janosh/svelte-multiselect/pull/189/files#r1058853697) for example screenshots).

You can also import [the types this component uses](https://github.com/janosh/svelte-multiselect/blob/main/src/lib/index.ts) for downstream applications:

```ts
import {
Option,
ObjectOption,
DispatchEvents,
MultiSelectEvents,
} from 'svelte-multiselect'
```

## ✨ &nbsp; Styling
Expand Down
7 changes: 4 additions & 3 deletions src/lib/MultiSelect.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script lang="ts">
import { createEventDispatcher, tick } from 'svelte'
import { flip } from 'svelte/animate'
import type { DispatchEvents, MultiSelectEvents, ObjectOption, Option } from './'
import type { DispatchEvents, MultiSelectEvents, Option as GenericOption } from './'
import CircleSpinner from './CircleSpinner.svelte'
import { CrossIcon, DisabledIcon, ExpandIcon } from './icons'
import Wiggle from './Wiggle.svelte'
type Option = $$Generic<GenericOption>

export let activeIndex: number | null = null
export let activeOption: Option | null = null
Expand Down Expand Up @@ -60,7 +61,7 @@
export let searchText: string = ``
export let selected: Option[] =
options
?.filter((op) => (op as ObjectOption)?.preselected)
?.filter((op) => op instanceof Object && op?.preselected)
.slice(0, maxSelect ?? undefined) ?? []
export let sortSelected: boolean | ((op1: Option, op2: Option) => number) = false
export let selectedOptionsDraggable: boolean = !sortSelected
Expand All @@ -69,7 +70,7 @@
export let value: Option | Option[] | null = null

// get the label key from an option object or the option itself if it's a string or number
const get_label = (op: Option) => (op instanceof Object ? op.label : op)
const get_label = (op: GenericOption) => (op instanceof Object ? op.label : op)

// if maxSelect=1, value is the single item in selected (or null if selected is empty)
// this solves both https://github.com/janosh/svelte-multiselect/issues/86 and
Expand Down