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: improve types for createEventDispatcher #7224

Merged
merged 15 commits into from Apr 14, 2023
43 changes: 33 additions & 10 deletions src/runtime/internal/lifecycle.ts
Expand Up @@ -56,6 +56,25 @@ export function onDestroy(fn: () => any) {
get_current_component().$$.on_destroy.push(fn);
}

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
? I
: never

type ExtractObjectValues<Object extends Record<any, any>> = Object[keyof Object]

type ConstructDispatchFunction<EventMap extends Record<string, any>, EventKey extends keyof EventMap> =
EventMap[EventKey] extends never | null
? (type: EventKey, detail?: null, options?: DispatchOptions) => boolean
: null extends EventMap[EventKey]
? (type: EventKey, detail?: EventMap[EventKey], options?: DispatchOptions) => boolean
: (type: EventKey, detail: EventMap[EventKey], options?: DispatchOptions) => boolean

type CreateDispatchFunctionMap<EventMap> = {
[Key in keyof EventMap]: ConstructDispatchFunction<EventMap, Key>
}

type EventDispatcher<EventMap extends Record<string, any>> = UnionToIntersection<ExtractObjectValues<CreateDispatchFunctionMap<EventMap>>>

export interface DispatchOptions {
cancelable?: boolean;
}
Expand All @@ -72,16 +91,10 @@ export interface DispatchOptions {
*
* https://svelte.dev/docs#run-time-svelte-createeventdispatcher
*/
export function createEventDispatcher<EventMap extends {} = any>(): <
EventKey extends Extract<keyof EventMap, string>
>(
type: EventKey,
detail?: EventMap[EventKey],
options?: DispatchOptions
) => boolean {
export function createEventDispatcher<EventMap extends Record<string, any> = any>(): EventDispatcher<EventMap> {
const component = get_current_component();

return (type: string, detail?: any, { cancelable = false } = {}): boolean => {
return ((type: string, detail?: any, { cancelable = false } = {}): boolean => {
const callbacks = component.$$.callbacks[type];

if (callbacks) {
Expand All @@ -95,9 +108,19 @@ export function createEventDispatcher<EventMap extends {} = any>(): <
}

return true;
};
}) as EventDispatcher<EventMap>;
}

// no type restrictions
const dispatch1 = createEventDispatcher()
dispatch1('click')
dispatch1('something')
dispatch1('something', true)

const dispatch2 = createEventDispatcher<{ click: never }>()
dispatch2('click')
dispatch2('click', true)
ivanhofer marked this conversation as resolved.
Show resolved Hide resolved

/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
Expand Down Expand Up @@ -140,7 +163,7 @@ export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T {
* https://svelte.dev/docs#run-time-svelte-hascontext
*/
export function hasContext(key): boolean {
return get_current_component().$$.context.has(key);
return get_current_component().$$.context.has(key);
}

// TODO figure out if we still want to support
Expand Down