Skip to content

JSX and TypeScript

Typed components, and JSX when you have a build step.

TypeScript

Signy is written in TypeScript and ships its types. Signals infer their type from the first value. A component takes its props as a type parameter and marks render() and setup() as protected.

import { Component, Signal, html } from 'signy';

interface BadgeProps {
  label: string;
}

class Badge extends Component<BadgeProps> {
  private clicks = new Signal(0);

  protected render() {
    return html`
      <button onClick=${() => this.clicks.set((c) => c + 1)}>
        ${this.props.label} (${() => this.clicks.value})
      </button>
    `;
  }
}

Give a signal an explicit type when the first value does not say enough.

const user = new Signal<User | null>(null);

JSX

JSX is compiled by babel-plugin-jsx-dom-expressions, the same compiler Solid uses. Calling jsx() at runtime throws, so setting jsxImportSource alone is not enough.

// babel.config.js
module.exports = {
  plugins: [
    ['babel-plugin-jsx-dom-expressions', {
      moduleName: 'signy/jsx',
      generate: 'dom',
      delegateEvents: true,
      wrapConditionals: true,
    }],
  ],
};

With the compiler, {count.value} in JSX is reactive because the compiler wraps it in a function. The other rules hold: render() runs once and lists go through For.

class Counter extends Component {
  private count = new Signal(0);

  protected render() {
    return (
      <button onClick={() => this.count.set((c) => c + 1)}>
        Clicked {this.count.value} times
      </button>
    );
  }
}