TypeScript в связке с библиотекой Iron используется не как дополнительный слой документации, а как полноценный механизм контроля структуры приложения на этапе компиляции. Основная цель типизации — обеспечить предсказуемость API компонентов, реактивных сущностей и внутренних утилит, исключая целый класс ошибок, связанных с несовпадением контрактов.
В Iron типы распределяются по нескольким уровням: компоненты, свойства, события, состояния и расширения модулей. Каждый уровень имеет собственные правила описания и взаимодействия.
В большинстве проектов типы подключаются автоматически через пакет библиотеки. Однако в случае ручной конфигурации TypeScript требуется убедиться, что компилятор видит декларации:
{
"compilerOptions": {
"strict": true,
"moduleResolution": "node",
"types": ["iron"]
}
}
Если библиотека поставляется без встроенных типов, используются внешние декларации:
npm install @types/iron
или локальные файлы .d.ts, добавляемые в проект
вручную.
Компоненты в Iron описываются через обобщённые интерфейсы. Базовая структура компонента опирается на разделение входных параметров и возвращаемого представления.
interface ComponentProps {
title: string;
count?: number;
}
function Component(props: ComponentProps): JSX.Element {
return /* ... */;
}
Важным аспектом является строгая фиксация входных данных. Отсутствие необязательных свойств компенсируется значениями по умолчанию внутри логики компонента.
Props в Iron не ограничиваются простыми объектами. Они могут включать вложенные структуры, функции обратного вызова и реактивные значения.
interface ButtonProps {
label: string;
disabled?: boolean;
onClick: (event: MouseEvent) => void;
}
Допускается использование union-типов для создания гибких интерфейсов:
type Size = "small" | "medium" | "large";
interface AvatarProps {
size: Size;
}
Такой подход позволяет ограничить набор допустимых значений на уровне компиляции.
События в Iron описываются через обобщённые сигнатуры. Это особенно важно при работе с DOM и кастомными событиями.
type EventHandler<T = Event> = (event: T) => void;
interface InputProps {
onInput: EventHandler<InputEvent>;
onFocus?: EventHandler<FocusEvent>;
}
Кастомные события требуют явного описания структуры payload:
interface UserEvent {
id: number;
action: string;
}
type UserEventHandler = (event: UserEvent) => void;
Реактивные сущности в Iron типизируются через обобщённые контейнеры состояния. Основной принцип — сохранение строгого соответствия между значением и его типом на протяжении всего жизненного цикла.
interface State<T> {
value: T;
set: (value: T) => void;
}
Использование:
const counter: State<number>;
const user: State<{ name: string; age: number }>;
Такой подход предотвращает неявные преобразования типов внутри реактивной системы.
Обобщения позволяют создавать переиспользуемые компоненты без потери типобезопасности.
interface ListProps<T> {
items: T[];
render: (item: T) => string;
}
function List<T>(props: ListProps<T>) {
return /* ... */;
}
При использовании тип выводится автоматически:
<List items={[1, 2, 3]} render={(n) => n.toString()} />;
Iron поддерживает расширение существующих интерфейсов через декларативное слияние модулей. Это используется для добавления пользовательских свойств в глобальные типы или API библиотеки.
declare module "iron" {
interface GlobalConfig {
debug: boolean;
}
}
Подобный механизм позволяет интегрировать библиотеку в крупные архитектуры без модификации исходного кода.
Для описания глобальных сущностей используются ambient declarations:
declare const __DEV__: boolean;
или расширение глобального пространства:
declare global {
interface Window {
ironApp: unknown;
}
}
Такие объявления особенно важны при интеграции с legacy-кодом.
Файлы деклараций служат контрактом между JavaScript-реализацией и TypeScript-окружением. Структура типичного файла:
export interface IronConfig {
root: string;
strictMode: boolean;
}
export declare function createApp(config: IronConfig): void;
Ключевое требование — отсутствие реализации внутри
.d.ts. Только описание структуры.
Утилиты в Iron часто строятся на базе generics и conditional types.
type Maybe<T> = T | null;
function wrap<T>(value: T): Maybe<T> {
return value;
}
Для сложных преобразований используются условные типы:
type IsString<T> = T extends string ? true : false;
Контекст в Iron описывается как строго типизированное хранилище зависимостей.
interface Context {
theme: "light" | "dark";
userId: number;
}
const AppContext: Context;
Доступ к контексту всегда сопровождается проверкой типов, что исключает возможность обращения к несуществующим полям.
При подключении JavaScript-модулей используется декларация через
any или постепенное уточнение типов.
declare module "legacy-lib" {
const legacyFunction: (input: any) => any;
export default legacyFunction;
}
Постепенная миграция позволяет внедрять строгую типизацию без переписывания всей кодовой базы.
Типовая система Iron не рассматривается как вспомогательный инструмент. Она формирует архитектурный контракт между слоями приложения. Любое нарушение типов фиксируется до выполнения кода, что позволяет переносить сложность из runtime в compile-time.
Использование интерфейсов, generics, деклараций и module augmentation формирует единый типовой слой, в котором каждая сущность имеет строго определённую структуру и поведение.