What Changed Before and After TypeScript

Dev
ยทDante Chun

The JavaScript-Only Days

When I started freelance development in 2021, I only used JavaScript. React, Node.js - all in JS. I had thoughts like "Isn't TypeScript just for large companies?" and "Won't it slow me down having to add types?"

Then I experienced something shocking in a project.

The Trigger: Runtime Error Nightmare

In a project I was working on, the API response structure changed. A backend developer changed user.profile.name to user.profileData.userName, and this data was being used in over 20 places in the frontend.

I discovered the bug after deployment.

// Previous code
const displayName = user.profile.name
// TypeError: Cannot read property 'name' of undefined

I didn't know until users reported it directly. It took half a day to manually search through all files and fix them. I couldn't make the same mistake twice.

First TypeScript Project

From the next project, I adopted TypeScript. At first, it was really frustrating. Red errors poured out, and I couldn't figure out how to fix them.

// First errors I encountered
// Object is possibly 'undefined'.
// Property 'xxx' does not exist on type 'yyy'.
// Type 'string' is not assignable to type 'number'.

But after about 2 weeks, I adapted. And I realized:

"These errors would have been bugs that exploded at runtime."

What Actually Changed

1. API Response Type Definitions

Now I always define API responses with types.

interface User {
  id: string
  profileData: {
    userName: string
    email: string
    avatar?: string
  }
}

async function getUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

When the backend changes the response structure? Just modify the type. Then TypeScript tells you all the affected code. Just go fix the places with red underlines.

2. The Power of Autocomplete

When you type an object in VS Code, you get autocomplete. Type user. and you see profileData, id, and other possible properties. No need to dig through documentation or check with console.log.

// Without autocomplete
const name = user.??? // profile? name? userName?

// With types
const name = user.profileData.userName // Select directly with Cmd+Space

3. Refactoring Confidence

I used to be scared when changing function names or structures. The anxiety of "Did I find everywhere it's used?"

In TypeScript, one F2 (Rename Symbol) safely changes the name across the entire project. If a reference is missed, the build fails, so I can modify with confidence.

4. Error Prevention

This is my favorite part.

// These mistakes are caught at compile time
function calculateTotal(price: number, quantity: number) {
  return price * quantity
}

// Error: Argument of type 'string' is not assignable to parameter of type 'number'
calculateTotal("100", 2)

// This too
const items: Product[] = []
items.push({ id: 1, name: "item" }) // Error: 'price' is required

Productivity Changes

First 2 weeks: 50% productivity drop

Fighting type errors cut my coding speed in half. I had to resist the temptation to abuse any.

After 1 month: Back to original level

Type definitions became familiar, and common patterns became second nature. Editor autocomplete kept code writing speed similar.

After 3 months: Felt productivity improvement

The real gain was reduced maintenance time.

  • Time finding runtime bugs: Dramatically reduced
  • Time responding to API changes: Hours โ†’ Minutes
  • Type errors found in code review: Almost none
  • Modifying 6-month-old code: Understand immediately by looking at types

TypeScript in Client Projects

Unlike solo development, there are additional considerations for client projects.

Handoffs

When handing projects over to clients or when other developers maintain them, TypeScript code is much easier to understand. Type definitions serve as documentation themselves.

// It's clear what this function receives and returns
function processOrder(
  order: Order,
  options: ProcessOptions
): Promise<ProcessResult> {
  // ...
}

Gradual Migration

When taking on legacy JavaScript projects, you don't have to change everything at once. Setting allowJs: true in tsconfig allows JS and TS to coexist.

Just create new files as .ts, and convert existing files one by one when you have time.

Recommended Development Environment

To maximize the TypeScript experience:

  1. VS Code - Best TypeScript support
  2. Strict mode - Hard at first but keep it on
  3. ESLint + typescript-eslint - Utilize type-related lint rules
  4. Prisma - Auto-generate types from DB schema
// Recommended tsconfig.json settings
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitAny": true
  }
}

When to Use any

Sometimes any is necessary.

  • When external library types are wrong
  • When dealing with truly dynamic data
  • Quick prototyping (must fix later)

But when using any, I leave a comment explaining why.

// TODO: Fix when API response type is confirmed
const data: any = await fetchUnknownAPI()

Conclusion

The biggest change before and after TypeScript adoption is "from anxiety to confidence."

Before, every time I modified code, I had the anxiety of "This won't cause problems elsewhere, right?" Now, when the build succeeds, I'm confident there are at least no type-related bugs.

There's an initial learning cost, but long-term, it's definitely worth it. Especially for solo developers, TypeScript has an effect similar to having a fellow developer code review your work.

If you're still only using JavaScript, I recommend trying it on your next project. Just endure the first 2 weeks.