Skip to content

Commit 2b9f2bb

Browse files
fix: should display type error when using useModal() #323 (#324)
* fix: should display type error when using `useModal()` #323 * fix: resolve type issues --------- Co-authored-by: Alex Liu <[email protected]>
1 parent 2521c37 commit 2b9f2bb

File tree

4 files changed

+41
-70
lines changed

4 files changed

+41
-70
lines changed

Diff for: packages/vue-final-modal/src/Modal.ts

+18-52
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,57 @@
1-
import type { App, CSSProperties, Component, ComponentOptions, ComponentPublicInstance, ComputedRef, ConcreteComponent, Raw, Ref, VNodeProps } from 'vue'
2-
import type { VueFinalModal } from '.'
1+
import type { App, CSSProperties, Component, ComponentPublicInstance, ComputedRef, Raw, Ref, VNodeProps } from 'vue'
32

43
export type ComponentProps = ComponentPublicInstance['$props']
54

65
export type ModalId = number | string | symbol
76
export type StyleValue = string | CSSProperties | (string | CSSProperties)[]
87

9-
type RawProps = VNodeProps & {
8+
export interface Constructor<P = any> {
9+
__isFragment?: never
10+
__isTeleport?: never
11+
__isSuspense?: never
12+
new (...args: any[]): { $props: P }
13+
}
14+
15+
export type RawProps = VNodeProps & {
1016
// used to differ from a single VNode object as children
1117
__v_isVNode?: never
1218
// used to differ from Array children
1319
[Symbol.iterator]?: never
1420
} & Record<string, any>
1521

16-
type VfmAttrs<P> = (RawProps & P) | ({} extends P ? InstanceType<typeof VueFinalModal>['$props'] : never)
17-
interface UseModalOptionsConcreteComponent<P> { component?: ConcreteComponent<P>; attrs?: VfmAttrs<P> }
18-
interface UseModalOptionsComponentOptions<P> { component?: ComponentOptions<P>; attrs?: VfmAttrs<P> }
19-
20-
type SlotAttrs<P> = (RawProps & P) | ({} extends P ? null : never)
21-
interface ModalSlotOptionsConcreteComponent<P> { component: ConcreteComponent<P>; attrs?: SlotAttrs<P> }
22-
interface ModalSlotOptionsComponentOptions<P> { component: ComponentOptions<P>; attrs?: SlotAttrs<P> }
23-
2422
export interface ModalSlotOptions { component: Raw<Component>; attrs?: Record<string, any> }
2523
export type ModalSlot = string | Component | ModalSlotOptions
2624

27-
export type UseModalOptionsSlots = {
25+
export type UseModalOptions<P> = {
26+
defaultModelValue?: boolean
27+
context?: Vfm
28+
component?: Constructor<P>
29+
attrs?: (RawProps & P) | ({} extends P ? null : never)
2830
slots?: {
29-
default: ModalSlot
3031
[key: string]: ModalSlot
3132
}
3233
}
3334

34-
export type UseModalOptions = {
35-
defaultModelValue?: boolean
36-
context?: Vfm
37-
component?: Raw<Component>
38-
attrs?: Record<string, any>
39-
} & UseModalOptionsSlots
40-
41-
export interface IOverloadedUseModalFn {
42-
<P>(options: UseModalOptionsConcreteComponent<P> & UseModalOptionsSlots): UseModalReturnType
43-
<P>(options: UseModalOptionsComponentOptions<P> & UseModalOptionsSlots): UseModalReturnType
44-
<P>(options:
45-
| UseModalOptionsConcreteComponent<P> & UseModalOptionsSlots
46-
| UseModalOptionsComponentOptions<P> & UseModalOptionsSlots
47-
| UseModalOptions
48-
): UseModalReturnType
49-
}
50-
51-
interface IOverloadedPatchOptionsFn {
52-
<P>(options: UseModalOptionsConcreteComponent<P> & UseModalOptionsSlots): void
53-
<P>(options: UseModalOptionsComponentOptions<P> & UseModalOptionsSlots): void
54-
<P>(options:
55-
| UseModalOptionsConcreteComponent<P> & UseModalOptionsSlots
56-
| UseModalOptionsComponentOptions<P> & UseModalOptionsSlots
57-
| Omit<UseModalOptions, 'defaultModelValue' | 'context'>
58-
): void
59-
}
60-
61-
interface IOverloadedUseModalSlotFn {
62-
<P>(options: ModalSlotOptionsConcreteComponent<P>): ModalSlot
63-
<P>(options: ModalSlotOptionsComponentOptions<P>): ModalSlot
64-
<P>(options: ModalSlotOptionsConcreteComponent<P> | ModalSlotOptionsComponentOptions<P> | ModalSlot): ModalSlot
65-
}
66-
67-
export const useModalSlot: IOverloadedUseModalSlotFn = (options: ModalSlot): ModalSlot => options
68-
6935
export type UseModalOptionsPrivate = {
7036
id: symbol
7137
modelValue: boolean
7238
resolveOpened: () => void
7339
resolveClosed: () => void
7440
}
7541

76-
export interface UseModalReturnType {
77-
options: UseModalOptions & UseModalOptionsPrivate
42+
export interface UseModalReturnType<P> {
43+
options: UseModalOptions<P> & UseModalOptionsPrivate
7844
open: () => Promise<string>
7945
close: () => Promise<string>
80-
patchOptions: IOverloadedPatchOptionsFn
46+
patchOptions: (options: Partial<Omit<UseModalOptions<P>, 'defaultModelValue' | 'context'>>) => void
8147
destroy: () => void
8248
}
8349

8450
export type Vfm = {
8551
install(app: App): void
8652
modals: ComputedRef<Modal>[]
8753
openedModals: ComputedRef<Modal>[]
88-
dynamicModals: (UseModalOptions & UseModalOptionsPrivate)[]
54+
dynamicModals: (UseModalOptions<any> & UseModalOptionsPrivate)[]
8955
modalsContainers: Ref<symbol[]>
9056
get: (modalId: ModalId) => undefined | ComputedRef<Modal>
9157
toggle: (modalId: ModalId, show?: boolean) => undefined | Promise<string>

Diff for: packages/vue-final-modal/src/components/ModalsContainer.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const _vfm = useInternalVfm()
1111
const uid = Symbol('ModalsContainer')
1212
const shouldMount = computed(() => uid === vfm.modalsContainers.value?.[0])
1313
14-
const openedDynamicModals: Ref<(UseModalOptions & UseModalOptionsPrivate)[]> = shallowRef([])
14+
const openedDynamicModals: Ref<(UseModalOptions<any> & UseModalOptionsPrivate)[]> = shallowRef([])
1515
1616
function syncOpenDynamicModals() {
1717
openedDynamicModals.value = vfm.dynamicModals.filter(modal => modal.modelValue)

Diff for: packages/vue-final-modal/src/plugin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { InternalVfm, Modal, ModalId, UseModalOptions, UseModalOptionsPriva
66
export function createVfm() {
77
const modals: ComputedRef<Modal>[] = shallowReactive([])
88
const openedModals: ComputedRef<Modal>[] = shallowReactive([])
9-
const dynamicModals: (UseModalOptions & UseModalOptionsPrivate)[] = shallowReactive([])
9+
const dynamicModals: (UseModalOptions<any> & UseModalOptionsPrivate)[] = shallowReactive([])
1010
const modalsContainers = ref<symbol[]>([])
1111

1212
const vfm: Vfm = markRaw({

Diff for: packages/vue-final-modal/src/useApi.ts

+21-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { isString, tryOnUnmounted } from '@vueuse/core'
22
import { computed, getCurrentInstance, inject, markRaw, reactive, useAttrs } from 'vue'
3-
import type { Component, Raw } from 'vue'
3+
import type { Component } from 'vue'
44
import VueFinalModal from './components/VueFinalModal/VueFinalModal.vue'
55
import type CoreModal from './components/CoreModal/CoreModal.vue'
66
import { internalVfmSymbol, vfmSymbol } from './injectionSymbols'
77

8-
import type { ComponentProps, IOverloadedUseModalFn, InternalVfm, ModalSlot, ModalSlotOptions, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
8+
import type { ComponentProps, Constructor, InternalVfm, ModalSlot, ModalSlotOptions, RawProps, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
99

1010
/**
1111
* Returns the vfm instance. Equivalent to using `$vfm` inside
@@ -22,7 +22,7 @@ export function useInternalVfm(): InternalVfm {
2222
return inject(internalVfmSymbol)!
2323
}
2424

25-
function withMarkRaw(options: Partial<UseModalOptions>, DefaultComponent: Component = VueFinalModal) {
25+
function withMarkRaw<P>(options: Partial<UseModalOptions<P>>, DefaultComponent: Component = VueFinalModal) {
2626
const { component, slots: innerSlots, ...rest } = options
2727

2828
const slots = typeof innerSlots === 'undefined'
@@ -43,23 +43,23 @@ function withMarkRaw(options: Partial<UseModalOptions>, DefaultComponent: Compon
4343

4444
return {
4545
...rest,
46-
component: markRaw(component || DefaultComponent),
46+
component: markRaw(component || DefaultComponent) as Constructor<P>,
4747
slots,
4848
}
4949
}
5050

5151
/**
5252
* Create a dynamic modal.
5353
*/
54-
export const useModal: IOverloadedUseModalFn = function (_options: UseModalOptions): UseModalReturnType {
54+
export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_options: UseModalOptions<P>): UseModalReturnType<P> {
5555
const options = reactive({
5656
id: Symbol('useModal'),
5757
modelValue: !!_options?.defaultModelValue,
58-
resolveOpened: () => {},
59-
resolveClosed: () => {},
58+
resolveOpened: () => { },
59+
resolveClosed: () => { },
6060
attrs: {},
61-
...withMarkRaw(_options),
62-
}) as UseModalOptions & UseModalOptionsPrivate
61+
...withMarkRaw<P>(_options),
62+
}) as UseModalOptions<P> & UseModalOptionsPrivate
6363

6464
if (!options.context) {
6565
const currentInstance = getCurrentInstance()
@@ -89,7 +89,7 @@ export const useModal: IOverloadedUseModalFn = function (_options: UseModalOptio
8989
})
9090
}
9191

92-
function patchOptions(_options: Partial<UseModalOptions>) {
92+
function patchOptions(_options: Partial<Omit<UseModalOptions<P>, 'defaultModelValue' | 'context'>>) {
9393
const { slots, ...rest } = withMarkRaw(_options, options.component)
9494

9595
// patch options.component and options.attrs
@@ -132,6 +132,13 @@ export const useModal: IOverloadedUseModalFn = function (_options: UseModalOptio
132132
return modal
133133
}
134134

135+
export function useModalSlot<P>(options: {
136+
component: Constructor<P>
137+
attrs?: (RawProps & P) | ({} extends P ? null : never)
138+
}) {
139+
return options
140+
}
141+
135142
function patchAttrs<T extends Record<string, any>>(attrs: T, newAttrs: Partial<T>): T {
136143
Object.entries(newAttrs).forEach(([key, value]) => {
137144
attrs[key as keyof T] = value
@@ -140,12 +147,10 @@ function patchAttrs<T extends Record<string, any>>(attrs: T, newAttrs: Partial<T
140147
return attrs
141148
}
142149

143-
type ComponentOptions = {
144-
component?: Raw<Component>
145-
attrs?: Record<string, any>
146-
}
147-
148-
function patchComponentOptions(options: ComponentOptions | ModalSlotOptions, newOptions: ComponentOptions | ModalSlotOptions) {
150+
function patchComponentOptions<P>(
151+
options: Omit<UseModalOptions<P>, 'defaultModelValue' | 'context'> | ModalSlotOptions,
152+
newOptions: Partial<Omit<UseModalOptions<P>, 'defaultModelValue' | 'context'>> | ModalSlotOptions,
153+
) {
149154
if (newOptions.component)
150155
options.component = newOptions.component
151156

0 commit comments

Comments
 (0)