Skip to main content

Module 34: TypeScript Introduction

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. It adds static typing, interfaces, and advanced features for better developer experience.


1. Why TypeScript?

1.1 Benefits

// JavaScript - Runtime errors
function greet(name) {
return `Hello, ${name.toUpperCase()}`;
}

greet(123); // Runtime error: name.toUpperCase is not a function

// TypeScript - Compile-time errors
function greet(name: string): string {
return `Hello, ${name.toUpperCase()}`;
}

greet(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'

// Benefits:
// ✅ Catch errors before runtime
// ✅ Better IDE support (autocomplete, refactoring)
// ✅ Self-documenting code
// ✅ Easier refactoring
// ✅ Better collaboration on large projects

1.2 Setup

# Install TypeScript
npm install -g typescript

# Check version
tsc --version

# Initialize TypeScript project
tsc --init # Creates tsconfig.json

# Compile TypeScript file
tsc index.ts # Creates index.js

# Watch mode
tsc --watch

# Project setup
npm init -y
npm install --save-dev typescript @types/node

# tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

2. Basic Types

2.1 Primitive Types

// String
let name: string = 'John';
let message: string = `Hello, ${name}`;

// Number
let age: number = 30;
let price: number = 19.99;
let hex: number = 0xf00d;

// Boolean
let isActive: boolean = true;
let isLoggedIn: boolean = false;

// Null and Undefined
let nothing: null = null;
let notDefined: undefined = undefined;

// Any (avoid when possible)
let anything: any = 'hello';
anything = 42;
anything = true;

// Unknown (safer than any)
let userInput: unknown = getUserInput();
if (typeof userInput === 'string') {
console.log(userInput.toUpperCase()); // OK after type checking
}

// Void (no return value)
function logMessage(message: string): void {
console.log(message);
}

// Never (never returns)
function throwError(message: string): never {
throw new Error(message);
}

function infiniteLoop(): never {
while (true) {}
}

2.2 Arrays and Tuples

// Arrays
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ['Alice', 'Bob', 'Charlie'];

// Mixed types with union
let mixed: (number | string)[] = [1, 'two', 3, 'four'];

// Readonly arrays
let readonlyNumbers: ReadonlyArray<number> = [1, 2, 3];
// readonlyNumbers.push(4); // Error!

// Tuples (fixed length and types)
let person: [string, number] = ['John', 30];
let [name, age] = person; // Destructuring

// Optional tuple elements
let optional: [string, number?] = ['John'];

// Rest in tuples
let rest: [string, ...number[]] = ['John', 1, 2, 3];

// Named tuples
type Point = [x: number, y: number];
let point: Point = [10, 20];

2.3 Objects and Interfaces

// Object type
let user: { name: string; age: number } = {
name: 'John',
age: 30
};

// Interface
interface User {
name: string;
age: number;
email?: string; // Optional property
readonly id: number; // Readonly property
}

let user: User = {
id: 1,
name: 'John',
age: 30
};

// user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property

// Extending interfaces
interface Admin extends User {
role: string;
permissions: string[];
}

let admin: Admin = {
id: 1,
name: 'Admin',
age: 35,
role: 'superadmin',
permissions: ['read', 'write', 'delete']
};

// Index signatures
interface Dictionary {
[key: string]: string;
}

let dict: Dictionary = {
hello: 'world',
foo: 'bar'
};

// Function types in interfaces
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}

let calc: Calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};

3. Functions

3.1 Function Types

// Function declaration
function add(a: number, b: number): number {
return a + b;
}

// Function expression
const multiply = function(a: number, b: number): number {
return a * b;
};

// Arrow function
const divide = (a: number, b: number): number => {
return a / b;
};

// Optional parameters
function greet(name: string, greeting?: string): string {
return `${greeting || 'Hello'}, ${name}`;
}

greet('John'); // "Hello, John"
greet('John', 'Hi'); // "Hi, John"

// Default parameters
function createUser(name: string, age: number = 18): User {
return { name, age };
}

// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}

sum(1, 2, 3, 4); // 10

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

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

// Function overloading
function format(value: string): string;
function format(value: number): string;
function format(value: boolean): string;
function format(value: any): string {
return String(value);
}

format('hello'); // OK
format(42); // OK
format(true); // OK

// Generic functions
function identity<T>(value: T): T {
return value;
}

identity<number>(42); // number
identity<string>('hello'); // string
identity(true); // boolean (inferred)

function getArray<T>(items: T[]): T[] {
return [...items];
}

getArray<number>([1, 2, 3]);
getArray(['a', 'b', 'c']);

4. Advanced Types

4.1 Union and Intersection Types

// Union types (OR)
type StringOrNumber = string | number;

function format(value: StringOrNumber): string {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value.toFixed(2);
}

// Discriminated unions
type Square = {
kind: 'square';
size: number;
};

type Circle = {
kind: 'circle';
radius: number;
};

type Shape = Square | Circle;

