Home
React
Fundamentals: TypeScript
Daniel Nguyen
Daniel Nguyen
July 01, 2025
2 min

TypeScript isn’t just JavaScript with type annotations. It’s a tool that helps you catch mistakes early, model your data correctly, and write safer, more maintainable code.

Here are 9 TypeScript concepts every developer should understand.

1. Catch Errors Before Runtime

TypeScript catches many mistakes during type checking, before your code runs.

function greet(user: { name: string }): string {
return user.name.toUpperCase();
}
greet({ age: 25 });
// ❌ Error: 'name' is missing

Why it matters: Catching type errors early reduces avoidable runtime bugs. It doesn’t eliminate every possible runtime error.

2. type, interface, and enum

Use type and interface to describe data structures.

type User = {
id: number;
name: string;
};
interface Admin extends User {
role: "admin";
}
  • type: Supports unions, intersections, and type aliases.
  • interface: Supports extension and declaration merging.
  • enum: Defines named constants, though string unions are often a simpler alternative.
type Status = "loading" | "success" | "error";

3. Narrowing Types

Union types restrict values to a defined set of options.

type Operator = "+" | "-" | "*" | "/";
function calculate(operator: Operator) {
// Only the four allowed operators are accepted.
}
calculate("%"); // ❌ Type error

Narrowing also lets TypeScript determine a more specific type through checks such as typeof, in, and equality comparisons.

4. Derive Types from Values

Avoid duplicating information that already exists in your code.

const operations = {
"+": (a: number, b: number) => a + b,
"-": (a: number, b: number) => a - b,
};
type Operator = keyof typeof operations;
// "+" | "-"

Add another operation, and the Operator type updates automatically.

5. Understand keyof and typeof

These operators help you derive types from existing values and structures.

type Operations = typeof operations;
type Operator = keyof Operations;
  • typeof: Gets the type of a variable or value.
  • keyof: Extracts an object’s keys as a union of property names.

Together, they help keep your types synchronized with your implementation.

6. Generics

Generics let you write reusable functions without sacrificing type safety.

function identity<T>(value: T): T {
return value;
}
const name = identity("Viet"); // string
const age = identity(25); // number

TypeScript infers T from the argument, so you usually don’t need to specify it manually.

7. Utility Types

TypeScript provides built-in utilities to transform existing types.

type User = {
id: number;
name: string;
email: string;
};
type PartialUser = Partial<User>;
type UserName = Pick<User, "name">;
type UserWithoutId = Omit<User, "id">;

Use Record when you need a consistent mapping between keys and values.

type OperationFn = (a: number, b: number) => number;
type Operator = "+" | "-";
const operations: Record<Operator, OperationFn> = {
"+": (a, b) => a + b,
"-": (a, b) => a - b,
};

This checks that all required keys exist and that their values match the expected function type.

8. The satisfies Operator

The satisfies operator validates a value against a type without replacing its inferred type with that target type.

const operations = {
"+": (a: number, b: number) => a + b,
"-": (a: number, b: number) => a - b,
} satisfies Record<"+" | "-", OperationFn>;

Compared with a type annotation, satisfies can preserve more specific information about the expression while still checking compatibility.

Use it when you want validation without unnecessarily widening your inferred types.

9. any vs. unknown vs. never

These types serve very different purposes.

any — Opt out of type safety

let value: any = 10;
value.foo.bar(); // No compile-time error

unknown — Validate before using

let value: unknown = "hello";
if (typeof value === "string") {
value.toUpperCase(); // Safe after narrowing
}

never — Represent impossible values

function fail(message: string): never {
throw new Error(message);
}

never is also useful for exhaustive checks in discriminated unions.

Bonus: Safe vs. Unsafe TypeScript

Safer patterns:

  • Prefer unknown over any for values of uncertain type.
  • Use union types to restrict allowed values.
  • Derive types with keyof typeof.
  • Use satisfies to validate object shapes.
  • Use never to enforce exhaustive checks.

Potentially unsafe patterns:

  • any disables meaningful type checking.
  • as SomeType can override the compiler’s assumptions without validating the value.
  • ! can hide a possible null or undefined.
  • @ts-ignore can suppress useful errors.
  • Using unknown without narrowing doesn’t make an operation safe.

Remember: TypeScript provides static type safety, not a guarantee against all runtime failures. Validate external data, handle errors, and test your application.


Tags

#ReactFundamentals

Share

Daniel Nguyen

Daniel Nguyen

Frontend Developer

Frontend developer specializing in React, Next.js, and JavaScript. Writing practical guides on modern web development at Dev98.

Expertise

React
Next.js
JavaScript
TypeScript
Python

Social Media

githublinkedinyoutubewebsite

Related Posts

React Fundamentals
Fundamentals: Error Boundaries
July 06, 2025
1 min
Dev98

Dev98

React · Next.js · Web development