Back to: Angular Tutorials For Beginners and Professionals
Angular is not built on plain JavaScript. It is built on TypeScript, and understanding TypeScript properly is one of the most important steps in becoming a confident Angular developer. Many beginners try to learn Angular without understanding TypeScript fundamentals, which often leads to confusion, fear of errors, and difficulty reading real-world Angular code.
This chapter focuses on building a strong TypeScript foundation specifically for Angular development. You will learn why Angular uses TypeScript, how TypeScript improves code safety and clarity, and how Angular projects use TypeScript features in real applications.
What will you learn in this chapter?
By the end of this chapter, you will not just “write” TypeScript; you will understand Angular codebases comfortably, even in large enterprise projects. Now, you will understand:
- Why Angular uses TypeScript instead of JavaScript.
- What TypeScript really is and how it works.
- How data types improve safety.
- How Type Inference reduces code without losing safety.
- How to model data using Interfaces and Type Aliases.
- How functions, classes, and constructors work.
- How Angular uses access modifiers.
- Why is composition preferred over inheritance?
- How generics enable reusable logic.
- How enums, tuples, union, and intersection types work.
Why Angular Uses TypeScript Instead of JavaScript?
JavaScript is flexible and powerful, but that flexibility can become a problem in large applications. In small scripts, JavaScript works well. But in enterprise-scale applications with hundreds of files, multiple developers, and long-term maintenance, JavaScript can easily become difficult to manage.
Angular uses TypeScript because it adds Structure, Safety, and Clarity on top of JavaScript. TypeScript helps Angular developers by:
- Catching Errors before the app runs.
- Making code Self-Documenting.
- Improving Editor Support (auto-complete, refactoring, navigation).
- Making large codebases Easier to understand and maintain.
- Supporting Object-Oriented Patterns used heavily in Angular.
In simple terms, JavaScript allows anything. TypeScript guides you toward correctness. This guidance becomes extremely valuable as Angular applications grow.
What Is TypeScript?
TypeScript is a Superset of JavaScript developed by Microsoft. This means:
- Every valid JavaScript code is valid TypeScript code
- TypeScript adds extra features on top of JavaScript
- TypeScript code is compiled into JavaScript before running in the browser
Browsers do not understand TypeScript directly. During the Angular build process, TypeScript is converted into regular JavaScript that browsers can execute. So, TypeScript exists to help developers, not browsers.
Why TypeScript Matters in Real Angular Projects?
In real Angular projects:
- Applications contain hundreds of components and services
- Data flows through APIs, forms, and models
- Multiple developers work on the same codebase
- Code must remain readable even after years
TypeScript ensures that:
- Data shapes are clear
- Incorrect usage is detected early
- Refactoring is safe
- Code behaves predictably
That is why Angular enforces TypeScript by default.
What Is a Data Type in TypeScript?
A data type describes what kind of value a variable is allowed to store. In TypeScript, every variable has a known type that tells the compiler what the variable represents and how it can be used safely. For example:
- Is the value a number or text?
- Can it be empty?
- Can it change later?
- What operations are allowed on it?
TypeScript uses data types to understand your intention as a developer, not just the value itself.
Data Types in TypeScript
TypeScript introduces static typing, which means variables have known types. The most commonly used basic data types are:
- Number: Used to store numeric values such as age, price, quantity, salary, or calculation results.
- String: Used to store textual data such as names, usernames, emails, messages, or labels.
- Boolean: Used to store true/false values, mainly for conditions, flags, and decision-making logic.
- Any: Allows a variable to hold any type of value, effectively disabling type checking for that variable. Use any only when the type is completely unknown or dynamic (not recommended in Angular).
- Unknown: Similar to any, but safer, it forces you to check the type before using the value. Used when data comes from an external source and must be validated before use.
- Void: Represents the absence of a value and is mainly used as a function return type when nothing is returned. A function that only performs an action but returns nothing uses void.
- Null: Represents an intentional empty value, meaning “this variable has no value right now”.
- Undefined: Represents a variable that has been declared but not assigned any value. Indicates that a value has not been initialized yet.
Examples to Understand Data Types Using TypeScript
Example 1: Basic Variable Declarations
- let age: number = 30;
- let username: string = “pranaya”;
- let isLoggedIn: boolean = true;
Note: TypeScript will not allow assigning text to age or a number to username.
Example 2: Preventing Wrong Assignments
- let price: number = 500;
- price = “five hundred”; //Error: Type ‘string’ is not assignable to type ‘number’
Note: TypeScript catches this mistake before the app runs.
Example 3: Using any (Not Recommended)
- let data: any;
- data = 10;
- data = “hello”;
- data = true;
Note: This works, but TypeScript gives up safety, which is risky in Angular applications.
Example 4: Using unknown (Safer Alternative)
let response: unknown;
response = "API data";
// Error: Object is of type 'unknown'
response.toUpperCase();
You must first check the type:
if (typeof response === "string") {
response.toUpperCase();
}
This is why unknown is safer than any.
Example 5: void Return Type
function logMessage(message: string): void {
console.log(message);
}
This function performs an action but does not return anything.
Example 6: null and undefined
- let selectedUser: string | null = null;
- let email: string | undefined;
Here,
- null → value is intentionally empty
- undefined → value not assigned yet
Both are common in Angular forms and API handling.
What Is Type Inference in TypeScript?
Type Inference is a feature of TypeScript where the compiler automatically determines the data type of a variable based on the value assigned to it, even if you do not explicitly mention the type. In simple words, TypeScript “understands” the type by looking at the value. So, instead of you telling TypeScript this is a number, TypeScript figures it out on its own.
How Type Inference Works
When you declare a variable and assign a value at the same time, TypeScript examines the value and infers the type automatically. For example:
- Assign a number → inferred as number
- Assign text → inferred as string
- Assign true/false → inferred as boolean
Once inferred, the type is locked and cannot be changed.
Example to Understand Type Inference in TypeScript
Example: Variable Declaration with Inferred Type
let totalAmount = 1500;
Here, we did not explicitly specify the type. What TypeScript understands:
- The value 1500 is a number
- So totalAmount is inferred as a number
Now, TypeScript enforces this rule.
- totalAmount = 2000; // Allowed
- totalAmount = “2000”; // Error: Type ‘string’ is not assignable to type ‘number’
Even though we never wrote : number, TypeScript still protects the variable.
Note: Type inference works best when a value is assigned at declaration time.
Type Inference vs Explicit Typing
Both are correct and used together.
- // Using Type Inference
- let count = 10;
- // Using Explicit Type
- let count: number = 10;
In real Angular projects:
- Use Type Inference when the type is obvious
- Use Explicit Types when:
-
- The variable is declared first and assigned later
- The logic is complex
- Public APIs, models, or function parameters are involved
-
What Is an Interface in TypeScript?
An interface in TypeScript is a Blueprint (Or Contract) that defines the structure of an object, that is, what properties it must have and what types those properties should be. An interface does not contain values or logic. It only describes how an object should look. In simple words: An interface tells TypeScript, “Any object of this type must follow this shape.”
Why Do We Need Interfaces in TypeScript?
In real Angular applications, we work heavily with objects, especially:
- API responses.
- Request payloads (DTOs).
- Form data.
- Component state.
- Configuration objects.
Without interfaces, objects can:
- Miss required properties.
- Contain wrong data types.
- Break code silently at runtime.
Interfaces solve this problem by adding structure and safety.
Example to Understand Interfaces in TypeScript
Step 1: Define an Interface
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
This interface defines the shape of a User object.
Step 2: Use the Interface
let user: User = {
id: 1,
name: "Pranaya",
email: "pranaya@example.com",
isActive: true
};
This object is valid because it matches the interface exactly.
Step 3: TypeScript Prevents Mistakes
Missing Property
let user: User = {
id: 1,
name: "Pranaya",
email: "pranaya@example.com"
};
TypeScript error: Property ‘isActive’ is missing
Wrong Data Type
let user: User = {
id: "one",
name: "Pranaya",
email: "pranaya@example.com",
isActive: true
};
TypeScript error: Type ‘string’ is not assignable to type ‘number’
What Are Functions in TypeScript?
A function is a reusable block of code that performs a specific task and can be executed whenever needed. Instead of repeating the same logic over and over, we place it in a function and call it whenever required. In TypeScript, functions work just like JavaScript functions, but with an important difference: TypeScript allows functions to be strongly typed. This means:
- Function parameters can have types
- Function return values can have types
- TypeScript checks correctness before the code runs
Why Do We Need Functions in TypeScript?
Functions are essential because real applications involve logic, not just values. In Angular applications, functions are used everywhere:
- Handling user actions (button clicks, form submissions)
- Processing data
- Calling APIs
- Validating input
- Performing calculations
- Sharing reusable logic across components and services
Without functions:
- Code becomes repetitive
- Logic becomes hard to maintain
- Applications become difficult to extend
Example to Understand Functions in TypeScript
Example 1: Simple Function with Types
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
Explanation:
- price must be a number.
- quantity must be a number.
- The function always returns a number.
Using the Function
- let total = calculateTotal(500, 2); //Works
- calculateTotal(“500”, 2); // Error
TypeScript catches the mistake before execution.
Example 2: Function Without Return Value (void)
function logMessage(message: string): void {
console.log(message);
}
This function:
- Performs an action
- Does not return anything
- Uses void as return type
This is very common in Angular for:
- Logging
- Event handling
- Side-effect operations
What Are Classes in TypeScript?
A class in TypeScript is a blueprint for creating objects that combine data (properties) and behaviour (methods) into a single structured unit. Instead of working with scattered variables and functions, a class allows you to:
- Group related data together
- Define how that data should behave
- Create multiple objects with the same structure and behaviour
In simple words: A class represents a real-world entity in code.
Why Do We Need Classes in TypeScript?
Classes are essential because real applications, especially Angular applications, are built using object-oriented design. In Angular:
- Components are classes
- Services are classes
- Guards are classes
- Interceptors are classes
- Pipes are classes
Without classes, Angular’s architecture would not be possible.
Basic Structure of a Class in TypeScript
class ClassName {
// properties
constructor() {
// initialization
}
// methods
}
Example to Understand Classes in TypeScript
Example: User Class
class User {
id: number;
name: string;
email: string;
isActive: boolean;
constructor(id: number, name: string, email: string, isActive: boolean) {
this.id = id;
this.name = name;
this.email = email;
this.isActive = isActive;
}
getStatus(): string {
return this.isActive ? "Active" : "Inactive";
}
}
Creating an Object from the Class
- const user1 = new User(1, “Pranaya”, “pranaya@example.com”, true);
Now:
- user1 is an object created from User
- It has properties and methods defined by the class
Using Class Methods
- console.log(user1.getStatus()); // “Active”
The method operates on the data inside the object.
What Is a Constructor in TypeScript?
A constructor is a special method inside a class that is automatically executed when an object of that class is created. Its main purpose is to:
- Initialize the object
- Assign initial values to class properties
- Prepare the object for use
In simple words, A constructor sets up the object when it is born.
Why Do We Need Constructors?
In real applications, objects are not created empty. They usually need initial data to work correctly. Constructors ensure that:
- Required data is provided at object creation time
- Objects start in a valid and usable state
- Initialization logic is centralized in one place
Without constructors, objects could be created in an incomplete or incorrect state.
Basic Syntax of a Constructor
class Example {
constructor() {
// initialization logic
}
}
A class can have only one constructor.
Example to Understand Constructors in TypeScript
Example 1: Constructor Initializing Properties
class User {
id: number;
name: string;
email: string;
constructor(id: number, name: string, email: string) {
this.id = id;
this.name = name;
this.email = email;
}
}
Here:
- Constructor requires id, name, and email
- Object cannot be created without providing these values
Creating an Object
- const user = new User(1, “Pranaya”, “pranaya@example.com”);
Object is created in a valid state. Missing arguments would cause a TypeScript error
What Are Access Modifiers in TypeScript?
Access modifiers are keywords used in classes to control who can access class properties and methods. They define the visibility and accessibility of data and behaviour inside a class. In simple words, Access modifiers decide what is visible and what is hidden. They help you protect your class from being used in the wrong way.
Why Do We Need Access Modifiers?
In real applications, especially Angular applications, classes grow large and complex. If everything is accessible from everywhere:
- Data can be changed accidentally
- Internal logic can be misused
- Bugs become hard to trace
- Code becomes fragile
Access modifiers solve this by enforcing boundaries inside classes.
Types of Access Modifiers in TypeScript
TypeScript supports four main access modifiers:
- public
- private
- protected
- readonly
public
Members marked as public are accessible from anywhere. If no modifier is specified, TypeScript treats the member as public by default. Use public for:
- Properties and methods meant to be used outside the class
- Data that components or templates need to access
Example
class User {
public name: string;
constructor(name: string) {
this.name = name;
}
}
const user = new User("Pranaya");
console.log(user.name); // Accessible
private
Members marked as private are accessible only inside the same class. The private modifier protects internal logic from misuse or incorrect modification. They cannot be accessed from:
- Outside the class
- Child classes
Example
class BankAccount {
private balance: number = 0;
deposit(amount: number) {
this.balance += amount;
}
}
const account = new BankAccount();
// account.balance = 1000; // Error: private property
Only the class itself controls the balance.
protected
Members marked as protected are accessible:
- Inside the class
- Inside child (derived) classes
But not accessible from outside objects. Use protected when:
- A property or method should be reusable in subclasses
- But should not be accessed directly by external code
Example
class Vehicle {
protected speed: number = 0;
}
class Car extends Vehicle {
accelerate() {
this.speed += 10;
}
}
Here:
- speed is accessible inside Car.
- But not accessible from a Car object directly.
readonly
The readonly allows a property to be assigned only once, usually in the constructor. After initialization, the value cannot be changed. It ensures certain values remain constant after object creation.
Example
class Order {
readonly orderId: number;
constructor(orderId: number) {
this.orderId = orderId;
}
}
const order = new Order(101);
// order.orderId = 200; // Error: cannot assign to readonly property
What Is Inheritance in TypeScript?
Inheritance is an object-oriented concept where one class (child or derived class) reuses and extends the properties and methods of another class (parent or base class). Instead of writing the same code over and over, a child class can inherit the behavior from a parent class and add its own specific functionality. In simple words, Inheritance allows one class to build on top of another class.
Why Do We Need Inheritance in TypeScript?
In real applications, many entities share common characteristics. Inheritance helps represent these relationships cleanly and logically.
Without inheritance:
- Code gets duplicated
- Changes must be made in many places
- Bugs become harder to manage
Inheritance allows you to:
- Reuse common logic
- Avoid duplication
- Maintain consistency
- Model real-world “is-a” relationships
Example to Understand Inheritance in TypeScript
Step 1: Parent (Base) Class
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getDetails(): string {
return `${this.name} is ${this.age} years old`;
}
}
This class defines common properties and behavior.
Step 2: Child (Derived) Class
class Student extends Person {
studentId: number;
constructor(name: string, age: number, studentId: number) {
super(name, age); // Call parent constructor
this.studentId = studentId;
}
getStudentInfo(): string {
return `${this.name} has Student ID ${this.studentId}`;
}
}
Here:
- Student inherits name, age, and getDetails() from Person.
- super() calls the parent constructor.
- The student adds their own behavior.
Step 3: Using the Child Class
- const student = new Student(“Pranaya”, 21, 1001);
- console.log(student.getDetails()); // Inherited method
- console.log(student.getStudentInfo()); // Child-specific method
Important Rules About Inheritance
- A child class can access:
-
- public members of the parent.
- protected members of the parent.
-
- A child class cannot access its parent class’s private members.
- TypeScript supports single inheritance (one parent class).
What Is Composition in TypeScript?
Composition is an object-oriented design principle in which a class uses other classes as properties rather than inheriting from them. In composition:
- One class contains another class
- Functionality is achieved by delegation, not inheritance
In simple words, Composition means “has-a” relationship, not “is-a”.
Why Do We Need Composition in TypeScript?
While inheritance allows code reuse, it can create tight coupling between classes. As applications grow, this tight coupling becomes hard to manage and risky to change. Composition solves this by:
- Making classes more flexible
- Reducing dependency between classes
- Allowing behaviour to be swapped or changed easily
This is why modern TypeScript and Angular strongly prefer composition over inheritance.
Example to Understand Composition in TypeScript
Step 1: Create Independent Classes
class Logger {
log(message: string): void {
console.log(message);
}
}
class ApiService {
fetchData(): string {
return "Data from API";
}
}
These classes do one job each.
Step 2: Use Composition
class UserService {
private logger: Logger;
private apiService: ApiService;
constructor() {
this.logger = new Logger();
this.apiService = new ApiService();
}
loadUser(): void {
const data = this.apiService.fetchData();
this.logger.log(`User loaded: ${data}`);
}
}
Here:
- UserService has a Logger.
- UserService has an ApiService.
- No inheritance is involved.
This is a composition.
What Are Generics in TypeScript?
Generics allow you to write reusable, type-safe code where the exact data type is determined at runtime. Instead of fixing a type like number or string, generics use a type placeholder (commonly T) that represents any type. In simple words: Generics let you write logic once and use it safely with different data types.
Why Do We Need Generics in TypeScript?
In real applications (especially Angular applications), the same logic is often needed for different types of data.
For example:
- Fetching users, products, or orders
- Handling API responses
- Writing utility functions
- Managing collections
Without generics, you would either:
- Duplicate code for each type, or
- Use any and lose type safety
Generics solve this problem without sacrificing safety.
Basic Syntax of Generics
function example<T>(value: T): T {
return value;
}
Here:
- T is a type parameter
- T will be replaced by the actual type when the function is used
Example to Understand Generics in TypeScript
Example 1: Generic Function
function identity<T>(value: T): T {
return value;
}
Usage:
- identity<number>(10); // T becomes number
- identity<string>(“Hello”); // T becomes string
TypeScript automatically knows the return type.
What Are Enums in TypeScript?
An Enum (Enumeration) is a special TypeScript feature used to define a fixed set of named values. Instead of using raw numbers or strings repeatedly, enums give those values meaningful names. In simple words, Enums allow us to represent a limited set of possible values in a safe and readable way.
Why Do We Need Enums in TypeScript?
In real applications, many values come from a known and limited set, such as:
- User roles (Admin, User, Guest)
- Status values (Pending, Approved, Rejected)
- Application states (Loading, Success, Error)
Without enums, developers often use:
- Magic numbers
- Hard-coded strings
This leads to:
- Typos
- Inconsistent values
- Hard-to-maintain code
Enums solve this by centralizing and restricting allowed values.
Basic Enum Example
Example 1: Numeric Enum
enum OrderStatus {
Pending,
Shipped,
Delivered,
Cancelled
}
TypeScript assigns numeric values automatically:
- Pending → 0
- Shipped → 1
- Delivered → 2
- Cancelled → 3
Using the Enum
- let status: OrderStatus;
- status = OrderStatus.Shipped; // Allowed
- status = OrderStatus.Cancelled; // Allowed
- status = “Delivered”; // Error
Example 2: String Enum
enum UserRole {
Admin = "ADMIN",
User = "USER",
Guest = "GUEST"
}
String enums are:
- More readable
- Easier to debug
- Commonly used in Angular applications
Using the String Enum
- let role: UserRole;
- role = UserRole.Admin; // Allowed
- role = UserRole.User; // Allowed
- role = “SuperAdmin”; // Error
Example 3: Enum in a Class
class User {
name: string;
role: UserRole;
constructor(name: string, role: UserRole) {
this.name = name;
this.role = role;
}
}
Usage:
- const user = new User(“Pranaya”, UserRole.Admin);
This guarantees:
- Only valid roles are assigned
- No typos
- Clear intent
What Are Tuples in TypeScript?
A Tuple is a special type of array in TypeScript where:
- The number of elements is fixed
- The type of each element is fixed and ordered
Unlike normal arrays (where all elements are usually of the same type), tuples allow you to store multiple values of different types in a specific sequence. In simple words, A tuple represents a fixed-structure group of values.
Why Do We Need Tuples in TypeScript?
In real applications, sometimes data is:
- Naturally grouped
- Small and fixed in size
- Order-dependent
Using objects for such simple data can feel heavy, while using normal arrays loses type safety. Tuples solve this by:
- Keeping data lightweight (like arrays)
- Maintaining strict type safety (like objects)
Basic Syntax of Tuples
let tupleName: [type1, type2, type3];
Example to Understand Tuples in TypeScript
Example 1: Simple Tuple
- let userInfo: [number, string, boolean];
- userInfo = [1, “Pranaya”, true];
Meaning:
- number → User ID
- string → User Name
- boolean → Active Status
Correct usage. Wrong order or wrong types will cause errors
Invalid Assignments
- userInfo = [“Pranaya”, 1, true]; // Wrong order
- userInfo = [1, “Pranaya”]; // Missing value
- userInfo = [1, “Pranaya”, true, “extra”]; // Extra value
TypeScript catches these mistakes at compile time.
Example 2: Tuple as Function Return Type
function getUserSummary(): [string, number] {
return ["Pranaya", 30];
}
Usage:
- const result = getUserSummary();
- const name = result[0]; // string
- const age = result[1]; // number
TypeScript knows the exact type at each position.
Example 3: Named Tuple (More Readable)
TypeScript also supports named tuples, which improve readability:
- let productInfo: [id: number, name: string, price: number];
- productInfo = [101, “Laptop”, 55000];
This makes the purpose of each element clearer.
What Is a Union Type in TypeScript?
A Union Type allows a variable, parameter, or return value to hold one of multiple specified types. Instead of restricting a value to a single type, union types let you say: This value can be this OR that. Union types are created using the | (pipe) symbol. In simple words, A union type represents multiple possible types for a value.
Why Do We Need Union Types in TypeScript?
In real applications, especially Angular applications, data is not always of a single fixed type.
Examples:
- API responses that can be successful or an error
- Form fields that can be empty or filled
- Status values with limited options
- Functions that accept different input types
Without union types:
- Developers use any
- Type safety is lost
- Bugs appear at runtime
Union types provide flexibility without losing safety.
Basic Syntax of Union Types
type TypeName = TypeA | TypeB;
Example to Understand Union Types in TypeScript
Example 1: Union of Primitive Types
- let userId: number | string;
- userId = 101; // Allowed
- userId = “A102”; // Allowed
- userId = true; // Error
This ensures that userId is either a number or a string.
What Is an Intersection Type in TypeScript?
An Intersection Type allows you to combine multiple types into a single type. The value of an intersection type must satisfy all the combined types at the same time. Intersection types are created using the & (ampersand) symbol. In simple words: An intersection type represents “this AND that” together.
Why Do We Need Intersection Types in TypeScript?
In real applications—especially Angular applications—you often need to:
- Combine properties from multiple sources
- Build richer data models
- Extend functionality without inheritance
Intersection types allow you to compose types cleanly without duplicating code.
Basic Syntax of Intersection Types
type CombinedType = TypeA & TypeB;
Example to Understand Intersection Types in TypeScript
Example 1: Combining Object Types
type Person = {
name: string;
age: number;
};
type Employee = {
employeeId: number;
department: string;
};
type EmployeeProfile = Person & Employee;
Here:
- EmployeeProfile must have all properties from Person and Employee.
Using the Intersection Type
const emp: EmployeeProfile = {
name: "Pranaya",
age: 30,
employeeId: 101,
department: "IT"
};
Valid object Missing any property will cause a TypeScript error
TypeScript is the foundation of every Angular application. By learning TypeScript fundamentals, you are not just learning a programming language; you are building the discipline needed to write safe, clean, and maintainable Angular code. The concepts you learned in this chapter, such as data types, interfaces, classes, generics, and type safety, will appear in almost every Angular file you write. With this strong TypeScript foundation, Angular will no longer feel confusing or complex, and you will be able to understand and build real-world Angular applications with much more confidence.
Registration Open – Angular Online Training
Session Time: 8:30 PM – 10:00 PM IST
Advance your career with our expert-led, hands-on live training program. Get complete course details, the syllabus, and Zoom credentials for demo sessions via the links below.


Very nice tutorials. Simple and easy to learn
Easy to understand for beginners 👍
nice
Is there an update for Visual Studio 2019?