Welcome back! I am Mihir, and in the previous lesson we learned what TypeScript is.
Now let us answer a very practical question:
Why should we use TypeScript when JavaScript already works?
This is a fair question. JavaScript is powerful, popular, and runs everywhere. TypeScript is not popular because JavaScript is bad. TypeScript is popular because modern apps are bigger, more connected, and harder to maintain without extra safety.
The Main Problem: JavaScript Finds Many Bugs Too Late
JavaScript usually finds type-related bugs when the code is running.
That means bugs can appear:
- During testing
- After deployment
- When a user enters unexpected data
- When an API response changes
- When another developer uses your function incorrectly
Example:
function getDiscountedPrice(price, discount) {
return price - price * discount;
}
console.log(getDiscountedPrice(1000, 0.1)); // 900
console.log(getDiscountedPrice("1000", 0.1)); // 900
console.log(getDiscountedPrice("free", 0.1)); // NaNThe function looks simple, but it accepts anything.
Now see the TypeScript version:
function getDiscountedPrice(price: number, discount: number): number {
return price - price * discount;
}
getDiscountedPrice(1000, 0.1);
getDiscountedPrice("free", 0.1);TypeScript warns you:
Argument of type 'string' is not assignable to parameter of type 'number'.This is the first big reason to use TypeScript:
It moves many bugs from runtime to development time.
Problem 1: Wrong Function Arguments
In JavaScript, functions do not clearly tell you what they expect unless you read the implementation or documentation.
function createUser(name, age, isAdmin) {
return {
name,
age,
isAdmin,
};
}
const user = createUser("Mihir", true, 25);This code runs, but the arguments are in the wrong order:
agereceivestrueisAdminreceives25
TypeScript catches this immediately:
function createUser(name: string, age: number, isAdmin: boolean) {
return {
name,
age,
isAdmin,
};
}
const user = createUser("Mihir", true, 25);TypeScript tells us:
Argument of type 'boolean' is not assignable to parameter of type 'number'.Better Real-World Pattern
For functions with multiple values, an object is often clearer.
type CreateUserInput = {
name: string;
age: number;
isAdmin: boolean;
};
function createUser(input: CreateUserInput) {
return {
name: input.name,
age: input.age,
isAdmin: input.isAdmin,
};
}
const user = createUser({
name: "Mihir",
age: 25,
isAdmin: false,
});This is easier to read because every value has a name.
Problem 2: Missing Object Properties
JavaScript objects are flexible, but that flexibility can hide mistakes.
const product = {
id: 1,
title: "Laptop",
price: 75000,
};
function printProduct(product) {
console.log(product.name.toUpperCase());
}
printProduct(product);This crashes because product.name does not exist.
TypeError: Cannot read properties of undefinedIn TypeScript:
type Product = {
id: number;
title: string;
price: number;
};
const product: Product = {
id: 1,
title: "Laptop",
price: 75000,
};
function printProduct(product: Product) {
console.log(product.name.toUpperCase());
}TypeScript warns:
Property 'name' does not exist on type 'Product'.This is powerful because TypeScript understands the object structure before the code runs.
Problem 3: Unsafe API Responses
In real apps, you often fetch data from an API.
JavaScript version:
async function getUser() {
const response = await fetch("/api/user");
const user = await response.json();
console.log(user.profile.name);
}This assumes the API response always has:
user.profile.nameBut what if the API sends:
{
"id": 1,
"name": "Mihir"
}Then user.profile is undefined, and the code crashes.
TypeScript lets us describe what we expect:
type UserResponse = {
id: number;
name: string;
profile?: {
bio: string;
avatarUrl: string;
};
};
async function getUser(): Promise<UserResponse> {
const response = await fetch("/api/user");
return response.json();
}
async function showUser() {
const user = await getUser();
console.log(user.name);
console.log(user.profile?.bio);
}The ? in profile? means the property is optional.
So TypeScript encourages safe access:
user.profile?.bioinstead of unsafe access:
user.profile.bioProblem 4: Refactoring Becomes Risky
Refactoring means changing code structure without changing behavior.
Example: you rename a property from username to displayName.
In JavaScript:
const user = {
username: "Mihir",
};
console.log(user.username);After refactoring:
const user = {
displayName: "Mihir",
};
console.log(user.username);This code does not show an error immediately. It logs undefined.
In TypeScript:
type User = {
displayName: string;
};
const user: User = {
displayName: "Mihir",
};
console.log(user.username);TypeScript warns:
Property 'username' does not exist on type 'User'.This makes refactoring much safer.
In a large project, this is a huge benefit. You can rename types, functions, and properties with more confidence.
Problem 5: Code Is Harder to Understand Later
Imagine you find this function in a project:
function sendEmail(user, options) {
// many lines of code
}Questions immediately come up:
- What should
usercontain? - Is
optionsrequired? - What fields exist inside
options? - Does the function return anything?
Now compare the TypeScript version:
type EmailUser = {
email: string;
name: string;
};
type EmailOptions = {
subject: string;
template: "welcome" | "reset-password" | "invoice";
sendCopy?: boolean;
};
function sendEmail(user: EmailUser, options: EmailOptions): boolean {
console.log(`Sending ${options.template} email to ${user.email}`);
return true;
}Without reading the whole function, you already know:
user.emailmust existuser.namemust existsubjectis requiredtemplatecan only be one of three valuessendCopyis optional- the function returns a boolean
TypeScript makes code self-documenting.
Problem 6: Invalid States in Your App
Many bugs happen because your code allows states that should not be possible.
JavaScript example:
const requestState = {
loading: true,
success: true,
error: "Something went wrong",
data: { id: 1 },
};This object is confusing.
Is the request loading? Did it succeed? Did it fail?
TypeScript lets us model valid states clearly:
type RequestState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: { id: number; name: string } }
| { status: "error"; message: string };Now each state has only the data it needs.
const state: RequestState = {
status: "success",
data: {
id: 1,
name: "Mihir",
},
};And when we use it:
function renderState(state: RequestState) {
if (state.status === "loading") {
return "Loading...";
}
if (state.status === "error") {
return state.message;
}
if (state.status === "success") {
return state.data.name;
}
return "Start request";
}TypeScript understands which properties are available in each state.
This is one of the most practical TypeScript skills for frontend apps.
TypeScript Is Especially Useful In These Places
TypeScript gives the biggest benefit when your project has:
- Many files
- Many developers
- Complex API responses
- Shared reusable components
- Business logic
- Forms
- Authentication
- Dashboards
- Payment flows
- Admin panels
- Long-term maintenance
For a tiny one-file script, JavaScript may be enough.
For a serious app, TypeScript becomes very valuable.
Does TypeScript Prevent Every Bug?
No.
TypeScript catches type-related mistakes, but it does not automatically solve every problem.
It will not automatically catch:
- Wrong business logic
- Bad UI behavior
- Wrong API data at runtime unless you validate it
- Security problems
- Poor performance
- Incorrect algorithms
Example:
function calculateDiscount(price: number): number {
return price + 100;
}TypeScript is happy because the types are correct.
But the logic is wrong because a discount should probably reduce the price.
So remember:
TypeScript improves safety, but you still need good thinking, testing, and clean architecture.
TypeScript Benefits Summary
| Benefit | Why It Matters |
|---|---|
| Early error detection | Catch mistakes before running code |
| Better autocomplete | Write code faster with editor help |
| Safer refactoring | Rename and restructure with confidence |
| Clear contracts | Functions and objects show what they expect |
| Better teamwork | Other developers understand your code faster |
| Fewer runtime crashes | Many common undefined/type bugs are avoided |
| Easier scaling | Large codebases stay more manageable |
Practice: Convert JavaScript Thinking to TypeScript Thinking
JavaScript
function calculateInvoice(customer, amount, paid) {
return {
customer,
amount,
paid,
};
}TypeScript
type Invoice = {
customer: string;
amount: number;
paid: boolean;
};
function calculateInvoice(
customer: string,
amount: number,
paid: boolean
): Invoice {
return {
customer,
amount,
paid,
};
}Better Version
type CreateInvoiceInput = {
customer: string;
amount: number;
paid: boolean;
};
type Invoice = {
customer: string;
amount: number;
paid: boolean;
createdAt: Date;
};
function createInvoice(input: CreateInvoiceInput): Invoice {
return {
customer: input.customer,
amount: input.amount,
paid: input.paid,
createdAt: new Date(),
};
}This version is easier to extend because all input values are grouped in one object.
What You've Learned
You now understand why TypeScript is useful:
- JavaScript often catches type bugs too late
- TypeScript catches many mistakes while coding
- Function arguments become safer
- Object properties become clearer
- API responses can be described with types
- Refactoring becomes less scary
- Code becomes easier to understand
- TypeScript helps model valid app states
What's Next?
In the next lesson, we will compare TypeScript vs JavaScript directly.
We will look at syntax, type checking, compilation, learning curve, tooling, and when to choose each one.
Need Help?
- Have questions, confusion, or want to know more? Contact me
The goal is not to write more complicated code. The goal is to make your JavaScript code safer, clearer, and easier to maintain.