15 Battle-Tested Prompts for Generating JavaScript and TypeScript Code

If you write JavaScript or TypeScript daily and use AI to speed up your workflow, you know the difference between a vague prompt that produces garbage and a precise one that saves you 30 minutes. Over the past year, I've collected, refined, and battle-tested prompts that consistently generate clean, production-ready JS/TS code. Here are 15 of them — organized by category: React components, Node.js utilities, TypeScript types, and general helpers.

React Components

1. Generate a React component with TypeScript and Storybook

Generate a React component in TypeScript that renders a user avatar. Include:
- Props: src (string), alt (string), size ('sm'

| 'md' | 'lg'), onClick (optional callback)
- Use styled-components for styling
- Export a default export and a named export
- Write a Storybook story for all three sizes

This prompt works because it specifies exact prop types, styling approach, and export expectations. Without "Storybook story", you get just the component. Without "three sizes", you get one story.

Real usage: I needed a reusable <Avatar> for a dashboard. The AI returned a complete component with styled.img, border-radius: 50%, and stories for sm (32px), md (48px), lg (64px). I adjusted onClick to type React.MouseEventHandler — that was the only edit.

2. Write a custom React hook with loading state

Write a custom React hook `useFetch<T>` that:
- Takes a URL string and an optional config object (method, headers, body)
- Returns { data: T

| null, loading: boolean, error: string | null, refetch: () => void }
- Uses AbortController for cleanup
- Handles network errors and 4xx/5xx responses
- TypeScript generic for the response type

Without AbortController, the hook would leak memory on unmount. Without refetch, it's one-shot. The generic <T> makes it reusable across any API.

3. Generate a React form with validation

Create a React form in TypeScript with:
- Fields: email (email validation), password (min 8 chars, one number), confirmPassword (must match)
- Use React Hook Form with yup resolver
- Show inline error messages below each field
- On submit, log the values to console and reset form
- Disable submit button while submitting

This prompt maps directly to common form patterns. The "disable submit while submitting" detail prevents double-clicks — a UX pattern many developers forget.

Node.js Utilities

4. Build an Express middleware for JWT authentication

Write an Express middleware in TypeScript that:
- Extracts JWT from Authorization header (Bearer token)
- Verifies using jsonwebtoken library
- Attaches decoded payload to req.user
- Returns 401 with JSON error message if token missing or invalid
- Handles token expiry with specific error message
- TypeScript declaration merging for Express Request

Declaration merging is the key here. Without it, req.user won't be typed. The prompt explicitly asks for it.

Real usage: I integrated this into an API gateway. The AI generated the exact declare global { namespace Express { interface Request { user: JwtPayload } } } block. I only had to install @types/express and jsonwebtoken.

5. Write a file processing pipeline with streams

Write a Node.js script that:
- Reads a large CSV file (1GB+) using fs.createReadStream
- Transforms each row: convert all values to lowercase, trim whitespace, remove rows with empty cells
- Writes the result to a new CSV file using fs.createWriteStream
- Uses the csv-parse and csv-stringify packages
- Handles backpressure properly
- Logs progress every 1000 rows

Streams are hard to write from scratch. This prompt includes backpressure (pipe() or pipeline()) and progress logging — both critical for production.

6. Generate a CLI tool with Commander

Create a CLI tool in Node.js using Commander and TypeScript that:
- Has a command `init` that creates a config file in the current directory
- Has a command `build [input] --output [path]` that reads a JSON file and writes a minified version
- Shows colored output with chalk
- Handles missing input file gracefully with a friendly error message
- Includes a --version flag

Specifying init and build commands prevents the AI from guessing. Adding chalk and error handling makes the output user-ready.

TypeScript Types and Utilities

7. Create a type-safe event emitter

Implement a TypeScript class `TypedEventEmitter<T extends Record<string, any[]>>` that:
- Has methods: on, off, emit
- Is generic over event names and argument types
- TypeScript ensures that emit('userCreated', user) only accepts arguments matching the tuple for 'userCreated'
- Uses a Map for listeners
- Supports multiple listeners per event

This is a classic typing challenge. The prompt explicitly asks for generic constraints — otherwise AI often returns a plain EventEmitter without types.

Real usage: In a WebSocket manager, I needed typed events: 'message': [data: Message], 'error': [err: Error]. The AI produced a class where emit('message', someError) was a compile-time error.

8. Generate a PickByType utility type

Write a TypeScript utility type `PickByType<T, U>` that:
- Extracts keys from T where value type extends U
- Example: PickByType<{a: string, b: number, c: string}, string> => {a: string, c: string}
- Also write `OmitByType` that excludes those keys
- Include test cases with `type tests = [Expect<Equal<...>>]` from type-testing library

