Фреймворк Fresh, основанный на Deno и современном подходе к рендерингу на стороне сервера, предлагает гибкую систему плагинов, расширяющую возможности приложения. Одним из ключевых аспектов безопасной и масштабируемой разработки является корректная типизация этих плагинов. Типизация обеспечивает строгую проверку параметров, предсказуемое поведение и удобство автодополнения в редакторах.
Плагин в Fresh представляет собой функцию, которая может изменять
поведение приложения на уровне обработки запросов, маршрутизации или
рендеринга компонентов. Каждый плагин получает объект контекста
(ctx) и может возвращать модифицированный контекст или
новые свойства для приложения.
Типичная структура плагина:
import type { PluginContext } from "fresh/server.ts";
export function examplePlugin(ctx: PluginContext) {
// Модификация контекста
ctx.state.example = true;
return ctx;
}
Ключевой момент: объект PluginContext содержит всю
необходимую информацию о текущем запросе, состоянии приложения и его
конфигурации. Типизация контекста позволяет строго определять, какие
свойства допустимы и какие методы можно использовать.
Для обеспечения полной типизации рекомендуется определять интерфейсы и типы, которые отражают структуру данных, добавляемых плагином:
interface ExampleState {
user?: { id: string; name: string };
isAuthenticated: boolean;
}
declare module "fresh/server.ts" {
interface PluginContext {
state: ExampleState;
}
}
export function authPlugin(ctx: PluginContext) {
const token = ctx.request.headers.get("Authorization");
ctx.state.isAuthenticated = !!token;
return ctx;
}
Расширение встроенного интерфейса PluginContext через
declare module позволяет интегрировать новые свойства
напрямую в контекст, сохраняя строгую типизацию на уровне
TypeScript.
Некоторые плагины требуют конфигурационных параметров. В Fresh это оформляется через дженерики или отдельные интерфейсы:
interface LoggerOptions {
level: "info" | "warn" | "error";
timestamp?: boolean;
}
export function loggerPlugin(options: LoggerOptions) {
return (ctx: PluginContext) => {
if (options.timestamp) {
console.log(`[${new Date().toISOString()}]`);
}
console.log(`[${options.level}] Request: ${ctx.request.url}`);
return ctx;
};
}
Использование типов для параметров позволяет гарантировать корректность передаваемых настроек и предотвращает ошибки при конфигурации плагина.
В сложных приложениях плагины часто комбинируются. Fresh поддерживает функциональный подход к композиции:
function composePlugins(...plugins: Array<(ctx: PluginContext) => PluginContext>) {
return (ctx: PluginContext) => plugins.reduce((acc, plugin) => plugin(acc), ctx);
}
const appPlugin = composePlugins(authPlugin, loggerPlugin({ level: "info", timestamp: true }));
Типизация сохраняется благодаря единообразному интерфейсу
PluginContext, что позволяет безопасно комбинировать
плагины и расширять их функциональность.
any: Использование
any подрывает преимущества TypeScript. Все новые свойства
лучше описывать через интерфейсы.Плагины напрямую влияют на серверный контекст, который используется компонентами Fresh. Типизированный контекст обеспечивает корректную работу хуков и серверных функций:
export default function Page({ ctx }: { ctx: PluginContext }) {
return (
<div>
{ctx.state.isAuthenticated ? `User is logged in` : `Guest`}
</div>
);
}
Типизация позволяет безопасно обращаться к свойствам
state и использовать данные, добавленные плагинами, без
необходимости ручных проверок типов.
Для удобства и повторного использования можно создавать фабрики плагинов с предопределённой типизацией:
function createFeaturePlugin<T extends object>(feature: T) {
return (ctx: PluginContext & { state: T }) => {
Object.assign(ctx.state, feature);
return ctx;
};
}
const analyticsPlugin = createFeaturePlugin({ analyticsEnabled: true });
Такой подход позволяет создавать расширяемые и типобезопасные плагины, которые легко комбинируются и поддерживаются в масштабных приложениях.
Типизация плагинов в Fresh — это не просто инструмент для предотвращения ошибок. Это механизм построения предсказуемой архитектуры, где каждый плагин строго определяет свою область ответственности, а взаимодействие между ними становится безопасным и прозрачным.