From a3a639aebdc098b289b513748dcfd40638c4a5e0 Mon Sep 17 00:00:00 2001 From: Joe Pea Date: Sat, 26 Oct 2024 21:00:22 -0700 Subject: [PATCH] improve the type helper --- examples/kitchen-sink-vue/src/App.vue | 5 ++ examples/kitchen-sink-vue/src/KitchenSink.ts | 51 +++++++++++++------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/examples/kitchen-sink-vue/src/App.vue b/examples/kitchen-sink-vue/src/App.vue index f17dadf..a170202 100644 --- a/examples/kitchen-sink-vue/src/App.vue +++ b/examples/kitchen-sink-vue/src/App.vue @@ -39,6 +39,11 @@ sink.value!.$emit + + + diff --git a/examples/kitchen-sink-vue/src/KitchenSink.ts b/examples/kitchen-sink-vue/src/KitchenSink.ts index be5cdba..bc431c3 100644 --- a/examples/kitchen-sink-vue/src/KitchenSink.ts +++ b/examples/kitchen-sink-vue/src/KitchenSink.ts @@ -1,4 +1,4 @@ -import type {EmitFn, HTMLAttributes, ObjectEmitsOptions, PublicProps} from 'vue' +import {type EmitFn, type HTMLAttributes, type PublicProps} from 'vue' // Select the properties to expose to template type checking. type KitchenSinkAttributes = 'foo' | 'bar' @@ -20,21 +20,8 @@ export class SomeEvent extends Event { } } -type DefineCustomElement< - T extends HTMLElement, - Attributes extends keyof T = keyof T, - Events extends ObjectEmitsOptions = {}, -> = new () => T & { - // Use this to define the properties exposed to template type checking. - /** @deprecated Do not use the $props property on a Custom Element ref, this is for template prop types only. */ - $props: HTMLAttributes & Partial> & PublicProps - - // Use this to define specifically the event properties exposed to template type checking. - /** @deprecated Do not use the $emit property on a Custom Element ref, this is for template prop types only. */ - $emit: EmitFn - - // /** @deprecated Do not use the $attrs property on an element ref, this is for template prop types only. */ - // $attrs: {[x: string]: string} +type KitchenSinkEvents = { + 'some-event': SomeEvent } customElements.define('kitchen-sink', KitchenSink) @@ -42,6 +29,36 @@ customElements.define('kitchen-sink', KitchenSink) declare module 'vue' { interface GlobalComponents { // Use the helper to add any custom element to GlobalComponents. - 'kitchen-sink': DefineCustomElement void}> + 'kitchen-sink': DefineCustomElement } } + +// helper for Vue type definitions ///////////////////////////////////////////////////////// + +type DefineCustomElement< + ElementType extends HTMLElement, + Events extends EventMap = {}, + SelectedAttributes extends keyof ElementType = keyof ElementType, +> = new () => ElementType & { + // Use $props to define the properties exposed to template type checking. Vue + // specifically reads prop definitions from the `$props` type. Note that we + // combine the element's props with the global HTML props and Vue's special + // props. + /** @deprecated Do not use the $props property on a Custom Element ref, this is for template prop types only. */ + $props: HTMLAttributes & Partial> & PublicProps + + // Use $emit to specifically define event types. Vue specifically reads event + // types from the `$emit` type. Note that `$emit` expects a particular format + // that we map `Events` to. + /** @deprecated Do not use the $emit property on a Custom Element ref, this is for template prop types only. */ + $emit: VueEmit +} + +type EventMap = { + [event: string]: Event +} + +// This maps an EventMap to the format that Vue's $emit type expects. +type VueEmit = EmitFn<{ + [K in keyof T]: (event: T[K]) => void +}>