TypeScript
Ornata was designed to be TypeScript-friendly. For the best TypeScript experience, define the component’s typed parts explicitly.
Type component parts explicitly
Section titled “Type component parts explicitly”Define the parts of the internal component instance (this) explicitly when you want stronger typing throughout the component. This allows the types for state, elements, methods, data, or computed to flow through wherever those parts are referenced.
interface CounterState { count: number; label: string;}
const Counter = defineComponent<{ state: CounterState; methods: { increment(): void; }; computed: { summary: string; };}>({ name: 'Counter', state: { count: { default: 0 }, label: { default: 'Clicks' }, }, methods: { increment() { this.state.count += 1; }, }, computed: { summary() { return `${this.state.label}: ${this.state.count}`; }, },});Type state as part of the component API
Section titled “Type state as part of the component API”Explicit state types do more than improve authoring inside the component. They also shape the public contract that downstream code works with through the constructor, mounted instance, and state listeners.
interface DisclosureState { /** Whether the disclosure is currently expanded. */ open: boolean;
/** Text announced to external code and visible UI. */ label: string;}
const Disclosure = defineComponent<{ state: DisclosureState;}>({ state: { open: { default: false }, label: { default: 'Details' }, },});That means well-typed state and good JSDoc comments can improve:
- authoring inside the component through clearer
this.stateaccess - mounted instance usage through better
instance.stateautocomplete and docs - state listener usage through clearer property names and value types
Extract types from a constructor
Section titled “Extract types from a constructor”Use the following utilites to extract the instance or state type from a component.
type DisclosureInstance = Ornata.InferComponentInstance<typeof Disclosure>;
type DisclosureState = Ornata.InferComponentState<typeof Disclosure>;Typing elements with resolve()
Section titled “Typing elements with resolve()”When you use resolve() to return a custom element shape or a narrower DOM type, cast the returned value so it matches the type declared for that element on the component.
interface LiveFilterState { query: string;}
const LiveFilter = defineComponent<{ state: LiveFilterState; elements: { input: HTMLInputElement | null; items: HTMLElement[]; };}>({ state: { query: { default: '' }, }, elements: { input: { resolve(root) { const input = root.querySelector('[data-filter-input]'); return input as HTMLInputElement | null; }, }, items: { resolve(root) { const items = root.querySelectorAll('[data-filter-item]'); return Array.from(items) as HTMLElement[]; }, }, },});A good mental model
Section titled “A good mental model”In Ornata, types are part of the component contract. When you type a component explicitly, you are not only improving authoring inside the component itself. You are also shaping the API that downstream code will interact with.