TypeScript
TypeScript
The package is written in TypeScript and ships full type declarations. This page covers the patterns that give end-to-end type safety.
Registering groups
Groups are validated at compile time via module augmentation. Without this step the package cannot be used — ModalGroup resolves to never and every group reference is a type error.
import { type DefineGroups } from '@kolirt/vue-modal'
declare module '@kolirt/vue-modal' {
interface ModalGroupRegistry extends DefineGroups<['default', 'confirm', 'panel']> {}
}
After augmentation every group field in openModal, useModal, closeModalsByGroup, <ModalTarget>, and createModal only accepts 'default' | 'confirm' | 'panel'.
openModal(MyDialog, { group: 'default' }) // ✓
openModal(MyDialog, { group: 'unknown' }) // ✗ Type error
Typed results — openModal<TResult>
The first generic is the type the modal resolves with:
const result = await openModal<{ id: number; name: string }>(SaveDialog, {
group: 'default'
}).catch(() => null)
result?.id // number
result?.name // string
Inside the component, useModalContext<TResult>() must use the same type:
const { confirm } = useModalContext<{ id: number; name: string }>()
confirm({ id: 1, name: 'Alice' }) // ✓
confirm({ id: 1 }) // ✗ missing name
The type is not inferred from the component automatically — you annotate it explicitly on both sides.
Typed props
openModal's props option mirrors exactly what defineProps declares on the component:
<script setup lang="ts">
const props = defineProps<{
title: string
message?: string
}>()
</script>
openModal(ConfirmDialog, {
group: 'confirm',
props: {
title: 'Delete?', // ✓ required string
message: 'Sure?', // ✓ optional string
unknown: true // ✗ Type error
}
})
Declaring a component's group
Use defineOptions to bind a component to a group at design time:
<script setup lang="ts">
defineOptions({ modalGroup: 'confirm' })
</script>
After declaring modalGroup, openModal(ConfirmDialog) without a group option is valid. Passing an undeclared group value is still a type error.
useModal type inference
const modal = useModal<SaveResult>(SaveDialog, {
group: 'default'
})
await modal.open({ props: { title: 'Save' } })
The second generic (C) is usually inferred — you only need it if TypeScript cannot resolve the component type from context.
ModalHandle typing
ModalHandle<T> extends Promise<T>:
interface ModalHandle<T> extends Promise<T> {
id: number
group: ModalGroup
close(opts?: CloseModalOptions<T>): void
on(event: string, handler: (...args: any[]) => void): void
off(event: string, handler: (...args: any[]) => void): void
}
Store it typed when you need to close it later:
const handle = openModal<boolean>(ConfirmDialog, { group: 'confirm' })
handle.close({ success: true, data: true })
Full registry pattern example
import { type DefineGroups } from '@kolirt/vue-modal'
declare module '@kolirt/vue-modal' {
interface ModalGroupRegistry extends DefineGroups<[
'default',
'confirm',
'panel'
]> {}
}
import './modal-types' // side-effect import to activate augmentation
import { createModal } from '@kolirt/vue-modal'
app.use(createModal({
groups: {
default: {},
confirm: { disableCloseOnEscape: true },
panel: { enableInteractOutside: true, disableLockBodyScroll: true }
}
}))
CloseModalOptions<T> typing
When calling closeModalById with success: true the data field must match T:
closeModalById<boolean>(handle.id, { success: true, data: true }) // ✓
closeModalById<boolean>(handle.id, { success: true, data: 'oops' }) // ✗
