Introduction
Generating production-ready JavaScript and TypeScript code with AI has become a staple for modern developers. Whether you're building React components, writing Node.js middleware, or crafting utility functions, the right prompt can turn a vague idea into a working snippet—fast. Based on patterns from the official TypeScript Handbook (typescriptlang.org) and real-world usage in open-source projects, this collection of 15 prompts covers the most common tasks. Each prompt is designed to be copy-pasted into any AI tool (ChatGPT, Claude, Copilot) and includes a concrete example to show you exactly what to expect. No fluff—just actionable templates.
1. React Component with TypeScript Props
Task: Generate a reusable React component with fully typed props, proper event handlers, and inline styles.
Prompt:
Create a React functional component called "UserCard" using TypeScript. It should accept props: `name: string`, `age: number`, `email: string`, and an optional `onDelete: (id: string) => void`. The component must render a card with a delete button. Use inline styles and handle the delete click event. Return the full component code with and without the export default.
Example Output:
interface UserCardProps {
name: string;
age: number;
email: string;
onDelete?: (id: string) => void;
}
const UserCard: React.FC<UserCardProps> = ({ name, age, email, onDelete }) => {
const handleDelete = () => {
if (onDelete) {
onDelete(email);
}
};
return (
<div style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
<h3>{name}</h3>
<p>Age: {age}</p>
<p>Email: {email}</p>
<button onClick={handleDelete}>Delete</button>
</div>
);
};
export default UserCard;
Why it works: It asks for a specific component with typed props, event handling, and styling—all in one go. The prompt forces the AI to produce complete, executable code.
2. TypeScript Utility: Deep Clone Function
Task: Write a type-safe deep clone function that works with nested objects, arrays, dates, and Maps.
Prompt:
Write a generic TypeScript function `deepClone<T>(obj: T): T` that deep clones an object. It must handle nested objects, arrays, Date instances, Map objects, and primitive values. Include a usage example with a complex object that contains all these types. Return only the function and example.
Example Output:
function deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj.getTime()) as any;
if (obj instanceof Map) {
const clone = new Map();
obj.forEach((value, key) => clone.set(key, deepClone(value)));
return clone as any;
}
if (Array.isArray(obj)) return obj.map(item => deepClone(item)) as any;
const cloneObj = {} as T;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
const original = { name: 'Alice', birth: new Date('1990-01-01'), tags: ['dev'], meta: new Map([['theme', 'dark']]) };
const cloned = deepClone(original);
console.log(cloned);
Why it works: The prompt explicitly lists edge cases (Date, Map) to ensure the generated function is robust.
3. Node.js Express Middleware with TypeScript
Task: Build a logging middleware for Express using TypeScript that logs request method, URL, and duration.
Prompt:
Create an Express middleware function in TypeScript that logs the HTTP method, URL, and response time in milliseconds for every incoming request. Use the `Request`, `Response`, and `NextFunction` types from Express. Include the import statements and an example of how to use it in an Express app.
Example Output:
import { Request, Response, NextFunction } from 'express';
const requestLogger = (req: Request, res: Response, next: NextFunction): void => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next();
};
export default requestLogger;
// Usage: app.use(requestLogger);
Why it works: It specifies the exact types and behavior (response time), reducing ambiguity.
4. React Hook: useLocalStorage with TypeScript
Task: Create a custom React hook that syncs state with localStorage, with full TypeScript generics.
Prompt:
Write a custom React hook called `useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void]`. It should read from localStorage on mount, update localStorage on state change, and handle JSON parsing errors gracefully by falling back to the initial value. Include usage example with a counter.
Example Output:
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value: T) => {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
};
return [storedValue, setValue];
}
export default useLocalStorage;
// Usage: const [count, setCount] = useLocalStorage('count', 0);
Why it works: The prompt demands generics and error handling, producing a production-quality hook.
5. TypeScript Type Guard for Union Types
Task: Write a type guard that narrows a union type to a specific member based on a discriminant property.
Prompt:
Define two interfaces: `Circle` with `kind: 'circle'` and `radius: number`, and `Square` with `kind: 'square'` and `side: number`. Then write a type guard function `isCircle(shape: Circle | Square): shape is Circle`. Include a usage example that calculates area using a switch statement.
Example Output:
interface Circle { kind: 'circle'; radius: number; }
interface Square { kind: 'square'; side: number; }
function isCircle(shape: Circle | Square): shape is Circle {
return shape.kind === 'circle';
}
function getArea(shape: Circle | Square): number {
if (isCircle(shape)) {
return Math.PI * shape.radius ** 2;
} else {
return shape.side ** 2;
}
}
const myCircle: Circle = { kind: 'circle', radius: 5 };
console.log(getArea(myCircle)); // 78.54
Why it works: It demonstrates discriminated unions and type narrowing—core TypeScript concepts.
6. React Form Handler with Validation
Task: Generate a controlled form component with validation and error messages in TypeScript.
Prompt:
Create a React component called "LoginForm" that renders email and password fields. It should be a controlled form using useState. Validate that email is a valid email format and password is at least 6 characters. Show error messages below each field. Use TypeScript interfaces for the form data and errors. Include a submit handler that prevents default and logs data.
Example Output:
import React, { useState } from 'react';
interface FormData { email: string; password: string; }
interface FormErrors { email?: string; password?: string; }
const LoginForm: React.FC = () => {
const [formData, setFormData] = useState<FormData>({ email: '', password: '' });
const [errors, setErrors] = useState<FormErrors>({});
const validate = (): boolean => {
const newErrors: FormErrors = {};
if (!/\S+@\S+\.\S+/.test(formData.email)) newErrors.email = 'Invalid email';
if (formData.password.length < 6) newErrors.password = 'Password too short';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (validate()) console.log('Submitting', formData);
};
return (
<form onSubmit={handleSubmit}>
<input value={formData.email} onChange={e => setFormData({...formData, email: e.target.value})} />
{errors.email && <span>{errors.email}</span>}
<input type="password" value={formData.password} onChange={e => setFormData({...formData, password: e.target.value})} />
{errors.password && <span>{errors.password}</span>}
<button type="submit">Login</button>
</form>
);
};
export default LoginForm;
Why it works: It defines clear validation rules and separates data from errors using interfaces.
7. TypeScript Async Wrapper for Express
Task: Create a utility to wrap async Express route handlers and catch errors automatically.
Prompt:
Write a TypeScript higher-order function `asyncHandler(fn: (req: Request, res: Response, next: NextFunction) => Promise<any>)` that catches any rejected promise and passes the error to Express error-handling middleware (next). Include a usage example with an async route that throws an error.
Example Output:
import { Request, Response, NextFunction } from 'express';
type AsyncFunction = (req: Request, res: Response, next: NextFunction) => Promise<any>;
const asyncHandler = (fn: AsyncFunction) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
export default asyncHandler;
// Usage:
// app.get('/data', asyncHandler(async (req, res) => {
// const data = await fetchData();
// res.json(data);
// }));
Why it works: It solves a common Express pain point—unhandled promise rejections—with a clean, typed wrapper.
8. React Context with TypeScript Generics
Task: Build a typed context provider and consumer for a theme (light/dark) without third-party libraries.
Prompt:
Create a React context for a theme with values 'light' or 'dark'. Define a `ThemeContext` that provides `theme` and a `toggleTheme` function. Write a `ThemeProvider` component that manages state and a custom hook `useTheme`. Use TypeScript generics for the context value type. Include a consumer example that displays the current theme.
Example Output:
import React, { createContext, useContext, useState, ReactNode } from 'react';
interface ThemeContextType { theme: 'light' | 'dark'; toggleTheme: () => void; }
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
const ThemeProvider: React.FC<{children: ReactNode}> = ({ children }) => {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => setTheme(prev => prev === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
};
export { ThemeProvider, useTheme };
// Usage: const { theme, toggleTheme } = useTheme();
Why it works: It follows the official React Context pattern with proper error handling for misuse.
9. JavaScript Debounce Function
Task: Write a plain JavaScript debounce function with TypeScript type annotations.
Prompt:
Write a debounce function in TypeScript: `debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void`. It should return a debounced version that only calls `fn` after `delay` ms of inactivity. Include a usage example with a search input handler.
Example Output:
function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
const handleSearch = (query: string) => console.log('Searching:', query);
const debouncedSearch = debounce(handleSearch, 300);
debouncedSearch('hello'); // will log after 300ms
Why it works: It uses TypeScript's Parameters<T> utility type for perfect type inference.
10. Node.js File Reader with Error Handling
Task: Create a utility to read a file asynchronously and parse JSON with proper error handling.
Prompt:
Write an async function `readJSONFile<T>(filePath: string): Promise<T>` that reads a file using Node.js `fs.promises`, parses it as JSON, and returns the typed result. If the file doesn't exist or JSON is invalid, throw a descriptive error. Include import statements and a usage example.
Example Output:
import { readFile } from 'fs/promises';
async function readJSONFile<T>(filePath: string): Promise<T> {
try {
const data = await readFile(filePath, 'utf-8');
return JSON.parse(data) as T;
} catch (error) {
if (error instanceof SyntaxError) {
throw new Error(`Invalid JSON in file: ${filePath}`);
}
throw new Error(`Could not read file: ${filePath}`);
}
}
// Usage:
// interface Config { port: number; }
// const config = await readJSONFile<Config>('./config.json');
Why it works: It leverages Node.js native promises and provides clear error messages for debugging.
11. TypeScript Mapped Type for API Response
Task: Generate a mapped type that converts all properties of an interface to optional and nullable.
Prompt:
Define an interface `User` with properties `id: number`, `name: string`, `email: string`. Then create a mapped type `PartialNullable<T>` that makes every property optional and nullable (`T | null`). Apply it to `User` to create `PartialNullable<User>`. Show the resulting type and a usage example.
Example Output:
interface User { id: number; name: string; email: string; }
type PartialNullable<T> = { [P in keyof T]?: T[P] | null };
type PartialNullableUser = PartialNullable<User>;
// Result: { id?: number
| null; name?: string | null; email?: string | null; }
const updatePayload: PartialNullableUser = { name: 'Bob', email: null };
Why it works: It teaches advanced type manipulation with a practical use case (partial API updates).
12. React List with Key and Empty State
Task: Create a component that renders a list of items with a fallback empty state.
Prompt:
Write a React component called "ItemList" that accepts a generic array `items: T[]` and a `renderItem: (item: T) => ReactNode` function. If the array is empty, render a message "No items available." Otherwise, render an unordered list. Use TypeScript generics for the component.
Example Output:
import React, { ReactNode } from 'react';
interface ItemListProps<T> { items: T[]; renderItem: (item: T) => ReactNode; }
function ItemList<T>({ items, renderItem }: ItemListProps<T>): JSX.Element {
if (items.length === 0) return <p>No items available.</p>;
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
export default ItemList;
// Usage: <ItemList items={['a', 'b']} renderItem={item => <span>{item}</span>} />
Why it works: It handles edge cases (empty list) and is fully reusable with generics.
13. JavaScript Array Chunk Function
Task: Write a utility that splits an array into chunks of a given size.
Prompt:
Write a TypeScript function `chunkArray<T>(arr: T[], size: number): T[][]` that splits an array into chunks of the specified size. The last chunk may be smaller if the array length is not divisible. Include two test cases with different array sizes.
Example Output:
function chunkArray<T>(arr: T[], size: number): T[][] {
const result: T[][] = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
console.log(chunkArray([1, 2, 3, 4, 5], 2)); // [[1, 2], [3, 4], [5]]
console.log(chunkArray(['a', 'b', 'c'], 3)); // [['a', 'b', 'c']]
Why it works: Simple, typed, and handles the edge case of unequal division.
14. TypeScript Conditional Type for API Status
Task: Create a conditional type that resolves to different shapes based on a status field.
Prompt:
Define a type `ApiResponse<T>` that is a discriminated union: if `status` is 'success', include `data: T`; if 'error', include `error: string`. Write a conditional type `ExtractData<T>` that extracts the data type only when status is 'success'. Include a usage example.
Example Output:
type ApiResponse<T> =
| { status: 'success'; data: T }
| { status: 'error'; error: string };
type ExtractData<T> = T extends { status: 'success'; data: infer D } ? D : never;
type UserResponse = ApiResponse<{ name: string }>;
type UserData = ExtractData<UserResponse>; // { name: string }
const response: UserResponse = { status: 'success', data: { name: 'Alice' } };
Why it works: It demonstrates conditional types with infer—a powerful TypeScript feature.
15. React Memoized Component with useMemo
Task: Write an optimized component that uses useMemo to avoid expensive recalculations.
Prompt:
Create a React component called "ExpensiveList" that accepts an array of numbers and filters them to even numbers, then doubles them. Use `useMemo` to memoize the filtered and mapped result. The component should render the list as bullet points. Use TypeScript for the props.
Example Output:
import React, { useMemo } from 'react';
interface ExpensiveListProps { numbers: number[]; }
const ExpensiveList: React.FC<ExpensiveListProps> = ({ numbers }) => {
const processed = useMemo(() =>
numbers.filter(n => n % 2 === 0).map(n => n * 2),
[numbers]);
return (
<ul>
{processed.map((val, index) => <li key={index}>{val}</li>)}
</ul>
);
};
export default ExpensiveList;
Why it works: It uses useMemo correctly with a dependency array, demonstrating performance optimization.
Conclusion
These 15 prompts cover the most frequent tasks you'll encounter when coding in JavaScript and TypeScript—from React components to Node.js utilities and advanced type patterns. The key is specificity: by describing the exact behavior, types, and edge cases, you get production-ready code with minimal edits. Bookmark this list and adapt the prompts to your own projects. The more precise your prompt, the better the output. Happy coding!
Comments