Skip to content

Commit 42fe060

Browse files
committed
Correct and improve docs on type definitions for Custom Elements.
The doc was incorrect for two reasons: - For Vue-based custom elements, the type passed into `GlobalComponents` needs to be the Vue component type, not the custom element type, or the custom elements will not be type checked in any Vue templates. - The name of the elements need to have at least two words and a hyphen, otherwise with single words the custom elements will not be type checked in any Vue templates.
1 parent 78d61f2 commit 42fe060

File tree

1 file changed

+56
-8
lines changed

1 file changed

+56
-8
lines changed

src/guide/extras/web-components.md

+56-8
Original file line numberDiff line numberDiff line change
@@ -242,25 +242,73 @@ If you have many components, you can also leverage build tool features such as V
242242

243243
If you are developing an application or a library, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements.
244244

245-
Custom elements are registered globally using native APIs, so by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings using the the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) in Vue templates and/or in [JSX](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements):
245+
Custom elements are registered globally using native APIs, so by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings by augmenting the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) for type checking in Vue templates (JSX users can augment the [JSX.IntrinsicElements](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements) type, which is not shown here).
246+
247+
Here is how to define the type for a custom element made with Vue:
246248

247249
```typescript
248250
import { defineCustomElement } from 'vue'
249251

250-
// vue SFC
251-
import CounterSFC from './src/components/counter.ce.vue'
252+
// Import the Vue component.
253+
import SomeComponent from './src/components/SomeComponent.ce.vue'
254+
255+
// Turn the Vue component into a Custom Element class.
256+
export const SomeElement = defineCustomElement(SomeComponent)
257+
258+
// Remember define the element with the browser, or the element will not be instantiated.
259+
window.customElements.define('some-element', SomeElement)
260+
261+
// Augment Vue's GlobalComponents type with the new element type.
262+
declare module 'vue' {
263+
interface GlobalComponents {
264+
// Be sure to pass in the Vue component type here (SomeComponent, not SomeElement).
265+
// Custom Elements require a hyphen in their name, so best to use the element name here.
266+
'some-element': typeof SomeComponent
267+
}
268+
}
269+
```
270+
271+
Here is how to define the type for any other custom element not made with Vue, assuming that such elements have type definitions:
272+
273+
```ts
274+
import {SomeElement} from 'some-lib'
275+
import type {Component} from 'vue'
276+
277+
// Some Custom Element libraries pre-define their elements. But if they require you to do it instead, remember to do that:
278+
// window.customElements.define('some-element', SomeElement)
252279

253-
// turn component into web components
254-
export const Counter = defineCustomElement(CounterSFC)
280+
// List the properties of the SomeElement class that should be exposed to Vue templates for type checking.
281+
type SomeElementAttributes = 'foo' | 'bar'
255282

256-
// register global typings
283+
// Add the new element type to Vue's GlobalComponents type.
257284
declare module 'vue' {
258-
export interface GlobalComponents {
259-
Counter: typeof Counter
285+
interface GlobalComponents {
286+
'some-element': Component<Partial<Pick<SomeElement, SomeElementAttributes>>>
260287
}
261288
}
262289
```
263290

291+
A current limitation of this approach for non-Vue custom elements is that event types are not included, for example props like `@some-event="..."` are currently all typed as `(event: Event) => void` without more specific even types. An `Event` can be downcasted into a more specific type as needed.
292+
293+
If an element does not have type definitions (for example when they are plain JS and the types are not inferrable by TypeScript), the types of the properties can be defined manually:
294+
295+
```ts
296+
import {SomeElement} from 'some-lib'
297+
import type {Component} from 'vue'
298+
299+
// Some Custom Element libraries pre-define their elements. But if they require you to do it instead, remember to do that:
300+
// window.customElements.define('some-element', SomeElement)
301+
302+
// Add the new element type to Vue's GlobalComponents type.
303+
declare module 'vue' {
304+
interface GlobalComponents {
305+
'some-element': Component<{foo?: number, bar?: string}>
306+
}
307+
}
308+
```
309+
310+
Custom Element library authors can include one or more `.d.ts` files that their users can import, which contain the augmentation so that users don't have to do it themselves. For example, the previous sample could be in a file named `SomeElement.vue-types.d.ts` which is sibling to the other files that the library ships, for Vue users to import in addition to the main file that defines the custom element class.
311+
264312
## Web Components vs. Vue Components {#web-components-vs-vue-components}
265313

266314
Some developers believe that framework-proprietary component models should be avoided, and that exclusively using Custom Elements makes an application "future-proof". Here we will try to explain why we believe that this is an overly simplistic take on the problem.

0 commit comments

Comments
 (0)