TypeScript Essentials for Angular

Read one Angular interview question at a time, then flip for the complete explanation, example, and code.

0/137 Known filtered set
Level
Read Full Guide Question: 1 of 137

Angular Interview Questions · TypeScript Essentials for Angular Companion Article

TypeScript Essentials for Angular

Angular applications are primarily written in TypeScript. Although JavaScript knowledge is useful, understanding TypeScript makes Angular code easier to read, maintain, refactor, and debug. TypeScript adds features such as static typing, interfaces, generics, access modifiers, decorators, and compile-time checking on top of JavaScript. Angular uses many of these features throughout components, services, dependency injection, routing, forms, and HTTP communication. This chapter covers the TypeScript concepts that are most useful when working with Angular applications.

Why Angular Uses TypeScript

TypeScript is a programming language built on top of JavaScript. Valid JavaScript is generally valid TypeScript, but TypeScript provides additional language features that help developers catch mistakes before the application runs.

For example, JavaScript allows a variable to hold values of completely different types:

TypeScript
let age = 30;
age = "Thirty";

TypeScript can prevent this kind of accidental assignment when the variable is expected to contain a number.

TypeScript
let age: number = 30;

// Error
age = "Thirty";

In Angular projects, TypeScript helps with:

  • Component development
  • Service development
  • Dependency injection
  • HTTP request and response models
  • Form models
  • Application configuration
  • Reusable utility functions
  • Type-safe event handling
  • Object modeling
  • IDE autocompletion
  • Refactoring
  • Compile-time error detection

Variables and Data Types

Variables store values used by an application.

Modern TypeScript mainly uses let and const.

TypeScript
let username: string = "Rahul";
let age: number = 28;
const country: string = "India";

Use let when the value may change.

TypeScript
let score: number = 10;

score = 20;

Use const when the variable should not be reassigned.

TypeScript
const applicationName = "Employee Portal";

Common TypeScript Data Types

string

Stores text.

TypeScript
let firstName: string = "Amit";

number

Stores integers and decimal numbers.

TypeScript
let salary: number = 75000;
let rating: number = 4.5;

boolean

Stores true or false.

TypeScript
let isLoggedIn: boolean = true;

null

Represents an explicitly empty value.

TypeScript
let selectedUser: null = null;

undefined

Represents a value that has not been assigned.

TypeScript
let response: undefined;

any

Allows almost any type of value.

TypeScript
let data: any = "Angular";

data = 100;
data = true;

any removes much of TypeScript's type checking, so it should normally be avoided when the correct type can be defined.

unknown

unknown can store different types while requiring type checking before the value is used.

TypeScript
let response: unknown = "Success";

if (typeof response === "string") {
    console.log(response.toUpperCase());
}

unknown is usually safer than any when the type of external data is uncertain.

Type Inference

TypeScript does not always require an explicit type declaration.

It can often determine the type automatically from the assigned value.

TypeScript
let framework = "Angular";

TypeScript infers that framework is a string.

The following assignment would therefore produce an error:

TypeScript
framework = 100;

Another example:

TypeScript
let count = 10;

TypeScript infers:

Text
number

Type inference reduces unnecessary code while preserving type safety.

Both of the following are valid:

TypeScript
let city: string = "Pune";
TypeScript
let city = "Pune";

Explicit types are particularly useful when the intended type is not obvious or when defining APIs, models, parameters, and return values.

Functions

Functions contain reusable logic.

A TypeScript function can specify types for both its parameters and return value.

TypeScript
function add(a: number, b: number): number {
    return a + b;
}

Calling the function:

TypeScript
const result = add(10, 20);

console.log(result);

Function Returning a String

TypeScript
function getWelcomeMessage(name: string): string {
    return `Welcome ${name}`;
}

Function Returning Nothing

Use void when a function does not return a meaningful value.

TypeScript
function printMessage(message: string): void {
    console.log(message);
}

Arrow Functions

Arrow functions are commonly used in Angular applications.

TypeScript
const multiply = (a: number, b: number): number => {
    return a * b;
};

Short form:

TypeScript
const square = (number: number): number => number * number;

Arrow functions frequently appear in array operations, subscriptions, transformations, and callbacks.

Example:

TypeScript
users.filter(user => user.active);

Optional Function Parameters

A parameter can be made optional using ?.

TypeScript
function greet(name?: string): string {
    return name ? `Hello ${name}` : "Hello";
}

Both calls are valid:

