10 промтов для генерации JavaScript и TypeScript кода: React, Node.js, утилиты
Генерация кода с помощью ИИ — не футуризм, а повседневная реальность для разработчиков. Согласно опросу Stack Overflow 2025 года, более 60% респондентов используют AI-инструменты для написания кода, особенно для JavaScript и TypeScript. Эти языки — основа современного веба, от React-приложений до Node.js-серверов. Но чтобы ИИ выдавал качественный код, нужно правильно формулировать промты. В этой подборке — 10 проверенных шаблонов, которые я использую в работе. Каждый промт включает проблему, решение и пример кода.
1. Генерация React-компонента с TypeScript
Проблема: Нужно быстро создать типизированный компонент с обработчиками событий и пропсами. Решение: Промт для генерации компонента с явными типами. Результат: Экономия времени на шаблонном коде.
Промт: «Создай React-компонент на TypeScript для отображения списка задач (TodoList). Компонент принимает массив объектов { id: number, title: string, completed: boolean } и функцию onToggle для изменения статуса. Используй useState для локального состояния, если нужно. Добавь стили через CSS-in-JS (styled-components).»
Код:
import React, { useState } from 'react';
import styled from 'styled-components';
interface Todo {
id: number;
title: string;
completed: boolean;
}
interface TodoListProps {
todos: Todo[];
onToggle: (id: number) => void;
}
const List = styled.ul`
list-style: none;
padding: 0;
`;
const Item = styled.li<{ completed: boolean }>`
text-decoration: ${props => props.completed ? 'line-through' : 'none'};
cursor: pointer;
`;
const TodoList: React.FC<TodoListProps> = ({ todos, onToggle }) => {
return (
<List>
{todos.map(todo => (
<Item key={todo.id} completed={todo.completed} onClick={() => onToggle(todo.id)}>
{todo.title}
</Item>
))}
</List>
);
};
export default TodoList;
2. Создание Express-маршрута с валидацией
Проблем: Нужен REST API эндпоинт с проверкой входных данных. Решение: Промт для генерации маршрута с Joi-валидацией. Результат: Безопасный код без ручного написания проверок.
Промт: «Напиши Express.js роутер на TypeScript для POST /api/users. В теле запроса: name (строка, обязательное), email (строка, валидный email). Используй библиотеку Joi для валидации. При ошибке верни статус 400 и сообщение. Добавь async/await и обработку ошибок через try/catch.»
Код:
import { Router, Request, Response } from 'express';
import Joi from 'joi';
const router = Router();
const schema = Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
});
router.post('/users', async (req: Request, res: Response) => {
try {
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({ message: error.details[0].message });
}
// Здесь логика создания пользователя
res.status(201).json({ id: Date.now(), ...value });
} catch (err) {
res.status(500).json({ message: 'Internal server error' });
}
});
export default router;
3. Утилита для глубокого клонирования объекта
Проблем: Нужна надёжная функция без внешних зависимостей. Решение: Промт для реализации с учётом краевых случаев. Результат: Готовая утилита для продакшена.
Промт: «Напиши функцию на TypeScript для глубокого клонирования объекта. Обработай: вложенные объекты, массивы, null, Date, RegExp, Map, Set. Не используй JSON.parse/JSON.stringify. Используй рекурсию. Типизируй возвращаемое значение.»
Код:
function deepClone<T>(obj: T, hash = new WeakMap()): T {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
if (obj instanceof Date) return new Date(obj) as any;
if (obj instanceof RegExp) return new RegExp(obj) as any;
if (obj instanceof Map) {
const clone = new Map();
obj.forEach((val, key) => clone.set(key, deepClone(val, hash)));
hash.set(obj, clone);
return clone as any;
}
if (obj instanceof Set) {
const clone = new Set();
obj.forEach(val => clone.add(deepClone(val, hash)));
hash.set(obj, clone);
return clone as any;
}
const cloneObj = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
(cloneObj as any)[key] = deepClone((obj as any)[key], hash);
}
}
return cloneObj as T;
}
4. Хук для работы с localStorage в React
Проблем: Синхронизация состояния с localStorage при изменениях. Решение: Кастомный хук с подпиской на storage event. Результат: Удобный инструмент для кэширования на клиенте.
Промт: «Создай кастомный React хук useLocalStorage на TypeScript. Принимает ключ и начальное значение. Возвращает [value, setValue]. При изменении значения обновляй localStorage и синхронизируй между вкладками через window.addEventListener('storage'). Типизируй с дженериками.»
Код:
import { useState, useEffect, useCallback } from 'react';
function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = useCallback((value: T | ((prev: T) => T)) => {
setStoredValue(prev => {
const newValue = value instanceof Function ? value(prev) : value;
window.localStorage.setItem(key, JSON.stringify(newValue));
return newValue;
});
}, [key]);
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
if (e.key === key && e.newValue) {
setStoredValue(JSON.parse(e.newValue));
}
};
window.addEventListener('storage', handleStorageChange);
return () => window.removeEventListener('storage', handleStorageChange);
}, [key]);
return [storedValue, setValue];
}
export default useLocalStorage;
5. Генерация тестов для функции
Проблем: Нужно быстро покрыть код unit-тестами. Решение: Промт для Jest с иcпользованием describe/it. Результат: Набор тестов, проверяющих граничные случаи.
Промт: «Напиши тесты на Jest для функции deepClone из предыдущего примера. Проверь: клонирование примитивов, вложенных объектов, массивов, Date, Map, Set. Убедись, что изменения в клоне не влияют на оригинал. Используй describe и it.»
Код:
describe('deepClone', () => {
it('should clone primitive values', () => {
expect(deepClone(42)).toBe(42);
expect(deepClone('hello')).toBe('hello');
});
it('should clone nested objects', () => {
const obj = { a: { b: 1 } };
const clone = deepClone(obj);
clone.a.b = 2;
expect(obj.a.b).toBe(1);
});
it('should clone Date', () => {
const date = new Date();
const clone = deepClone(date);
expect(clone).toEqual(date);
expect(clone).not.toBe(date);
});
it('should clone Map', () => {
const map = new Map([[1, 'a']]);
const clone = deepClone(map);
clone.set(2, 'b');
expect(map.size).toBe(1);
});
});
6. Асинхронный обработчик ошибок для Express
Проблем: Повторяющийся код try/catch в каждом маршруте. Решение: Функция-обёртка. Результат: Чистый код без дублирования.
Промт: «Напиши утилиту на TypeScript для оборачивания асинхронных Express-обработчиков, чтобы автоматически передавать ошибки в next(). Используй дженерики для типов req/res/next.»
Код:
import { Request, Response, NextFunction, RequestHandler } from 'express';
type AsyncHandler = (req: Request, res: Response, next: NextFunction) => Promise<any>;
const asyncHandler = (fn: AsyncHandler): RequestHandler => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
export default asyncHandler;
7. Генерация Zod-схемы для валидации форм
Проблем: Нужна типобезопасная валидация без Joi. Решение: Промт для Zod с интеграцией с TypeScript. Результат: Схема, генерирующая типы автоматически.
Промт: «Создай Zod-схему для формы регистрации: username (от 3 до 20 символов), email (валидный email), password (минимум 8 символов, одна заглавная буква и цифра). Выведи инференс типа через z.infer. Добавь кастомное сообщение об ошибке для поля password.»
Код:
import { z } from 'zod';
export const registrationSchema = z.object({
username: z.string().min(3, 'Минимум 3 символа').max(20, 'Максимум 20 символов'),
email: z.string().email('Неверный формат email'),
password: z.string()
.min(8, 'Минимум 8 символов')
.regex(/[A-Z]/, 'Должна быть заглавная буква')
.regex(/\d/, 'Должна быть цифра'),
});
export type RegistrationForm = z.infer<typeof registrationSchema>;
8. Утилита для Debounce с TypeScript
Проблем: Нужен debounce для поискового инпута без внешних библиотек. Решение: Функция с правильной типизацией. Результат: Эффективная обработка ввода.
Промт: «Напиши функцию debounce на TypeScript. Принимает функцию и задержку. Возвращает новую функцию с тем же типом, но отложенным вызовом. Добавь отмену через .cancel(). Учти контекст this.»
Код:
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): ((...args: Parameters<T>) => void) & { cancel: () => void } {
let timer: ReturnType<typeof setTimeout> | null = null;
const debounced = (...args: Parameters<T>) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
timer = null;
}, delay);
};
debounced.cancel = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return debounced;
}
export default debounce;
9. Генерация enum для статусов заказа
Проблем: Нужен строгий тип для статусов в E-commerce. Решение: Промт для создания enum с описанием. Результат: Читаемый код без магических строк.
Промт: «Создай TypeScript enum для статусов заказа: PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED. Добавь функцию getStatusLabel, возвращающую русскоязычное описание. Используй const enum для оптимизации.»
Код:
const enum OrderStatus {
PENDING = 'PENDING',
PROCESSING = 'PROCESSING',
SHIPPED = 'SHIPPED',
DELIVERED = 'DELIVERED',
CANCELLED = 'CANCELLED',
}
const statusLabels: Record<OrderStatus, string> = {
[OrderStatus.PENDING]: 'Ожидает обработки',
[OrderStatus.PROCESSING]: 'В обработке',
[OrderStatus.SHIPPED]: 'Отправлен',
[OrderStatus.DELIVERED]: 'Доставлен',
[OrderStatus.CANCELLED]: 'Отменён',
};
function getStatusLabel(status: OrderStatus): string {
return statusLabels[status];
}
export { OrderStatus, getStatusLabel };
10. Функция для пагинации массива
Проблем: Нужна утилита для разбивки данных на страницы. Решение: Промт с учётом типов. Результат: Универсальный helper для списков.
Промт: «Напиши функцию paginate на TypeScript, которая принимает массив любого типа, номер страницы (page) и размер страницы (pageSize). Возвращает объект { data, total, page, pageSize, totalPages }. Обработай краевые случаи: page < 1, pageSize <= 0.»
Код:
interface PaginatedResult<T> {
data: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
function paginate<T>(items: T[], page: number = 1, pageSize: number = 10): PaginatedResult<T> {
const safePage = Math.max(1, page);
const safeSize = Math.max(1, pageSize);
const total = items.length;
const totalPages = Math.ceil(total / safeSize);
const start = (safePage - 1) * safeSize;
const data = items.slice(start, start + safeSize);
return { data, total, page: safePage, pageSize: safeSize, totalPages };
}
export default paginate;
Заключение
Эти 10 промтов покрывают большинство повседневных задач: от создания компонентов до утилит и тестов. Главное — всегда указывать типы, краевые случаи и желаемый результат. С практикой вы научитесь формулировать запросы так, чтобы код с первого раза был готов к продакшену. Попробуйте применить эти промты в своём проекте — и вы увидите, как ускорится разработка. А если хотите глубже изучить генерацию кода, загляните в наш блог — там ещё больше примеров и шаблонов.
Комментарии