Mithril с TypeScript

Основы интеграции Mithril и TypeScript

Mithril — это легковесный фронтенд-фреймворк для построения одностраничных приложений (SPA) с минимальным объемом кода. Его ключевые особенности — виртуальный DOM, маршрутизация и упрощённая работа с компонентами. TypeScript обеспечивает строгую типизацию и контроль над кодом, что особенно важно при масштабировании проектов.

Для начала интеграции достаточно установить пакеты:

npm install mithril
npm install --save-dev typescript @types/mithril

TypeScript автоматически использует типы из @types/mithril, обеспечивая автодополнение и проверку типов.

Компоненты в TypeScript

Компоненты в Mithril — это объекты с методами view и опционально oninit, oncreate, onupdate, onremove. С TypeScript их типизация выглядит следующим образом:

import m, { Component, Vnode } from "mithril";

interface MyComponentAttrs {
    title: string;
    count?: number;
}

const MyComponent: Component<MyComponentAttrs> = {
    view: ({ attrs }: Vnode<MyComponentAttrs>) => {
        return m("div", [
            m("h1", attrs.title),
            m("p", `Count: ${attrs.count ?? 0}`)
        ]);
    }
};

Ключевые моменты:

  • attrs задают типизированные свойства компонента.
  • Vnode<MyComponentAttrs> гарантирует корректное использование атрибутов внутри компонента.
  • TypeScript проверяет обязательность и типы свойств.

Состояние и локальные данные

Состояние в Mithril обычно хранится внутри объекта компонента:

interface CounterState {
    count: number;
}

const Counter: Component<{}> = {
    oninit: (vnode) => {
        (vnode.state as CounterState).count = 0;
    },
    view: (vnode) => {
        const state = vnode.state as CounterState;
        return m("div", [
            m("button", {
                onclick: () => state.count++
            }, "Increment"),
            m("span", `Count: ${state.count}`)
        ]);
    }
};

Использование интерфейсов для состояния позволяет TypeScript отслеживать ошибки при доступе и модификации данных.

Модели и асинхронные запросы

Для работы с сервером используют стандартный m.request. Типизация данных важна для предотвращения ошибок:

interface User {
    id: number;
    name: string;
    email: string;
}

const loadUsers = (): Promise<User[]> => {
    return m.request<User[]>({
        method: "GET",
        url: "/api/users"
    });
};

Вызов m.request<User[]> гарантирует, что возвращаемые данные соответствуют интерфейсу User. Это позволяет безопасно работать с массивами объектов:

loadUsers().then(users => {
    users.forEach(user => console.log(user.name));
});

Маршрутизация

Mithril включает встроенный роутер. Типизация маршрутов помогает предотвратить ошибки при передаче параметров:

import m from "mithril";

interface RouteParams {
    id: string;
}

const UserPage: Component<RouteParams> = {
    view: ({ attrs }) => m("div", `User ID: ${attrs.id}`)
};

m.route(document.body, "/", {
    "/": HomePage,
    "/user/:id": UserPage
});

attrs автоматически содержит параметры маршрута с правильными типами, что исключает ошибки при доступе к attrs.id.

Кастомные хуки и утилиты

Для переиспользования логики удобно создавать функции-хуки с типами:

function useCounter(initial: number) {
    let count = initial;
    const increment = () => count++;
    const decrement = () => count--;
    return { get count() { return count }, increment, decrement };
}

const CounterHookComponent: Component = {
    view: () => {
        const counter = useCounter(5);
        return m("div", [
            m("button", { onclick: counter.decrement }, "-"),
            m("span", ` ${counter.count} `),
            m("button", { onclick: counter.increment }, "+")
        ]);
    }
};

TypeScript гарантирует, что возвращаемый объект всегда будет содержать корректные методы и геттер count.

Типизация событий

События в Mithril также типизируются через TypeScript. Например:

const InputComponent: Component = {
    view: () => m("input", {
        type: "text",
        oninput: (e: InputEvent) => {
            const target = e.target as HTMLInputElement;
            console.log(target.value);
        }
    })
};

Приведение e.target as HTMLInputElement позволяет безопасно обращаться к свойствам элемента.

Оптимизация производительности

Mithril эффективно обновляет виртуальный DOM. С TypeScript это облегчает внедрение мемоизации и контроля типов:

const MemoComponent: Component<{ text: string }> = {
    view: ({ attrs }) => m("div", attrs.text)
};

С использованием attrs TypeScript предотвращает ошибки при изменении структуры данных, что особенно важно при больших приложениях.

Итоговая структура проекта

Типичный проект на Mithril с TypeScript может иметь следующую структуру:

src/
├─ components/
│  ├─ Header.ts
│  ├─ Footer.ts
│  └─ UserList.ts
├─ models/
│  └─ User.ts
├─ pages/
│  ├─ HomePage.ts
│  └─ UserPage.ts
├─ routes.ts
└─ main.ts
  • components/ — переиспользуемые UI-блоки.
  • models/ — типизированные модели данных.
  • pages/ — страницы приложения, компоненты с роутами.
  • routes.ts — определение маршрутов с параметрами.
  • main.ts — точка входа, инициализация приложения.

Преимущества использования TypeScript с Mithril

  • Строгая типизация attrs, state и ответов от API.
  • Автодополнение и предупреждения о возможных ошибках.
  • Упрощение масштабирования крупных SPA.
  • Безопасная работа с событиями, состоянием и асинхронными данными.

TypeScript превращает Mithril в более предсказуемый и безопасный инструмент, сохраняя его минималистичность и высокую производительность.