Support Ukraine

Velo By Wix: Types definitions for Custom Components

How to add support type checking for DOM elements in Velo using TypeScript Triple-Slash Directives

computer scene in the 'johnny mnemonic' movie

Velo editor has the built-in TypeScript compiler for type checking and code auto-completion. In the previous two posts, we looked at how to use JSDoc types annotations for Velo elements. And how to create global types.

  1. Type safety your code with JSDoc
  2. Global type definitions
code completion in velo editor
Example of code autocomplete

In this post, we look at how to add type support for Custom Components development when we use Browser APIs like the window, document, customElements, etc.

DOM types support

In Velo, we have built-in support for DOM types. The DOM types are disabled by default. We can turn on it with the special JavaScript comment. TypeScript uses this comment as compiler directives.

The TypeScript Handbook: Triple-Slash Directives
Triple-slash directives are single-line comments containing a single XML tag. The contents of the comment are used as compiler directives.

All we need to do it's add a Triple-slash directive to the top of the file where we will use DOM APIs.

Add DOM types checking in Velo editor

/// <reference lib="dom" />

With this directive, TypeScript starts to use DOM types. 🙌

Using Triple-Slash Directives in Velo

It provides a better development experience and saves a lot of time.

Example of use

TypeScript directives are open to us writing the Custom Component code in public files. We can move part of the code to a public file and reuse it for a few Custom Components.

Just try the next code in your project:

There is a util file with a function for creating a new HTML element. We can add DOM types annotations to provide type information about function signature.

public/domUtils.js

/// <reference lib="dom" />

/**
 * @template {keyof HTMLElementTagNameMap} T
 * @param {T} tagName
 * @param {Partial<HTMLElementTagNameMap[T]>} [attrs]
 * @returns {HTMLElementTagNameMap[T]}
 */
export const createElement = (tagName, attrs) => {
  return Object.assign(document.createElement(tagName), attrs);
}

We can ensure that the util function covers DOM types.

public/custom-elements/my-button.js

import { createElement } from 'public/domUtils';

const css = `
.my-button {
  border: 1px solid #000;
  padding: 10px 20px;
  cursor: pointer;
}
`;

class MyButton extends HTMLElement {
  constructor() {
    super();

    const style = createElement('style', {
      textContent: css,
    });

    const button = createElement('button', {
      type: 'button',
      textContent: 'Click Me',
      className: 'my-button',
      onclick: () => {
        this.dispatchEvent(new CustomEvent('click'));
      },
    });

    this.attachShadow({ mode: 'open' }).append(style, button);
  }
}

customElements.define('my-button', MyButton);

Posts