Фреймворк Fresh строится на основе Deno, JSX и реактивного подхода к построению интерфейсов. Одной из ключевых возможностей является создание generic компонентов, которые позволяют писать переиспользуемый код с параметризуемыми типами данных, обеспечивая строгую типизацию и предсказуемое поведение.
Generic компонент — это компонент, который принимает тип-параметр. В JavaScript это реализуется через TypeScript, позволяя уточнять тип пропсов при использовании компонента. Синтаксис выглядит так:
interface ListProps<T> {
items: T[];
renderItem: (item: T) => JSX.Element;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
T — параметр типа, который задается при использовании
компонента.items — массив элементов типа T.renderItem — функция, которая возвращает JSX для
каждого элемента массива.Generic компонент можно применять для различных типов данных, сохраняя строгую типизацию:
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
<List<User>
items={users}
renderItem={(user) => <span>{user.name}</span>}
/>
Здесь T автоматически заменяется на User, и
TypeScript гарантирует, что функция renderItem получает
объект с правильной структурой.
Иногда требуется использовать несколько типов. Fresh поддерживает это напрямую:
interface PairProps<T, U> {
first: T;
second: U;
renderPair: (first: T, second: U) => JSX.Element;
}
function Pair<T, U>({ first, second, renderPair }: PairProps<T, U>) {
return <div>{renderPair(first, second)}</div>;
}
Пример использования:
<Pair<number, string>
first={42}
second={"Answer"}
renderPair={(num, str) => <span>{num} - {str}</span>}
/>
TypeScript позволяет накладывать ограничения на типы
параметров через extends:
interface Identifiable {
id: number;
}
interface IdentifiableListProps<T extends Identifiable> {
items: T[];
}
function IdentifiableList<T extends Identifiable>({ items }: IdentifiableListProps<T>) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{JSON.stringify(item)}</li>
))}
</ul>
);
}
Такой подход гарантирует, что все объекты имеют поле id,
что делает компонент более строгим и безопасным.
Fresh использует JSX для описания структуры компонентов. Generic компоненты интегрируются полностью с JSX, включая вложенные компоненты и условный рендеринг:
function Card<T>({ data, renderContent }: { data: T; renderContent: (data: T) => JSX.Element }) {
return <div class="card">{renderContent(data)}</div>;
}
<Card data={{ title: "Hello", description: "Fresh Framework" }} renderContent={(d) => (
<>
<h1>{d.title}</h1>
<p>{d.description}</p>
</>
)}/>
Тип T в этом примере позволяет компоненту работать с
любыми объектами, обеспечивая максимальную гибкость без потери
типизации.
extends.Использование generic компонентов является фундаментальной практикой при построении масштабируемых приложений на Fresh, позволяя создавать мощные и универсальные UI-структуры.