TypeScript
greet();
greet("Neha");

Default Parameters

A function parameter can have a default value.

TypeScript
function calculatePrice(price: number, tax: number = 18): number {
    return price + (price * tax / 100);
}

Calling:

TypeScript
calculatePrice(1000);

Here, tax automatically becomes 18.

Arrays

Arrays store multiple values.

An array of numbers:

TypeScript
let marks: number[] = [70, 80, 90];

An array of strings:

TypeScript
let technologies: string[] = [
    "Angular",
    "TypeScript",
    "Java"
];

Alternative generic syntax:

TypeScript
let technologies: Array<string> = [
    "Angular",
    "TypeScript"
];

Both forms are valid.

Array Operations

TypeScript
let numbers: number[] = [10, 20, 30];

numbers.push(40);
numbers.pop();

Angular applications commonly use methods such as:

TypeScript
map()
filter()
find()
some()
every()
reduce()

Example:

TypeScript
const activeUsers = users.filter(user => user.active);

Objects

Objects store related values using properties.

TypeScript
const employee = {
    id: 101,
    name: "Rahul",
    department: "IT"
};

An explicit object type can also be declared.

TypeScript
let employee: {
    id: number;
    name: string;
    department: string;
};

employee = {
    id: 101,
    name: "Rahul",
    department: "IT"
};

For reusable object structures, interfaces or type aliases are usually more convenient.

Interfaces

An interface describes the expected structure of an object.

TypeScript
interface User {
    id: number;
    name: string;
    email: string;
}

Creating an object:

TypeScript
const user: User = {
    id: 1,
    name: "Amit",
    email: "amit@example.com"
};

If a required property is missing, TypeScript can report an error.

TypeScript
const user: User = {
    id: 1,
    name: "Amit"
};

The object does not satisfy the User interface because email is missing.

Interfaces in Angular

Interfaces are frequently used to represent API data.

TypeScript
export interface Product {
    id: number;
    name: string;
    price: number;
    available: boolean;
}

A service can then return typed data.

TypeScript
getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(this.apiUrl);
}

This provides stronger type checking when working with HTTP responses.

Type Aliases

A type alias creates a reusable name for a type.

TypeScript
type UserId = number;

Usage:

TypeScript
let currentUserId: UserId = 101;

Type aliases can also represent object structures.

TypeScript
type Employee = {
    id: number;
    name: string;
    salary: number;
};

Example:

TypeScript
const employee: Employee = {
    id: 1,
    name: "Kiran",
    salary: 60000
};

Interface vs Type Alias

Interfaces and type aliases can both describe object structures.

Example using an interface:

TypeScript
interface Customer {
    id: number;
    name: string;
}

Example using a type:

TypeScript
type Customer = {
    id: number;
    name: string;
};

Interfaces are particularly convenient for object-oriented structures and extension.

Type aliases are flexible and can represent unions, primitives, tuples, and other combinations.

For example:

TypeScript
type Status = "pending" | "approved" | "rejected";

Classes

A class is a blueprint for creating objects.

TypeScript
class Employee {
    name: string;
    salary: number;

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

    displayDetails(): void {
        console.log(this.name, this.salary);
    }
}

Creating an object:

TypeScript
const employee = new Employee("Rahul", 70000);

employee.displayDetails();

Angular heavily uses classes for concepts such as components and services.

A simplified Angular component is a class:

TypeScript
export class UserComponent {
    title = "Users";
}

Access Modifiers

Access modifiers control where class members can be accessed.

TypeScript supports commonly used modifiers such as:

  • public
  • private
  • protected

public

Public members can be accessed from outside the class.

TypeScript
class User {
    public name = "Rahul";
}

const user = new User();

console.log(user.name);

public is the default visibility when no access modifier is specified.

private

Private members can only be accessed inside the class.

TypeScript
class BankAccount {
    private balance = 5000;

    getBalance(): number {
        return this.balance;
    }
}

The following direct access is not allowed:

TypeScript
account.balance;

protected

Protected members are accessible inside the class and its subclasses.

TypeScript
class Employee {
    protected salary = 50000;
}

class Manager extends Employee {
    showSalary(): void {
        console.log(this.salary);
    }
}

Constructors

A constructor runs when an object is created from a class.

