В Fresh компоненты создаются как функции, возвращающие JSX. Для достижения строгой типизации используется TypeScript. Пропсы описываются интерфейсами или типами:
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
export default function Button({ label, onClick, disabled = false }: ButtonProps) {
return (
<button onCl ick={onClick} disabled={disabled}>
{label}
</button>
);
}
Ключевой момент: использование опциональных свойств
через ? позволяет задавать значения по умолчанию и
сохранять строгую проверку типов при использовании компонента.
Generic-параметры позволяют создавать универсальные компоненты, которые остаются типобезопасными при различных данных:
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => JSX.Element;
}
export function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map(renderItem)}
</ul>
);
}
Использование generics расширяет возможности переиспользования кода, не жертвуя безопасностью типов. Применение универсальных компонентов особенно важно при работе с API и динамическими данными.
Fresh поддерживает типизацию событий через стандартные типы DOM:
function InputField() {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
return <input type="text" onCha nge={handleChange} />;
}
Использование точных типов событий (MouseEvent,
KeyboardEvent, ChangeEvent) обеспечивает
автодополнение и предотвращает ошибки при доступе к свойствам
события.
Для стейт-переменных в Fresh через Deno и Preact используются хуки, которые можно типизировать явно:
import { useState } from "preact/hooks";
function Counter() {
const [count, setCount] = useState<number>(0);
return (
<div>
<p>{count}</p>
<button onCl ick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
Явная типизация стейта предотвращает неожиданные преобразования типов и ошибки при обновлении состояния.
Использование Partial, Required,
Pick, Omit позволяет создавать гибкие и
безопасные типы для компонентов:
interface User {
id: string;
name: string;
email?: string;
}
// Только обязательные поля
type MandatoryUser = Required<User>;
// Исключить поле email
type UserWithoutEmail = Omit<User, "email">;
Эти утилиты помогают минимизировать дублирование кода при изменении структуры данных и поддерживают строгую типизацию на уровне компиляции.
При работе с API важно точно определять тип возвращаемых данных:
interface Post {
id: number;
title: string;
body: string;
}
async function fetchPosts(): Promise<Post[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const data: Post[] = await res.json();
return data;
}
Явное указание типа данных предотвращает ошибки при обработке ответа и обеспечивает поддержку автокомплита.
Fresh поддерживает создание контекста для управления состоянием приложения:
import { createContext } from "preact";
import { useContext } from "preact/hooks";
interface AuthContextValue {
user: string | null;
login: (username: string) => void;
}
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error("useAuth must be used within AuthProvider");
return context;
}
Типизация контекста повышает надежность архитектуры приложения и обеспечивает корректную интеграцию хуков.
TypeScript позволяет создавать условные пропсы с помощью union-типов:
interface SuccessProps {
status: "success";
message: string;
}
interface ErrorProps {
status: "error";
code: number;
}
type AlertProps = SuccessProps | ErrorProps;
function Alert(props: AlertProps) {
if (props.status === "success") {
return <div>{props.message}</div>;
}
return <div>Error code: {props.code}</div>;
}
Такой подход обеспечивает строгую проверку значений и предотвращает некорректное использование компонентов.
Fresh использует функции handler для обработки
HTTP-запросов. Типизация запроса и ответа повышает надежность:
import type { Handlers } from "$fresh/server.ts";
export const handler: Handlers<{ name: string }> = {
GET(req, ctx) {
return new Response(JSON.stringify({ name: "Fresh" }), {
headers: { "Content-Type": "application/json" },
});
},
};
Типизация Handlers гарантирует, что структура ответа
соответствует ожидаемым типам, предотвращая ошибки на клиентской
стороне.
При использовании сторонних пакетов без типов можно создавать свои определения:
declare module "some-legacy-library" {
export function legacyFunction(arg: string): number;
}
Это позволяет интегрировать сторонние модули в строго типизированный проект Fresh без потери безопасности типов и автокомплита.
Эти паттерны типизации обеспечивают высокую надёжность, удобство сопровождения и масштабируемость приложений на Fresh с использованием TypeScript. Они позволяют строить комплексные компоненты, безопасно взаимодействовать с API и управлять состоянием с полной уверенностью в корректности типов.