Without test cases, you can't verify correctness. The Expect<Equal<...>> pattern from type-testing ensures the types actually work.

9. Create a Zod schema for a complex API response

Write a Zod schema for a paginated API response:
- data: array of objects with id (number), name (string), email (string), createdAt (ISO date string)
- pagination: object with page (number), limit (number), total (number), hasMore (boolean)
- Infer the TypeScript type from the schema
- Write a parseResponse function that validates and returns typed data or throws a formatted error

Zod's .parse() throws by default. Asking for parseResponse with formatted errors makes it production-ready.

General Utilities

10. Write a debounce function in TypeScript

Write a debounce function in TypeScript that:
- Takes a function F and a delay (number)
- Returns a debounced version with correct types
- Supports `leading` and `trailing` options (like Lodash)
- Has a `cancel` method to clear pending invocations
- Does NOT use any external library

Lodash's debounce is heavy. A typed implementation with cancel and leading/trailing covers 90% of use cases.

Real usage: Search input autocomplete. The AI returned a generic debounce<T extends (...args: any[]) => any> with setTimeout and clearTimeout. I added it to a shared utils package.

11. Generate a deep clone function

Write a deep clone function in TypeScript that:
- Handles: objects, arrays, Date, Map, Set, RegExp, primitives
- Does NOT handle: functions, WeakMap, WeakSet, circular references
- Returns a new object with all nested properties cloned
- Type-safe: the return type matches the input type

Circular references are explicitly excluded — otherwise the AI might produce an infinite loop. Including Date, Map, Set makes it useful for real data.

12. Write a retry utility with exponential backoff

Create a utility function `retry<T>` that:
- Takes an async function that returns Promise<T>
- Retries up to `maxRetries` times on any error
- Waits `baseDelay * 2^attempt` milliseconds between retries (exponential backoff)
- Adds jitter: randomize delay by ±50%
- Throws the last error if all retries fail
- TypeScript generic over T

Jitter prevents thundering herd. Without it, all retries hit the server simultaneously. The prompt explicitly asks for it.

13. Generate a memoize function with LRU cache

Write a memoize function in TypeScript that:
- Caches up to `maxSize` results (LRU eviction)
- Uses the first argument as cache key by default, but accepts a custom key function
- Handles async functions correctly (returns cached promise, not stale value)
- Exports both `memoize` and `memoizeAsync`

Async memoization is tricky because you must cache the promise, not the value. The prompt separates sync and async paths.

Testing

14. Write Jest tests for a React component

Write Jest tests for the Avatar component from prompt #1:
- Test that it renders the image with correct src and alt
- Test that it applies correct size class (sm=32px, md=48px, lg=64px)
- Test that onClick fires when clicked
- Test that it does not fire onClick when disabled (if disabled prop added)
- Use @testing-library/react and jest-styled-components

Referencing a previous prompt creates continuity. Specifying jest-styled-components ensures the AI checks actual CSS, not just class names.

15. Generate a test for an Express endpoint

Write a supertest test for the JWT middleware from prompt #4:
- Test 200 with valid token (mock verify to return payload)
- Test 401 with missing Authorization header
- Test 401 with invalid token
- Test 401 with expired token
- Use Jest mocks for jsonwebtoken

Mocking jsonwebtoken is essential — you don't want real tokens in tests. The prompt covers all error paths.

How to Write Better Prompts Yourself

Based on these examples, here are three rules I follow:

  1. Be specific about types and options. Instead of "write a React component", say "write a React component in TypeScript with props interface, using styled-components, exporting both default and named exports".

  2. Include edge cases. Mention error handling, loading states, cleanup, empty states. The AI will include them if you ask.

  3. Reference real libraries by name. "Use yup resolver" is better than "use a validation library". The AI knows yup's API.

Common Mistakes to Avoid

  • Vague prompts: "Generate utility functions" → you get add(a,b) and subtract(a,b). Be specific.
  • Missing context: "Write a hook" without saying React → you might get a Bash hook.
  • No constraints: Without "no external libraries", AI may pull in lodash for a simple function.

Conclusion

AI code generation is only as good as your prompts. The 15 prompts above are copy-paste ready — I use them daily to generate components, middleware, utility types, and tests. Start with these, then adapt them to your own patterns. The more specific you are, the less editing you'll do.

If you work with external APIs or services (like Stripe, Telegram, or Salesforce), ASI Biont supports connecting to many of them through API integrations — check the documentation at asibiont.com/courses for practical examples of building integrations with Node.js and TypeScript.

← All posts

Comments