TypeScript
class Product {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

Usage:

TypeScript
const product = new Product("Laptop");

Constructor Parameter Properties

TypeScript allows shorter class declarations.

Instead of:

TypeScript
class Product {
    name: string;
    price: number;

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

You can write:

TypeScript
class Product {
    constructor(
        public name: string,
        public price: number
    ) {}
}

TypeScript automatically creates the properties.

This syntax is commonly encountered in Angular code.

Constructors and Angular Dependency Injection

Angular often uses constructors to receive dependencies.

Example:

TypeScript
constructor(private userService: UserService) {}

Angular's dependency injection system supplies the required service instance.

The private keyword also creates a class property named userService.

The component can then use it:

TypeScript
this.userService.getUsers();

Modern Angular also supports the inject() API, so constructor injection is not the only dependency-injection style you may encounter.

Inheritance

Inheritance allows one class to reuse or extend another class.

TypeScript
class Person {
    constructor(public name: string) {}

    introduce(): void {
        console.log(`My name is ${this.name}`);
    }
}

Child class:

TypeScript
class Employee extends Person {
    constructor(
        name: string,
        public employeeId: number
    ) {
        super(name);
    }
}

Creating an object:

TypeScript
const employee = new Employee("Amit", 101);

employee.introduce();

The extends keyword creates inheritance.

The super() call invokes the parent constructor.

Inheritance can be useful, but Angular applications often favor composition and reusable services over creating deep inheritance hierarchies.

Generics

Generics allow reusable code to work with different data types while maintaining type safety.

Without generics, developers may be tempted to use any.

TypeScript
function getValue(value: any): any {
    return value;
}

A generic version is safer:

TypeScript
function getValue<T>(value: T): T {
    return value;
}

Usage:

TypeScript
const name = getValue<string>("Angular");
const age = getValue<number>(30);

Here, T represents a type supplied when the function is used.

Generic Interface

TypeScript
interface ApiResponse<T> {
    success: boolean;
    data: T;
}

A user response:

TypeScript
interface User {
    id: number;
    name: string;
}

const response: ApiResponse<User> = {
    success: true,
    data: {
        id: 1,
        name: "Rahul"
    }
};

A product response could use the same generic structure.

TypeScript
const response: ApiResponse<Product[]> = {
    success: true,
    data: products
};

Generics are heavily used by Angular and RxJS.

Examples include:

TypeScript
Observable<User>
Observable<User[]>
FormControl<string>
HttpResponse<Product>

Enums

An enum provides a group of named values.

TypeScript
enum UserRole {
    Admin,
    Manager,
    Employee
}

Usage:

TypeScript
let role: UserRole = UserRole.Admin;

String enums can make stored values more readable.

TypeScript
enum OrderStatus {
    Pending = "PENDING",
    Processing = "PROCESSING",
    Completed = "COMPLETED"
}

Usage:

TypeScript
const status: OrderStatus = OrderStatus.Pending;

For many Angular applications, literal union types are also a useful alternative when only a small set of string values is required.

Example:

TypeScript
type OrderStatus = "pending" | "processing" | "completed";

Union Types

A union allows a value to belong to more than one type.

TypeScript
let userId: string | number;

Both are valid:

TypeScript
userId = 101;
userId = "USR101";

Union types are particularly useful when an application accepts a limited group of possible values.

TypeScript
type ButtonSize = "small" | "medium" | "large";

Usage:

TypeScript
let size: ButtonSize = "medium";

The following would be invalid:

TypeScript
size = "huge";

Narrowing a Union Type

Before performing type-specific operations, check the actual type.

TypeScript
function printId(id: string | number): void {
    if (typeof id === "string") {
        console.log(id.toUpperCase());
    } else {
        console.log(id);
    }
}

This process is called type narrowing.

Optional Properties

An interface property can be optional.

Use ? after the property name.

TypeScript
interface User {
    id: number;
    name: string;
    phone?: string;
}

Both objects are valid:

TypeScript
const user1: User = {
    id: 1,
    name: "Rahul"
};
TypeScript
const user2: User = {
    id: 2,
    name: "Amit",
    phone: "9876543210"
};

Optional properties are useful when API data does not always contain every field.

Optional Chaining

Optional chaining helps safely access nested values that may be null or undefined.

TypeScript
const city = user.address?.city;

If address is unavailable, JavaScript does not attempt to access city and the expression evaluates to undefined.

Without optional chaining, additional checks may be needed.

TypeScript
if (user.address) {
    console.log(user.address.city);
}

Optional chaining is widely used when handling API data and optional application state.

Nullish Coalescing

The nullish coalescing operator ?? provides a fallback when a value is null or undefined.

TypeScript
const username = user.name ?? "Guest";

If user.name is null or undefined, "Guest" is used.

This differs from ||, which also treats values such as 0, false, and an empty string as falsy.

Modules

Modules help divide an application into reusable files.

A value must normally be exported before another file can import it.

Export

TypeScript
export interface User {
    id: number;
    name: string;
}

Import

TypeScript
import { User } from "./user";

Functions can also be exported.

TypeScript
export function calculateTax(amount: number): number {
    return amount * 0.18;
}

Then imported:

TypeScript
import { calculateTax } from "./tax-utils";

Angular projects rely heavily on ES module import and export syntax.

For example:

TypeScript
import { Component } from "@angular/core";

Type-Only Imports

When an import is required only for TypeScript type checking, it can be declared as a type import.

TypeScript
import type { User } from "./user";

This makes the intention clear that User is being used only as a type.

Example:

TypeScript
import type { User } from "./user";

function displayUser(user: User): void {
    console.log(user.name);
}

Decorators

A decorator adds metadata or behavior to a class or class member.

Angular uses decorators extensively.

A common example is @Component.

TypeScript
import { Component } from "@angular/core";

@Component({
    selector: "app-user",
    templateUrl: "./user.component.html"
})
export class UserComponent {}

The @Component decorator tells Angular that the class represents a component and supplies Angular with its configuration.

Other Angular decorators you may encounter include:

Text
@Injectable
@Input
@Output
@Directive
@Pipe

For example:

TypeScript
@Injectable({
    providedIn: "root"
})
export class UserService {}

@Injectable makes the class available to Angular's dependency-injection system according to its provider configuration.

Angular developers generally consume Angular-provided decorators rather than creating decorators for routine application development.

Async Programming in Angular

Web applications frequently perform operations that do not complete immediately.

Examples include:

