Skip to content

Typed props and emits

Problem

You need both prop and event contracts checked at the type level. Ore's define<Props>() generic types prop shapes; useEmit<Events>() types emitted event payloads.

Solution

Combine prop.* helpers on define() with a typed useEmit<Events>() call in setup().

ts
import { define, html, prop, useEmit, when } from '@vielzeug/ore';

type AlertBoxProps = {
  message: string;
  open: boolean;
  variant: 'primary' | 'danger';
};

type AlertBoxEvents = {
  close: void;
  change: { open: boolean };
};

define<AlertBoxProps>('alert-box', {
  props: {
    message: prop.string('Saved successfully'),
    open: prop.bool(true),
    variant: prop.oneOf(['primary', 'danger'] as const, 'primary'),
  },
  setup(props) {
    const emit = useEmit<AlertBoxEvents>();
    const close = () => {
      if (!props.open.value) return;

      props.open.value = false;
      emit('change', { open: props.open.value });
      emit('close');
    };

    return html`
      ${when(
        () => props.open.value,
        () => html`
          <div data-variant=${props.variant}>
            <span>${props.message}</span>
            <button @click=${close}>Close</button>
          </div>
        `,
      )}
    `;
  },
});

Pitfalls

  • Omitting as const on prop.oneOf(...) widens the type to string, losing the union constraint.
  • Prop values in setup() are writable signals. Mutating props.open.value = false works but only updates local state — it does not reflect back to the parent's attribute unless reflect: true is set (the default for prop.bool).