AI as Your Pair Programmer

Advanced · 18 min read · ▶ live playground · ✦ checkpoint

AI can make TypeScript work feel faster, but the best use is not "write my app." The best use is "help me make one typed change that I can inspect, check, and explain."

An AI assistant is a tool that responds to ordinary-language requests by predicting useful text, code, explanations, or edits. A pair programmer is a second developer working beside you while you still own the decisions. Treat AI like a sharp second chair at the workbench: useful for drafts and questions, risky when it becomes the driver.

Where AI shines in TypeScript

TypeScript gives AI more structure than plain JavaScript because the assistant can see names, shapes, signatures, return types, union members, and diagnostics. That does not make generated code correct. It does make the conversation more specific.

Good TypeScript jobs for AI:

  • Boilerplate: draft a mapper, a test fixture, a discriminated-union switch, a utility wrapper, or a small adapter.
  • Tests: propose boundary cases from a type, add cases for every union member, or turn a bug report into test names.
  • Migrations: convert a JavaScript helper to typed TypeScript, suggest boundary annotations, or find places where unknown should replace loose values.
  • Error explanations: translate a TypeScript diagnostic into the exact promise your code broke.
  • Type reading: summarize a library signature, overload list, or nested utility type after you paste the relevant declaration.

The weak workflow is "write the whole feature." The strong workflow is "here is the type, here is the failing diagnostic, here is the file rule, draft the next small edit." Small generated code is reviewable. Large generated code is where people lose track of decisions.

Prompt with the types

Context is the desk surface: the files, text, errors, and rules the assistant can read. If the right type signature is on the desk, the assistant has less room to guess.

A vague prompt asks for code without a contract:

Write a helper that formats users.

A stronger TypeScript prompt clips the work order to a type:

Task: Draft an implementation for this TypeScript signature.
 
type User = {
  id: string;
  displayName: string;
  roles: ("admin" | "member")[];
  disabledAt?: string;
};
 
function labelUser(user: User): string;
 
Rules:
- include "(disabled)" when disabledAt is present
- include "admin" only when roles contains "admin"
- do not change the types
- do not use assertions or loose types
 
Return:
- the function body
- then list 4 cases I should test

That prompt gives the assistant a type boundary, behavior rules, and review constraints. The result may still be wrong, but now the wrongness has places to show up: a mismatched return type, an unhandled optional property, a missing admin case, or a test case that contradicts the rules.

For error explanations, paste the smallest useful diagnostic plus the relevant type:

Explain this TypeScript error for a developer who understands unions.
Do not rewrite the whole function.
Name the exact type promise that failed and suggest the smallest fix.
 
Diagnostic:
src/actions.ts:14:13 - error TS2322: Type '"archived"' is not assignable to type '"draft" | "published"'.
 
Relevant type:
type PostStatus = "draft" | "published";

This is better than "why is TypeScript mad?" because it makes the assistant reason from evidence. The diagnostic and the union are the important materials on the desk.

Generate from examples, then verify the boundary

AI is also useful for moving from example data to first-draft types. That is not validation. It is a starting point for the boundary conversation you learned in Section 13.

Suppose you have this example response from a teammate or a fixture:

{
  "id": "user_123",
  "email": "sam@example.com",
  "roles": ["admin", "member"],
  "createdAt": "2026-07-04T09:30:00Z"
}

A useful AI request is:

From this example response, propose:
1. a TypeScript object type
2. a Role union if the roles look closed
3. a short list of questions where one example is not enough
4. the shape of a runtime parser contract, but no package imports

A reviewable draft might look like this:

type Role = "admin" | "member";
 
type UserFromApi = {
  id: string;
  email: string;
  roles: Role[];
  createdAt: string;
};
 
type UserParser = {
  parse: (value: unknown) => UserFromApi;
};

The type is useful, but it is not a receipt for data that arrived from outside. A single example cannot prove that roles is only "admin" | "member", that createdAt is always an ISO timestamp, or that email is a valid email address. Ask the assistant to list those assumptions, then decide which ones belong in a schema, parser, or product rule.

That is the TypeScript-specific move: use AI to draft a static shape, then use the runtime-boundary habits from Section 13 to decide how real data earns that shape.

Migrations are a strong AI use case

AI can help when you are converting JavaScript to TypeScript because migration work has repeatable moves: add boundary annotations, replace loose values with unknown, turn string states into literal unions, and split runtime checks from typed internals.

Good migration prompt:

Convert this JavaScript helper to TypeScript in the smallest safe step.
Constraints:
- keep runtime behavior the same
- annotate function parameters and return type
- use unknown for external input
- do not introduce package imports
- explain any place where you need a runtime check before trusting the type

That prompt fits the migration strategy from Section 19: type boundaries first, internals later. It also prevents a common AI failure: adding a broad assertion just to quiet the checker.

Use AI to generate a first pass, but make the compiler and tests part of the loop. Run tsc --noEmit, typed lint, and relevant tests after the edit. If the assistant claims a generated type is safe but the compiler rejects it, believe the compiler. If the compiler accepts it but a test contradicts the product rule, believe the test.

Keep the assistant on a short leash

The best AI prompts in a TypeScript repo usually include:

  • Goal: the one change you want.
  • Context: the type signature, diagnostic, test failure, or declaration.
  • Constraints: no imports, no assertions, preserve public API, use existing union names, or keep behavior unchanged.
  • Output format: code only, plan first, tests first, or explanation before edit.
  • Checks: tsc --noEmit, lint rules, unit tests, type tests, or cases the answer must satisfy.

Ask for assumptions before code when the task touches outside data, product policy, money, permissions, privacy, or security. AI is good at filling blanks. Your job is to decide which blanks are allowed to be filled.

The next lesson makes the TypeScript advantage concrete: AI can draft quickly, but strict types, tsc --noEmit, and type-aware lint can referee the draft before it reaches review.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion