Skip to content

Commit a3a639a

Browse files
committed
improve the type helper
1 parent 176c97f commit a3a639a

File tree

2 files changed

+39
-17
lines changed

2 files changed

+39
-17
lines changed

examples/kitchen-sink-vue/src/App.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ sink.value!.$emit
3939

4040
<!-- @vue-expect-error built-in attributes are type checked -->
4141
<kitchen-sink spellcheck="123"></kitchen-sink>
42+
43+
<!-- @vue-expect-error technically .attr should accept strings because it is
44+
setting an attribute, but the types are limited in this regard: the type is
45+
the same regardless if .attr or .prop is used. -->
46+
<kitchen-sink :foo.attr="'123'"></kitchen-sink>
4247
</template>
4348

4449
<style scoped></style>
Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {EmitFn, HTMLAttributes, ObjectEmitsOptions, PublicProps} from 'vue'
1+
import {type EmitFn, type HTMLAttributes, type PublicProps} from 'vue'
22

33
// Select the properties to expose to template type checking.
44
type KitchenSinkAttributes = 'foo' | 'bar'
@@ -20,28 +20,45 @@ export class SomeEvent extends Event {
2020
}
2121
}
2222

23-
type DefineCustomElement<
24-
T extends HTMLElement,
25-
Attributes extends keyof T = keyof T,
26-
Events extends ObjectEmitsOptions = {},
27-
> = new () => T & {
28-
// Use this to define the properties exposed to template type checking.
29-
/** @deprecated Do not use the $props property on a Custom Element ref, this is for template prop types only. */
30-
$props: HTMLAttributes & Partial<Pick<T, Attributes>> & PublicProps
31-
32-
// Use this to define specifically the event properties exposed to template type checking.
33-
/** @deprecated Do not use the $emit property on a Custom Element ref, this is for template prop types only. */
34-
$emit: EmitFn<Events>
35-
36-
// /** @deprecated Do not use the $attrs property on an element ref, this is for template prop types only. */
37-
// $attrs: {[x: string]: string}
23+
type KitchenSinkEvents = {
24+
'some-event': SomeEvent
3825
}
3926

4027
customElements.define('kitchen-sink', KitchenSink)
4128

4229
declare module 'vue' {
4330
interface GlobalComponents {
4431
// Use the helper to add any custom element to GlobalComponents.
45-
'kitchen-sink': DefineCustomElement<KitchenSink, KitchenSinkAttributes, {'some-event': (e: SomeEvent) => void}>
32+
'kitchen-sink': DefineCustomElement<KitchenSink, KitchenSinkEvents, KitchenSinkAttributes>
4633
}
4734
}
35+
36+
// helper for Vue type definitions /////////////////////////////////////////////////////////
37+
38+
type DefineCustomElement<
39+
ElementType extends HTMLElement,
40+
Events extends EventMap = {},
41+
SelectedAttributes extends keyof ElementType = keyof ElementType,
42+
> = new () => ElementType & {
43+
// Use $props to define the properties exposed to template type checking. Vue
44+
// specifically reads prop definitions from the `$props` type. Note that we
45+
// combine the element's props with the global HTML props and Vue's special
46+
// props.
47+
/** @deprecated Do not use the $props property on a Custom Element ref, this is for template prop types only. */
48+
$props: HTMLAttributes & Partial<Pick<ElementType, SelectedAttributes>> & PublicProps
49+
50+
// Use $emit to specifically define event types. Vue specifically reads event
51+
// types from the `$emit` type. Note that `$emit` expects a particular format
52+
// that we map `Events` to.
53+
/** @deprecated Do not use the $emit property on a Custom Element ref, this is for template prop types only. */
54+
$emit: VueEmit<Events>
55+
}
56+
57+
type EventMap = {
58+
[event: string]: Event
59+
}
60+
61+
// This maps an EventMap to the format that Vue's $emit type expects.
62+
type VueEmit<T extends EventMap> = EmitFn<{
63+
[K in keyof T]: (event: T[K]) => void
64+
}>

0 commit comments

Comments
 (0)