  • Calling REST APIs
  • Reading asynchronous browser APIs
  • Waiting for external resources
  • Performing asynchronous application operations

JavaScript and TypeScript support promises and async/await for asynchronous programming.

Promises

A promise represents a value that may become available later.

TypeScript
function getUsername(): Promise<string> {
    return Promise.resolve("Rahul");
}

The result can be handled using:

TypeScript
getUsername().then(name => {
    console.log(name);
});

Async/Await

async and await provide a more sequential syntax for working with promises.

TypeScript
async function loadUser(): Promise<void> {
    const user = await getUser();

    console.log(user);
}

An async function always returns a promise.

For example:

TypeScript
async function getNumber(): Promise<number> {
    return 10;
}

Although the function appears to return 10, callers receive a Promise<number>.

Error Handling with Async/Await

Use try, catch, and optionally finally.

TypeScript
async function loadData(): Promise<void> {
    try {
        const data = await fetchData();

        console.log(data);
    } catch (error) {
        console.error("Unable to load data", error);
    } finally {
        console.log("Request finished");
    }
}

This structure is useful when working with promise-based APIs.

Async/Await vs Observables in Angular

Angular applications frequently use RxJS Observables, especially for Angular APIs such as HttpClient.

Example:

TypeScript
this.http.get<User[]>("/api/users");

This returns an Observable<User[]>, not a promise.

A component might subscribe to it:

TypeScript
this.userService.getUsers().subscribe(users => {
    this.users = users;
});

Therefore, async/await does not replace Observables in Angular.

A useful distinction is:

FeaturePromiseObservable
Typical resultOne asynchronous resultZero, one, or many emitted values
Lazy behaviorPromise work usually starts when createdObservable execution commonly starts on subscription
OperatorsLimited built-in transformation APIRich RxJS operator ecosystem
CancellationNo general built-in cancellation model for arbitrary promisesSubscriptions can be unsubscribed
Common Angular usePromise-based APIsHTTP, forms, routing and reactive streams

Angular developers should understand both approaches.

Typing API Responses

One of the most useful TypeScript practices in Angular is defining types for backend responses.

Suppose an API returns:

JSON
{
    "id": 101,
    "name": "Laptop",
    "price": 45000
}

Create an interface:

TypeScript
export interface Product {
    id: number;
    name: string;
    price: number;
}

Then use the interface in the service.

TypeScript
getProduct(id: number): Observable<Product> {
    return this.http.get<Product>(`${this.apiUrl}/${id}`);
}

For multiple products:

TypeScript
getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(this.apiUrl);
}

This improves autocompletion and compile-time checking throughout the application.

However, TypeScript types do not validate server data at runtime. If runtime validation is required, the application must perform that validation separately.

Practical Angular Example

Consider a user-management application.

User Model

TypeScript
export interface User {
    id: number;
    name: string;
    email: string;
    role: UserRole;
    phone?: string;
}

Role Definition

TypeScript
export enum UserRole {
    Admin = "ADMIN",
    Manager = "MANAGER",
    Employee = "EMPLOYEE"
}

Generic API Response

TypeScript
export interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
}

