tutorialTypeScriptJavaScriptTutorial

TypeScript for JavaScript Developers: A Practical Guide

TypeScript for JavaScript Developers: A Practical Guide

TypeScript is JavaScript with types. It catches bugs at compile time instead of runtime, and makes your IDE incredibly helpful.


Why TypeScript?

The JavaScript Problem

function getUser(id) {

// What is id? string? number?

// What does this return?

}

// Later...

const user = getUser("abc");

console.log(user.fullName); // Runtime error: user.name, not fullName

You only find the bug when the code runs.

The TypeScript Solution

interface User {

id: string;

name: string;

email: string;

}

function getUser(id: string): User {

// TypeScript knows what id is and what we return

}

const user = getUser("abc");

console.log(user.fullName); // Error caught immediately in IDE

Bugs are caught before your code runs.


Getting Started

Setup

npm install typescript --save-dev

npx tsc --init

Or just use a framework that includes it:

npx create-next-app@latest my-app --typescript

File Extension

Use .ts for TypeScript files, .tsx for TypeScript with JSX (React).


Basic Types

Primitive Types

// String

let name: string = "John";

// Number

let age: number = 30;

// Boolean

let isActive: boolean = true;

// Null and Undefined

let nothing: null = null;

let notDefined: undefined = undefined;

Arrays

// Array of strings

let names: string[] = ["Alice", "Bob"];

// Array of numbers

let scores: number[] = [100, 95, 88];

// Alternative syntax

let items: Array = ["a", "b", "c"];

Objects

// Inline type

let user: { name: string; age: number } = {

name: "John",

age: 30,

};

// Better: use interface (see below)


Interfaces

Define the shape of objects:

interface User {

id: string;

name: string;

email: string;

age?: number; // Optional property (?)

readonly createdAt: Date; // Can't be changed

}

const user: User = {

id: "1",

name: "John",

email: "john@example.com",

createdAt: new Date(),

};

user.name = "Jane"; // OK

user.createdAt = new Date(); // Error: readonly

Extending Interfaces

interface Person {

name: string;

age: number;

}

interface Employee extends Person {

employeeId: string;

department: string;

}

const employee: Employee = {

name: "John",

age: 30,

employeeId: "E001",

department: "Engineering",

};


Type Aliases

Similar to interfaces but can define any type:

// Union type

type Status = "pending"

"approved"
"rejected";

// Object type

type Point = {

x: number;

y: number;

};

// Function type

type Callback = (data: string) => void;

Interface vs Type

FeatureInterfaceType
Object shapes
Extending✅ (with &)
Union types
Declaration merging

Rule of thumb: Use interface for objects, type for everything else.


Functions

Typed Parameters and Return

function greet(name: string): string {

return Hello, ${name}!;

}

// Arrow function

const add = (a: number, b: number): number => a + b;

Optional and Default Parameters

function greet(name: string, greeting?: string): string {

return ${greeting || "Hello"}, ${name}!;

}

function greetWithDefault(name: string, greeting: string = "Hello"): string {

return ${greeting}, ${name}!;

}

Function Types

type MathOperation = (a: number, b: number) => number;

const add: MathOperation = (a, b) => a + b;

const multiply: MathOperation = (a, b) => a * b;


Union Types

A value can be one of several types:

type StringOrNumber = string | number;

function printId(id: string | number) {

console.log(ID: ${id});

}

printId("abc"); // OK

printId(123); // OK

printId(true); // Error

Narrowing

TypeScript narrows types based on checks:

function printId(id: string | number) {

if (typeof id === "string") {

// TypeScript knows id is string here

console.log(id.toUpperCase());

} else {

// TypeScript knows id is number here

console.log(id.toFixed(2));

}

}


Generics

Make reusable types flexible:

// Generic function

function identity(value: T): T {

return value;

}

identity("hello"); // Returns string

identity(42); // Returns number

// TypeScript infers the type

identity("hello"); // Inferred as string

Generic Interfaces

interface ApiResponse {

data: T;

status: number;

message: string;

}

interface User {

id: string;

name: string;

}

// Response with User data

