Skip to content

Commit

Permalink
simplify the custom element type def, instead of using DefineComponen…
Browse files Browse the repository at this point in the history
…t, so that the element ref does not have all the property types that Vue components have
  • Loading branch information
trusktr committed Oct 24, 2024
1 parent e702757 commit 176c97f
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 12 deletions.
31 changes: 28 additions & 3 deletions examples/kitchen-sink-vue/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,41 @@ import {useTemplateRef} from 'vue'
const sink = useTemplateRef('sink')
// Ref works as expected:
sink.value!.someMethod()
sink.value!.foo = 123
// Unfortunately, these properties are unnecessarily exposed on the element ref.
// We marked them deprecated to prevent users from using them:
sink.value!.$props
sink.value!.$emit
</script>

<template>
<!-- There should be no type errors: -->
<kitchen-sink ref="sink" :foo="123" :bar="true"></kitchen-sink>
<kitchen-sink ref="sink" key="123" :foo="123" :bar="true"></kitchen-sink>

<!-- no error (all props optional) -->
<kitchen-sink></kitchen-sink>

<!-- @vue-expect-error boolean not assignable to number -->
<kitchen-sink :foo="true"></kitchen-sink>
<!-- @vue-expect-error number not assignable to boolean -->
<kitchen-sink :bar="123"></kitchen-sink>

<!-- The 'some-event' event type is known: -->
<kitchen-sink
@some-event="
event => {
const n: number = event.foo // ok
// @ts-expect-error number is not assignable to string
const s: string = event.foo // error
}
"
></kitchen-sink>

<!-- There should be type errors: -->
<kitchen-sink ref="sink" :foo="true" :bar="123"></kitchen-sink>
<!-- @vue-expect-error built-in attributes are type checked -->
<kitchen-sink spellcheck="123"></kitchen-sink>
</template>

<style scoped></style>
36 changes: 27 additions & 9 deletions examples/kitchen-sink-vue/src/KitchenSink.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {DefineComponent} from 'vue'
import type {EmitFn, HTMLAttributes, ObjectEmitsOptions, PublicProps} from 'vue'

// Select the properties to expose to template type checking.
type KitchenSinkAttributes = 'foo' | 'bar'
Expand All @@ -7,23 +7,41 @@ export class KitchenSink extends HTMLElement {
foo = 123
bar = false

// This is not exposed to template type checking.
// This is not exposed to template types.
someMethod() {
/* ... */
}
}

customElements.define('kitchen-sink', KitchenSink)
export class SomeEvent extends Event {
foo = 123
constructor() {
super('some-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<Pick<T, Attributes>> & PublicProps

type VueElementAttribtues<T extends HTMLElement, Attributes extends keyof T> =
// This is the type that is exposed to template type checking.
DefineComponent<Partial<Pick<T, Attributes>>> &
// Additionally intersect with a constructor type that returns the element type, for refs to work properly.
(new () => T)
// 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<Events>

// /** @deprecated Do not use the $attrs property on an element ref, this is for template prop types only. */
// $attrs: {[x: string]: string}
}

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

declare module 'vue' {
interface GlobalComponents {
// Use the helper to add any custom element to GlobalComponents.
'kitchen-sink': VueElementAttribtues<KitchenSink, KitchenSinkAttributes>
'kitchen-sink': DefineCustomElement<KitchenSink, KitchenSinkAttributes, {'some-event': (e: SomeEvent) => void}>
}
}

0 comments on commit 176c97f

Please sign in to comment.