The response can then be typed as:

TypeScript
ApiResponse<User>

or:

TypeScript
ApiResponse<User[]>

A service method might look like:

TypeScript
getUsers(): Observable<ApiResponse<User[]>> {
    return this.http.get<ApiResponse<User[]>>(this.apiUrl);
}

This example combines several TypeScript concepts:

  • Interfaces
  • Enums
  • Generics
  • Arrays
  • Modules
  • Function return types
  • Angular Observables

Type Assertions

Sometimes the developer knows more about a value's type than TypeScript can determine.

A type assertion can be used.

TypeScript
const input = document.getElementById("username") as HTMLInputElement;

Now TypeScript understands that input is an HTMLInputElement.

You can access properties such as:

TypeScript
input.value;

Type assertions do not convert the actual runtime value. They only tell TypeScript how the developer wants the value to be treated.

Incorrect assertions can therefore hide bugs.

readonly Properties

readonly prevents a property from being reassigned through normal TypeScript usage after initialization.

TypeScript
interface Configuration {
    readonly apiUrl: string;
}

Example:

TypeScript
const config: Configuration = {
    apiUrl: "/api"
};

The following reassignment is rejected by TypeScript:

TypeScript
config.apiUrl = "/new-api";

readonly is useful for values that should remain stable after initialization.

Literal Types

Literal types restrict a variable to exact values.

TypeScript
let direction: "left" | "right";

Valid:

TypeScript
direction = "left";
direction = "right";

Invalid:

TypeScript
direction = "up";

Literal types are useful for configuration options, component states, and other finite sets of values.

Tuples

A tuple represents an array with known positions and types.

TypeScript
let user: [number, string];

user = [101, "Rahul"];

The first value must be a number and the second must be a string.

Tuples can be useful when positional data has a clearly defined structure, although named object properties are often easier to understand in application code.

The never Type

never represents a situation where a function cannot complete normally.

Example:

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

Because the function always throws an exception, it never returns a normal value.

never also appears in advanced type checking when TypeScript determines that no possible type remains.

The void Type

void is commonly used when a function performs an operation but does not return a useful value.

TypeScript
function logMessage(message: string): void {
    console.log(message);
}

Angular event-handling methods frequently return void.

TypeScript
onSave(): void {
    console.log("Saved");
}

Common TypeScript Mistakes in Angular

Using any Everywhere

Avoid:

TypeScript
let user: any;

Prefer:

TypeScript
let user: User;

Proper typing helps TypeScript find errors earlier.

Assuming TypeScript Validates API Data

This:

TypeScript
this.http.get<User>(url);

tells TypeScript how you expect to treat the response.

It does not guarantee that the backend actually returned a valid User.

Runtime validation may still be necessary for untrusted or externally controlled data.

Ignoring Null and Undefined Values

Code such as:

TypeScript
console.log(user.address.city);

may fail if address is unavailable.

When appropriate, use optional chaining:

TypeScript
console.log(user.address?.city);

Using Non-Null Assertions Without Reason

The ! operator tells TypeScript that a value will not be null or undefined.

TypeScript
user!.name;

This should be used carefully.

If the assumption is incorrect, the application can still fail at runtime.

Creating Large Inheritance Hierarchies

Inheritance can be useful, but Angular applications usually benefit more from:

  • Services
  • Dependency injection
  • Composition
  • Reusable functions
  • Directives
  • Pipes

Avoid introducing inheritance when a simpler design solves the problem.

Forgetting Function Return Types in Public APIs

Type inference is useful, but explicit return types can make important methods easier to understand.

For example:

TypeScript
getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
}

The method contract is immediately visible.

TypeScript Features Commonly Seen in Angular Components

A typical Angular class can combine many TypeScript concepts.

TypeScript
export class ProductListComponent {
    products: Product[] = [];
    selectedProduct?: Product;
    loading = false;

    constructor(private productService: ProductService) {}