type UserResponse = ApiResponse;

const response: UserResponse = {

data: { id: "1", name: "John" },

status: 200,

message: "Success",

};

Generic Constraints

interface HasId {

id: string;

}

// T must have an id property

function getById(items: T[], id: string): T | undefined {

return items.find(item => item.id === id);

}


Practical React Examples

Typed Props

interface ButtonProps {

label: string;

onClick: () => void;

variant?: "primary" | "secondary";

disabled?: boolean;

}

function Button({ label, onClick, variant = "primary", disabled }: ButtonProps) {

return (

onClick={onClick}

disabled={disabled}

className={btn btn-${variant}}

>

{label}

);

}

Typed State

interface User {

id: string;

name: string;

email: string;

}

function UserProfile() {

const [user, setUser] = useState(null);

const [loading, setLoading] = useState(true);

// TypeScript knows user might be null

return (

{user?.name || "Loading..."}

);

}

Event Handlers

function Form() {

const handleChange = (event: React.ChangeEvent) => {

console.log(event.target.value);

};

const handleSubmit = (event: React.FormEvent) => {

event.preventDefault();

};

return (

);

}


Common Patterns

API Responses

interface ApiError {

message: string;

code: number;

}

type ApiResponse =

| { success: true; data: T }

| { success: false; error: ApiError };

async function fetchUser(id: string): Promise> {

try {

const response = await fetch(/api/users/${id});

const data = await response.json();

return { success: true, data };

} catch (error) {

return { success: false, error: { message: "Failed", code: 500 } };

}

}

// Usage

const result = await fetchUser("1");

if (result.success) {

console.log(result.data.name); // TypeScript knows data exists

} else {

console.log(result.error.message); // TypeScript knows error exists

}

Props with Children

interface CardProps {

title: string;

children: React.ReactNode;

}

function Card({ title, children }: CardProps) {

return (

{title}

{children}

);

}

Object Keys

interface Theme {

primary: string;

secondary: string;

accent: string;

}

const theme: Theme = {

primary: "#3b82f6",

secondary: "#6b7280",

accent: "#10b981",

};

// Type-safe key access

type ThemeKey = keyof Theme; // "primary"

"secondary"
"accent"

function getColor(key: ThemeKey): string {

return theme[key];

}


Migration Tips

Gradual Adoption

1. Rename .js to .ts

2. Add types where TypeScript complains

3. Enable stricter options over time

tsconfig.json Settings

Start lenient:

{

"compilerOptions": {

"strict": false,

"noImplicitAny": false

}

}

Move toward strict:

{

"compilerOptions": {

"strict": true,

"noImplicitAny": true,

"strictNullChecks": true

}

}

Using any (Temporarily)

When stuck, use any and fix later:

// TODO: Type this properly

const data: any = fetchSomething();

But mark it to fix later. Too much any defeats the purpose.


Common Errors

"Object is possibly undefined"

// Error

const user: User | undefined = getUser();

console.log(user.name); // Error!

// Fix 1: Check first

if (user) {

console.log(user.name);

}

// Fix 2: Optional chaining

console.log(user?.name);

// Fix 3: Non-null assertion (when you're sure)

console.log(user!.name);

"Type 'X' is not assignable to type 'Y'"

interface User {

name: string;

}

// Error: missing required property

const user: User = {}; // Error!

// Fix

const user: User = { name: "John" };

"Argument of type 'X' is not assignable"

function greet(name: string) {}

greet(123); // Error!

greet("John"); // OK


Tools and Resources

  • VS Code - Best TypeScript support

Conclusion

TypeScript benefits:

AspectWithout TSWith TS
Bug detectionRuntimeCompile time
IDE supportBasicExcellent
RefactoringScarySafe
DocumentationCommentsTypes
Team onboardingRead all codeTypes explain themselves

Start by adding types to function parameters and return values. As you get comfortable, enable stricter settings.

Related guides:

Learn Next.js with TypeScript →

Share:

Related Articles

View all

Ready to Migrate Your WordPress Site?

Use our free tool to export your WordPress content in minutes.

Start Free Migration