Beginner15 min

Learn TypeScript fundamentals to read, understand, and modify AI-generated type-safe code.

TypeScript for AI Code

AI tools generate TypeScript by default. Understanding types helps you read, modify, and debug AI-generated code effectively.

Why AI Tools Use TypeScript

Type Safety Benefits

Terminal
// JavaScript - no type hints
function createUser(name, age, email) {
  return { name, age, email }
}

// TypeScript - self-documenting
function createUser(name: string, age: number, email: string): User {
  return { name, age, email }
}

AI tools prefer TypeScript because:

  • Self-documenting - Types explain what code expects
  • Error prevention - Catches bugs before runtime
  • Better autocomplete - IDE suggestions are more accurate
  • Refactoring safety - Changes propagate correctly

What You'll See

AI-generated code includes:

Terminal
// Type annotations
const count: number = 0
const name: string = "John"

// Interface definitions
interface User {
  id: string
  name: string
  email: string
}

// Generic types
const items: Array<User> = []

// Function types
const handleClick: (e: React.MouseEvent) => void

Basic Type Annotations

Primitive Types

Terminal
// String
const name: string = "Alice"

// Number (integers and floats)
const age: number = 25
const price: number = 99.99

// Boolean
const isActive: boolean = true

// Array
const numbers: number[] = [1, 2, 3]
const names: string[] = ["a", "b", "c"]

// Alternative array syntax
const items: Array<string> = ["x", "y", "z"]

Type Inference

TypeScript often infers types automatically:

Terminal
// TypeScript knows these types
const name = "Alice"        // string
const count = 42            // number
const active = true         // boolean
const items = [1, 2, 3]     // number[]

// No need to write:
const name: string = "Alice"  // redundant

AI tools may include explicit types for clarity, but inference works fine.

Union Types

Variables that can be multiple types:

Terminal
// Can be string or number
let id: string | number
id = "abc123"
id = 12345

// Common pattern: nullable values
let name: string | null = null
name = "Alice"

// Optional chaining works with these
const length = name?.length

Literal Types

Specific values as types:

Terminal
// Can only be these exact strings
type Status = "pending" | "active" | "inactive"

let status: Status = "pending"
status = "active"    // OK
status = "unknown"   // Error!

// Common in component props
type Size = "sm" | "md" | "lg"
type Variant = "default" | "outline" | "ghost"

Interfaces and Types

Defining Object Shapes

Terminal
// Interface - preferred for objects
interface User {
  id: string
  name: string
  email: string
  age?: number        // Optional property
}

// Type alias - also works
type Product = {
  id: string
  name: string
  price: number
}

Using Interfaces

Terminal
interface User {
  id: string
  name: string
  email: string
}

// Function parameter
function getUser(id: string): User {
  return { id, name: "Alice", email: "alice@example.com" }
}

// Array of objects
const users: User[] = [
  { id: "1", name: "Alice", email: "alice@example.com" },
  { id: "2", name: "Bob", email: "bob@example.com" },
]

// Component props
interface ButtonProps {
  children: React.ReactNode
  onClick?: () => void
  disabled?: boolean
}

function Button({ children, onClick, disabled }: ButtonProps) {
  return <button onClick={onClick} disabled={disabled}>{children}</button>
}

Extending Interfaces

Terminal
// Base interface
interface BaseEntity {
  id: string
  createdAt: Date
  updatedAt: Date
}

// Extend with additional properties
interface User extends BaseEntity {
  name: string
  email: string
}

// User now has: id, createdAt, updatedAt, name, email

Optional vs Required

Terminal
interface UserProfile {
  // Required properties
  id: string
  name: string
  email: string

  // Optional properties (?)
  age?: number
  bio?: string
  avatarUrl?: string
}

// Valid - optional properties omitted
const user: UserProfile = {
  id: "1",
  name: "Alice",
  email: "alice@example.com"
}

Function Types

Parameter and Return Types

Terminal
// Basic function
function add(a: number, b: number): number {
  return a + b
}

// Arrow function
const multiply = (a: number, b: number): number => a * b

// Return type inference (often omitted)
const divide = (a: number, b: number) => a / b  // infers number

Void and Never

Terminal
// void - function doesn't return anything
function logMessage(message: string): void {
  console.log(message)
}

// Event handlers typically return void
const handleClick = (e: React.MouseEvent): void => {
  e.preventDefault()
}

// never - function never returns (throws or infinite loop)
function throwError(message: string): never {
  throw new Error(message)
}

Optional Parameters

Terminal
// Optional parameter with ?
function greet(name: string, greeting?: string): string {
  return `${greeting || "Hello"}, ${name}!`
}

