TypeScript
Type app contexts, component inputs, and runtime contracts without losing reactive behavior.
Regor has strong TypeScript support across app context, component context, and component head typing.
Make state and behavior explicit in your types so the editor can help you compose them.
Why this matters
You can model app and component state with real TypeScript types (interfaces/classes), and keep runtime template syntax concise.
In template expressions, Regor resolves refs automatically, so you write user.name instead of user.name.value or user.name().
Typed app context with interfaces
import { createApp, html, type IRegorContext, type Ref, ref } from 'regor'
interface TodoItem {
id: number
label: string
}
interface MyApp extends IRegorContext {
title: Ref<string>
items: Ref<TodoItem[]>
}
createApp<MyApp>(
{
title: ref('Todos'),
items: ref([{ id: 1, label: 'Ship' }]),
},
{
element: document.querySelector('#app')!,
template: html`<h1 r-text="title"></h1>
<li r-for="item in items" :key="item.id" r-text="item.label"></li>`,
},
) Typed class app context with useScope
import { createApp, html, ref, useScope } from 'regor'
class BoardApp {
query = ref('')
count = ref(0)
increment = () => this.count(this.count() + 1)
}
createApp(
useScope(() => new BoardApp()),
{
element: document.querySelector('#app')!,
template: html`<input r-model="query" />
<button @click="increment">+1</button>
<p>{{ query }} - {{ count }}</p>`,
},
) Typed component props and context with ComponentHead<TProps>
import { ComponentHead, defineComponent, html, type Ref, ref } from 'regor'
type CardProps = {
title: Ref<string>
}
class CardContext {
title: Ref<string>
selected = ref(false)
constructor(head: ComponentHead<CardProps>) {
this.title = head.props.title
}
}
const Card = defineComponent<CardContext>(
html`<article :class="{ selected: selected }">
<h3 r-text="title"></h3>
</article>`,
{
props: ['title'],
context: (head) => new CardContext(head),
},
) <Card :title="user.name"></Card> TypeScript checks your code during development. Use head.validateProps when component inputs also need a runtime contract.
Typed runtime prop validation with head.validateProps(...)
validateProps(...) is typed from ComponentHead<T>, so editor completion can suggest known prop keys while you write the schema.
Use the built-in validators through pval:
import { ComponentHead, defineComponent, html, pval } from 'regor'
type EditorCard = {
title: string
count?: number
mode: 'create' | 'edit'
summary?: string
}
const EditorCard = defineComponent<EditorCard>(
html`<article>{{ summary }}</article>`,
{
props: ['title', 'count', 'mode'],
context: (head: ComponentHead<EditorCard>) => {
head.validateProps({
title: pval.isString,
count: pval.optional(pval.isNumber),
mode: pval.oneOf(['create', 'edit'] as const),
})
return {
...head.props,
summary: `${head.props.title}:${head.props.mode}:${head.props.count ?? 'none'}`,
}
},
},
) For dynamic single-prop bindings that arrive as refs, type the prop accordingly and validate with pval.refOf(...):
import { ComponentHead, defineComponent, html, pval, type Ref } from 'regor'
type TitleCard = {
title: Ref<string>
summary?: string
}
const TitleCard = defineComponent<TitleCard>(
html`<h3>{{ summary }}</h3>`,
{
props: ['title'],
context: (head: ComponentHead<TitleCard>) => {
head.validateProps({
title: pval.refOf(pval.isString),
})
return {
...head.props,
summary: head.props.title(),
}
},
},
) You can also write custom validators:
import { pval, type PropValidator } from 'regor'
const isNonEmptyString: PropValidator<string> = (value, name) => {
if (typeof value !== 'string' || value.trim() === '') {
pval.fail(name, `expected non-empty string, ${pval.describe(value)}`)
}
} Typed class components and behavior controls
You can type head in class constructors and use runtime behavior flags intentionally:
head.autoPropsto disable automatic parent-input assignment.head.entangleto choose two-way ref sync vs snapshot behavior.head.enableSwitchfor parent-context slot evaluation.head.findContext(ServiceClass, occurrence?)for optional parent context lookup.head.requireContext(ServiceClass, occurrence?)for required parent context lookup.
Example:
class AppServices {
readonly apiBase = '/v1'
}
class ChildContext {
apiBase: string
constructor(head: ComponentHead<object>) {
const services = head.requireContext(AppServices)
this.apiBase = services.apiBase
}
}