function getArea(shape: Shape): number {
switch (shape.kind) {
case 'square':
return shape.size * shape.size;
case 'circle':
return Math.PI * shape.radius ** 2;
}
}

// Intersection types (AND)
interface Colorful {
color: string;
}

interface Movable {
speed: number;
}

type ColorfulMovable = Colorful & Movable;

let car: ColorfulMovable = {
color: 'red',
speed: 100
};

// Type guards
function isString(value: any): value is string {
return typeof value === 'string';
}

function process(value: string | number) {
if (isString(value)) {
console.log(value.toUpperCase()); // TypeScript knows it's a string
} else {
console.log(value.toFixed(2));
}
}

4.2 Type Aliases and Literal Types

// Type aliases
type ID = string | number;
type User = {
id: ID;
name: string;
age: number;
};

// Literal types
type Direction = 'north' | 'south' | 'east' | 'west';
type Status = 'pending' | 'active' | 'inactive';

let direction: Direction = 'north'; // OK
// direction = 'up'; // Error!

// Numeric literal types
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function rollDice(): DiceRoll {
return (Math.floor(Math.random() * 6) + 1) as DiceRoll;
}

// Template literal types
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/${string}`;
type Route = `${HTTPMethod} ${Endpoint}`;

let route: Route = 'GET /users'; // OK
// let invalidRoute: Route = 'PATCH users'; // Error!

4.3 Utility Types

// Partial<T> - Make all properties optional
interface User {
id: number;
name: string;
email: string;
}

type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }

function updateUser(id: number, updates: Partial<User>) {
// ...
}

// Required<T> - Make all properties required
type RequiredUser = Required<PartialUser>;

// Readonly<T> - Make all properties readonly
type ReadonlyUser = Readonly<User>;

// Pick<T, K> - Pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }

// Omit<T, K> - Omit specific properties
type UserWithoutId = Omit<User, 'id'>;
// { name: string; email: string; }

// Record<K, T> - Create object type with specific keys
type Roles = 'admin' | 'user' | 'guest';
type RolePermissions = Record<Roles, string[]>;

let permissions: RolePermissions = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read']
};

// Exclude<T, U> - Exclude types from union
type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>; // 'c'

// Extract<T, U> - Extract types from union
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // 'a' | 'b'

// NonNullable<T> - Exclude null and undefined
type T3 = NonNullable<string | number | null | undefined>; // string | number

// ReturnType<T> - Get return type of function
function getUser() {
return { id: 1, name: 'John' };
}

type User = ReturnType<typeof getUser>; // { id: number; name: string; }

// Parameters<T> - Get parameter types of function
function createUser(name: string, age: number) {}

type UserParams = Parameters<typeof createUser>; // [string, number]

5. Classes

5.1 Class Basics

class Person {
// Properties
name: string;
age: number;

// Constructor
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}

// Methods
greet(): string {
return `Hello, I'm ${this.name}`;
}
}

const person = new Person('John', 30);
console.log(person.greet());

// Access modifiers
class User {
public id: number; // Accessible everywhere
protected name: string; // Accessible in class and subclasses
private password: string; // Accessible only in class

constructor(id: number, name: string, password: string) {
this.id = id;
this.name = name;
this.password = password;
}

private hashPassword(): string {
return `hashed_${this.password}`;
}
}

// Shorthand constructor
class Product {
constructor(
public id: number,
public name: string,
private price: number
) {}

getPrice(): number {
return this.price;
}
}

// Readonly properties
class Circle {
readonly radius: number;

constructor(radius: number) {
this.radius = radius;
}
}

const circle = new Circle(10);
// circle.radius = 20; // Error!

// Static members
class MathUtils {
static PI = 3.14159;

static circle Area(radius: number): number {
return this.PI * radius ** 2;
}
}

console.log(MathUtils.PI);
console.log(MathUtils.circleArea(10));

// Getters and setters
class Temperature {
private _celsius: number = 0;

get celsius(): number {
return this._celsius;
}

set celsius(value: number) {
if (value < -273.15) {
throw new Error('Temperature below absolute zero');
}
this._celsius = value;
}

get fahrenheit(): number {
return (this._celsius * 9/5) + 32;
}

set fahrenheit(value: number) {
this.celsius = (value - 32) * 5/9;
}
}

5.2 Inheritance and Abstract Classes

// Inheritance
class Animal {
constructor(public name: string) {}

move(distance: number): void {
console.log(`${this.name} moved ${distance}m`);
}
}

class Dog extends Animal {
bark(): void {
console.log('Woof! Woof!');
}
}

class Bird extends Animal {
fly(distance: number): void {
console.log(`${this.name} flew ${distance}m`);
}
}

const dog = new Dog('Buddy');
dog.bark();
dog.move(10);

// Abstract classes
abstract class Shape {
abstract getArea(): number;
abstract getPerimeter(): number;

describe(): string {
return `Area: ${this.getArea()}, Perimeter: ${this.getPerimeter()}`;
}
}

class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}