greet("Alice")           // "Hello, Alice!"
greet("Alice", "Hi")     // "Hi, Alice!"

// Default parameter
function greet(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`
}

Callback Types

Terminal
// Function as parameter
function fetchData(callback: (data: User[]) => void): void {
  // ...
  callback(users)
}

// Common React patterns
interface ButtonProps {
  onClick?: () => void
  onSubmit?: (data: FormData) => Promise<void>
  onChange?: (value: string) => void
}

Generics Basics

What Are Generics?

Generics create reusable components that work with multiple types:

Terminal
// Without generics - only works with numbers
function firstNumber(arr: number[]): number {
  return arr[0]
}

// With generics - works with any type
function first<T>(arr: T[]): T {
  return arr[0]
}

first([1, 2, 3])           // returns number
first(["a", "b", "c"])     // returns string
first([{ id: 1 }])         // returns object

Common Generic Patterns

Terminal
// Array
const items: Array<string> = ["a", "b"]

// Promise
const data: Promise<User> = fetchUser()

// Record (object with known key/value types)
const scores: Record<string, number> = {
  alice: 100,
  bob: 85
}

// Partial (all properties optional)
const update: Partial<User> = { name: "New Name" }

// Pick (subset of properties)
type UserPreview = Pick<User, "id" | "name">

// Omit (exclude properties)
type UserWithoutEmail = Omit<User, "email">

React Component Generics

Terminal
// Generic component
interface ListProps<T> {
  items: T[]
  renderItem: (item: T) => React.ReactNode
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map(renderItem)}</ul>
}

// Usage
<List
  items={users}
  renderItem={(user) => <li key={user.id}>{user.name}</li>}
/>

Reading AI-Generated Types

Common Patterns

Terminal
// Props interface
interface PageProps {
  params: { slug: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

// Component with props
export default function Page({ params, searchParams }: PageProps) {
  // ...
}

// API response type
interface ApiResponse<T> {
  data: T
  error: string | null
  loading: boolean
}

// Hook return type
function useUser(id: string): {
  user: User | null
  loading: boolean
  error: Error | null
} {
  // ...
}

React Types

Terminal
// Event types
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void

// Children types
children: React.ReactNode       // Most flexible
children: React.ReactElement    // Single element
children: string                // Text only

// Ref types
const inputRef = useRef<HTMLInputElement>(null)
const divRef = useRef<HTMLDivElement>(null)

// State types
const [count, setCount] = useState<number>(0)
const [user, setUser] = useState<User | null>(null)

HTML Element Props

Terminal
// Extend HTML element props
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: "default" | "outline"
  size?: "sm" | "md" | "lg"
}

// All HTML button props available (onClick, disabled, type, etc.)
function Button({ variant, size, ...props }: ButtonProps) {
  return <button {...props} />
}

Type Errors

Common Errors and Fixes

Property doesn't exist:

Terminal
// Error: Property 'name' does not exist on type '{}'
const user = {}
console.log(user.name)

// Fix: Define the type
const user: User = { id: "1", name: "Alice", email: "a@b.com" }

Type mismatch:

Terminal
// Error: Type 'string' is not assignable to type 'number'
const age: number = "25"

// Fix: Use correct type
const age: number = 25
// or
const age = parseInt("25")

Possibly null:

Terminal
// Error: Object is possibly 'null'
const user = users.find(u => u.id === id)
console.log(user.name)  // user might be undefined

// Fix: Check for null
if (user) {
  console.log(user.name)
}
// or use optional chaining
console.log(user?.name)

Missing properties:

Terminal
// Error: Property 'email' is missing
const user: User = {
  id: "1",
  name: "Alice"
  // missing email!
}

// Fix: Add required property
const user: User = {
  id: "1",
  name: "Alice",
  email: "alice@example.com"
}

Type Assertions

When TypeScript Needs Help

Terminal
// You know more than TypeScript
const input = document.getElementById("email") as HTMLInputElement
input.value = "test@example.com"

// Non-null assertion (use carefully!)
const user = users.find(u => u.id === id)!
console.log(user.name)  // Trust that it exists

// Type narrowing is usually better
const user = users.find(u => u.id === id)
if (!user) throw new Error("User not found")
console.log(user.name)  // TypeScript knows it's defined

Summary

  • Type annotations: name: string, count: number
  • Interfaces: Define object shapes
  • Union types: string | null, "a" | "b" | "c"
  • Optional: property?: type
  • Generics: Array<T>, Promise<T>
  • Type inference: Let TypeScript figure out types when obvious

Next Steps

Learn common TypeScript patterns used in AI-generated React and Next.js code.

Mark this lesson as complete to track your progress