    loadProducts(): void {
        this.loading = true;

        this.productService.getProducts().subscribe({
            next: products => {
                this.products = products;
            },
            error: error => {
                console.error(error);
            },
            complete: () => {
                this.loading = false;
            }
        });
    }
}

This example contains:

  • Class
  • Array type
  • Interface-based model
  • Optional property
  • Type inference
  • Constructor
  • Private property
  • Dependency injection
  • Method
  • void return type
  • Arrow functions

Understanding these TypeScript concepts makes Angular component code considerably easier to follow.

TypeScript Features Commonly Seen in Angular Services

A service commonly contains typed methods and generic APIs.

TypeScript
export class ProductService {
    constructor(private http: HttpClient) {}

    getProducts(): Observable<Product[]> {
        return this.http.get<Product[]>("/api/products");
    }

    getProduct(id: number): Observable<Product> {
        return this.http.get<Product>(`/api/products/${id}`);
    }
}

Important TypeScript concepts involved here include:

  • Classes
  • Constructor parameter properties
  • Access modifiers
  • Parameter types
  • Generic types
  • Return types
  • Template literals
  • Modules

Choosing the Correct Type

Use the most precise type that accurately describes the data.

Instead of:

TypeScript
let status: string;

when only three values are allowed, consider:

TypeScript
type Status = "pending" | "approved" | "rejected";

let status: Status;

Instead of:

TypeScript
let products: any[];

prefer:

TypeScript
let products: Product[];

Instead of:

TypeScript
let response: any;

consider defining:

TypeScript
interface ApiResponse<T> {
    data: T;
    message: string;
}

Specific types make code easier to understand and help the compiler identify incorrect assignments.

Quick TypeScript Reference for Angular Developers

RequirementTypeScript Feature
Store textstring
Store numeric valuesnumber
Store true/falseboolean
Store multiple valuesArray
Describe an objectinterface or type
Allow multiple possible typesUnion
Make a property optional?
Reuse logicFunction
Model objectsClass
Control member accessAccess modifiers
Share code between filesModules
Create reusable typed structuresGenerics
Define named fixed valuesEnum
Add Angular metadataDecorators
Work with promisesasync / await
Safely access optional values?.
Provide null/undefined fallback??
Prevent reassignment of a propertyreadonly

TypeScript Concepts Worth Mastering Before Advanced Angular

You do not need to master every advanced feature of TypeScript before learning Angular. However, you should be comfortable with:

  1. Variables and primitive types
  2. Arrays and objects
  3. Functions and arrow functions
  4. Interfaces
  5. Type aliases
  6. Classes
  7. Constructors
  8. Access modifiers
  9. Modules
  10. Generics
  11. Union types
  12. Optional properties
  13. Optional chaining
  14. Decorators
  15. Promises and async programming

These concepts appear repeatedly in real Angular projects.

Practical Learning Exercise

Consider an Angular application that displays courses.

Create the following interface:

TypeScript
interface Course {
    id: number;
    title: string;
    price: number;
    category?: string;
}

Create an array:

TypeScript
const courses: Course[] = [
    {
        id: 1,
        title: "Angular",
        price: 999
    },
    {
        id: 2,
        title: "TypeScript",
        price: 699,
        category: "Programming"
    }
];

Filter courses:

TypeScript
const affordableCourses = courses.filter(
    course => course.price < 800
);

Create a generic response:

TypeScript
interface ApiResponse<T> {
    data: T;
    success: boolean;
}

Use it with the courses:

TypeScript
const response: ApiResponse<Course[]> = {
    data: courses,
    success: true
};

This small example practices interfaces, arrays, objects, optional properties, arrow functions, generics, and type inference together.

Key Takeaways

TypeScript is not simply JavaScript with extra syntax. In Angular development, it acts as a type system that helps describe how application data, classes, functions, services, and APIs should behave.

The most useful habits are to:

  • Prefer meaningful types over any
  • Define interfaces or types for application data
  • Add clear parameter and return types where they improve readability
  • Use generics for reusable typed structures
  • Handle null and undefined deliberately
  • Understand classes and constructors because Angular uses class-based APIs extensively
  • Understand modules because Angular code is organized through imports and exports
  • Learn decorators because Angular uses them to attach framework metadata
  • Understand both promises and Observables when working with asynchronous code

Once these TypeScript fundamentals are comfortable, Angular concepts such as components, services, dependency injection, forms, routing, and HTTP communication become much easier to understand.

Question Hint