getArea(): number {
return this.width * this.height;
}

getPerimeter(): number {
return 2 * (this.width + this.height);
}
}

// const shape = new Shape(); // Error: Cannot create instance of abstract class
const rect = new Rectangle(10, 5);
console.log(rect.describe());

// Implementing interfaces
interface Drawable {
draw(): void;
}

interface Resizable {
resize(scale: number): void;
}

class Canvas implements Drawable, Resizable {
draw(): void {
console.log('Drawing on canvas');
}

resize(scale: number): void {
console.log(`Resizing by ${scale}`);
}
}

6. Generics

6.1 Generic Functions and Classes

// Generic function
function wrap<T>(value: T): T[] {
return [value];
}

wrap<number>(42); // number[]
wrap<string>('hello'); // string[]
wrap(true); // boolean[] (inferred)

// Generic with constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}

const user = { id: 1, name: 'John', age: 30 };
getProperty(user, 'name'); // OK
// getProperty(user, 'email'); // Error: 'email' doesn't exist

// Generic class
class Box<T> {
private value: T;

constructor(value: T) {
this.value = value;
}

getValue(): T {
return this.value;
}

setValue(value: T): void {
this.value = value;
}
}

const numberBox = new Box<number>(42);
const stringBox = new Box<string>('hello');

// Generic interface
interface Repository<T> {
getById(id: number): T;
getAll(): T[];
create(item: T): T;
update(id: number, item: T): T;
delete(id: number): void;
}

class UserRepository implements Repository<User> {
getById(id: number): User {
// Implementation
return { id, name: 'John', age: 30 };
}

getAll(): User[] {
return [];
}

create(user: User): User {
return user;
}

update(id: number, user: User): User {
return user;
}

delete(id: number): void {
// Implementation
}
}

// Multiple type parameters
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}

const merged = merge({ name: 'John' }, { age: 30 });
// { name: string; age: number; }

7. TypeScript with JavaScript Libraries

7.1 Type Declarations

// Install type definitions
// npm install --save-dev @types/node
// npm install --save-dev @types/express
// npm install --save-dev @types/lodash

// Using typed libraries
import express from 'express';
import _ from 'lodash';

const app = express();

app.get('/users', (req, res) => {
res.json({ users: [] });
});

// Creating declaration files (.d.ts)
// math.d.ts
export function add(a: number, b: number): number;
export function subtract(a: number, b: number): number;

// Using ambient declarations
// global.d.ts
declare global {
interface Window {
myCustomProperty: string;
}
}

export {};

// Now you can use it
window.myCustomProperty = 'Hello';

// Module augmentation
// express.d.ts
import 'express';

declare module 'express' {
interface Request {
user?: {
id: number;
name: string;
};
}
}

// Now you can use it
app.get('/profile', (req, res) => {
console.log(req.user?.name);
});

8. TypeScript Configuration

8.1 tsconfig.json Options

{
"compilerOptions": {
// Language & Environment
"target": "ES2020", // Target ECMAScript version
"lib": ["ES2020", "DOM"], // Type libraries to include
"jsx": "react", // JSX support

// Modules
"module": "commonjs", // Module system
"moduleResolution": "node", // Module resolution strategy
"baseUrl": "./", // Base directory
"paths": { // Path mapping
"@/*": ["src/*"]
},
"resolveJsonModule": true, // Import JSON files

// Emit
"outDir": "./dist", // Output directory
"rootDir": "./src", // Input directory
"declaration": true, // Generate .d.ts files
"sourceMap": true, // Generate source maps
"removeComments": true, // Remove comments
"noEmit": false, // Don't emit files

// Type Checking
"strict": true, // Enable all strict options
"noImplicitAny": true, // Error on implicit any
"strictNullChecks": true, // Strict null checks
"strictFunctionTypes": true, // Strict function types
"noUnusedLocals": true, // Error on unused locals
"noUnusedParameters": true, // Error on unused parameters
"noImplicitReturns": true, // Error on missing return

// Interop
"esModuleInterop": true, // Emit helpers for import/export
"allowSyntheticDefaultImports": true,

// Advanced
"skipLibCheck": true, // Skip type checking of .d.ts files
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
}

Summary

In this module, you learned:

  • ✅ TypeScript benefits and setup
  • ✅ Basic and advanced types
  • ✅ Functions and classes with types
  • ✅ Interfaces and type aliases
  • ✅ Generics and utility types
  • ✅ TypeScript with JavaScript libraries
  • ✅ Configuration options
  • ✅ Best practices
Next Steps

In Module 35, you'll learn about Best Practices and Professional Development, mastering advanced techniques and industry standards.


Practice Exercises

  1. Convert a JavaScript project to TypeScript
  2. Create a typed REST API with Express
  3. Build a generic data structure (Stack, Queue)
  4. Implement type-safe form validation
  5. Create custom utility types
  6. Build a typed React component library
  7. Write type declarations for a JavaScript library
  8. Configure strict TypeScript for a project

Additional Resources