Type guards — это механизм в TypeScript, который позволяет точно определить тип переменной в определённом контексте кода. В фреймворке Fresh, построенном на Deno и поддерживающем TypeScript из коробки, использование type guards особенно полезно при работе с данными из API, форм, параметров маршрутов и состояния компонентов.
Type guard — это выражение, которое сужает тип
переменной внутри блока кода. Это необходимо, когда переменная
имеет объединённый тип (union type) или тип
any, и нужно гарантировать безопасность операций с ней.
Пример базового type guard:
function isString(value: unknown): value is string {
return typeof value === "string";
}
const input: unknown = "Fresh";
if (isString(input)) {
console.log(input.toUpperCase()); // Тип безопасно сузился до string
}
Ключевой момент: конструкция value is string в
определении функции позволяет TypeScript понимать, что после проверки
переменная имеет конкретный тип.
Fresh активно использует функции обработки запросов и
компонентный рендеринг, где данные приходят в виде параметров
props или Request. Type guards помогают
работать с ними безопасно.
import { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
GET(req, ctx) {
const url = new URL(req.url);
const idParam = url.searchParams.get("id");
if (isString(idParam)) {
return new Response(`ID: ${idParam}`);
} else {
return new Response("ID не указан", { status: 400 });
}
},
};
function isString(value: unknown): value is string {
return typeof value === "string";
}
Здесь type guard гарантирует, что переменная idParam
действительно строка, прежде чем выполнять с ней операции.
TypeScript предоставляет несколько встроенных возможностей для создания type guards без написания функций:
typeof Проверяет примитивные типы:
string, number, boolean,
symbol, bigint, undefined,
function.
const value: string | number = 42;
if (typeof value === "number") {
console.log(value + 1); // безопасно
}instanceof Проверяет, является ли объект экземпляром класса или конструктора.
class User {
constructor(public name: string) {}
}
const maybeUser: User | null = new User("Alice");
if (maybeUser instanceof User) {
console.log(maybeUser.name);
}in Проверяет наличие свойства в объекте. Особенно полезно для discriminated unions.
type Admin = { role: "admin"; privileges: string[] };
type Guest = { role: "guest"; visitingSince: Date };
function printInfo(user: Admin | Guest) {
if ("privileges" in user) {
console.log(user.privileges.join(", "));
} else {
console.log(user.visitingSince.toDateString());
}
}Discriminated unions — это объединения типов с общим “дискриминатором”, который используется для безопасного сужения типа. Fresh часто работает с такими структурами при обработке состояния компонентов.
type SuccessResponse = { status: "success"; dat a: string[] };
type ErrorResponse = { status: "error"; message: string };
function handleResponse(resp: SuccessResponse | ErrorResponse) {
if (resp.status === "success") {
console.log(resp.data.join(", "));
} else {
console.error(resp.message);
}
}
Здесь resp.status является дискриминатором, а проверка
resp.status === "success" служит type guard.
В Fresh, при работе с динамическими данными, часто создаются кастомные type guards:
interface Product {
id: number;
name: string;
price: number;
}
function isProduct(obj: unknown): obj is Product {
return typeof obj === "object" &&
obj !== null &&
"id" in obj &&
"name" in obj &&
"price" in obj;
}
const data: unknown = fetch("/api/product").then(res => res.json());
data.then(item => {
if (isProduct(item)) {
console.log(item.name, item.price);
}
});
Такой подход предотвращает ошибки при работе с внешними API и динамическими объектами.
При рендеринге компонентов можно использовать type guards для различения типов данных в props:
interface TextBlock {
type: "text";
content: string;
}
interface ImageBlock {
type: "image";
src: string;
}
type Block = TextBlock | ImageBlock;
export default function BlockRenderer({ block }: { block: Block }) {
if (block.type === "text") {
return <p>{block.content}</p>;
} else {
return <img src={block.src} alt="" />;
}
}
Такой подход позволяет безопасно использовать свойства каждого варианта union, избегая ошибок времени выполнения.
type, kind,
status) для union типов.typeof,
instanceof, in) с кастомными проверками для
максимальной безопасности.Type guards являются мощным инструментом, который позволяет совмещать динамику JavaScript с безопасностью TypeScript. В Fresh их применение обеспечивает стабильность и предсказуемость работы с props, API и состоянием компонентов, что особенно важно при построении масштабируемых приложений.