TypeScript DataTypes

TypeScript Data Types: The Ultimate Beginner-Friendly Guide

Learn TypeScript data types with simple explanations, interactive examples, visual learning, and interview questions.

If you have already learned TypeScript variables, you are ready for the next important step: TypeScript data types.

Don’t let the words “data type” scare you.

A data type is simply a way of telling TypeScript what kind of thing a value is.

Think about a child’s toy room.

You may have a box for cars, a box for dolls, a box for balls, a box for books, and a box for crayons.

You don’t randomly throw everything into every box. You know that a car is a car, a book is a book, and a ball is a ball.

TypeScript works in a similar way.

It helps us describe what kind of information a variable contains.

let name: string = "Rahul";
let age: number = 10;
let isStudent: boolean = true;

Here:

  • name → text
  • age → number
  • isStudent → true/false

Simple definition: A data type tells TypeScript what kind of value something is.

What You Will Learn

By the end of this tutorial, you will understand:

  • What a data type is
  • Why TypeScript needs data types
  • string
  • number
  • boolean
  • Arrays
  • Tuples
  • Objects
  • any
  • unknown
  • null
  • undefined
  • Union types
  • Literal types
  • void
  • never
  • Type inference
  • Type narrowing
  • Real-world examples
  • Playwright examples
  • Interview questions
  • Practice challenges

Meet the TypeScript Type Detective

Before learning individual types, imagine that TypeScript is a little detective.

It looks at a value and asks:

“What are you?”

If it sees:

10

it understands that the value is a number.

If it sees:

"Hello"

it understands that the value is a string.

If it sees:

true

it understands that the value is a boolean.

Every value has a job. The type tells TypeScript what that job is.

Why Do We Need Data Types?

Imagine you have a box.

You put a number inside it:

let age: number = 25;

This tells TypeScript:

“The age variable should contain a number.”

If you later write:

age = "twenty five";

TypeScript can tell you that something is wrong.

Think of TypeScript as a safety guard. It doesn’t stop you from building your application. It simply helps catch many mistakes before they become runtime problems.

TypeScript Data Types at a Glance

Type What It Stores Example
stringText"Hello"
numberNumbers100
booleanTrue/falsetrue
ArrayList of values[1,2,3]
TupleFixed structure["Rahul",10]
ObjectRelated properties{name:"Rahul"}
anyAlmost anythingany
unknownUnknown valueunknown
nullIntentional absencenull
undefinedMissing/unassignedundefined
UnionOne of multiple typesstring | number
voidNo useful returnvoid
neverNever normally completesnever

1. String: The Type for Text

A string represents text.

let name: string = "Rahul";

Other examples:

let firstName: string = "Deepesh";
let city: string = "Indore";
let message: string = "Welcome to TypeScript";
Easy way to remember: String = words or text.

Interactive Example: String

let name: string = "Rahul";

console.log(name);
Try It: Replace "Rahul" with your own name and predict the output.

2. Number: The Type for Numbers

The number type represents numeric values.

let age: number = 10;
let price: number = 999;
let marks: number = 95;
let temperature: number = 32.5;
Interview tip: TypeScript normally uses number for both integer-like and decimal numeric values.

Interactive Number Challenge

let price: number = 100;
let quantity: number = 3;

let total = price * quantity;

console.log(total);

Answer: 300

3. Boolean: The Yes-or-No Type

A boolean has only two values:

true
false
let isLoggedIn: boolean = true;
let testPassed: boolean = false;

Think about a light switch:

ON → true
OFF → false

Interactive Boolean Challenge

const age = 20;

const isAdult = age >= 18;

console.log(isAdult);
Answer: true

4. Arrays: One Box With Many Values

An array stores multiple values.

Imagine five toy cars. Instead of using five separate boxes, you can put them into one larger box.

const numbers: number[] = [10, 20, 30, 40];

You can also write:

const numbers: Array<number> = [10, 20, 30, 40];

Array Indexes

0 → Apple    1 → Banana    2 → Mango
const fruits: string[] = [
  "Apple",
  "Banana",
  "Mango"
];

console.log(fruits[1]);
Answer: Banana

5. Tuple: A Special Kind of Array

A tuple looks like an array, but each position can have a specific type.

let student: [string, number] = ["Rahul", 10];

That means:

  • Position 0 → string
  • Position 1 → number
Easy memory trick:
Array = many similar things.
Tuple = fixed seats with specific jobs.

6. Object: A Box With Named Compartments

An object groups related information.

const student = {
  name: "Rahul",
  age: 10,
  grade: 5
};

You can access the information using:

console.log(student.name);
console.log(student.age);
Think of an object as a student card: name, age, grade, email, phone number, and other related information all belong together.

7. any: The “Anything Goes” Type

Think of any as a box with no label.

let data: any = "Hello";

data = 100;
data = true;
data = {
  name: "Rahul"
};
Warning: Using any removes much of TypeScript’s type-safety benefit. Use it only when there is a good reason.

8. unknown: The Mystery Box

Imagine receiving a box but not knowing what’s inside.

You shouldn’t immediately pretend it contains a toy car. First, you check.

let data: unknown = "Hello";

if (typeof data === "string") {
  console.log(data.toUpperCase());
}
Remember: unknown means “I don’t know yet. Check me first.”

any vs unknown

Feature any unknown
Can hold any value?YesYes
Type checkingMostly disabledPreserved
Requires narrowing?NoYes
Safer choice?Usually noUsually yes
Easy memory trick:
any → “Do whatever you want.”
unknown → “Check me before you use me.”

9. null: Intentionally Empty

null represents an intentional absence of a value.

let selectedUser = null;

A common real-world example is:

let selectedUser: User | null = null;

This means the variable can contain a User, or intentionally contain no user.

10. undefined: Nothing Has Been Provided

undefined commonly means that a value hasn’t been assigned or isn’t currently available.

let nickname: string | undefined;
Easy memory trick:
null → “I intentionally have no value.”
undefined → “A value hasn’t been provided.”

11. Union Types: This OR That

A union type allows a variable to contain one of several known types.

let userId: string | number;

Both are valid:

userId = 101;

userId = "USER101";

But this is not:

userId = true;
Remember: The | symbol means OR.

Interactive Union Challenge

Which assignments are valid?

let id: string | number;
  1. id = 100;
  2. id = "ABC100";
  3. id = true;
Answer: 1 and 2 are valid. 3 is invalid because boolean isn’t part of the union.

12. Literal Types: Only These Exact Values

A literal type allows only specific values.

let browser: "Chrome" | "Firefox" | "Safari";

These are valid:

browser = "Chrome";
browser = "Firefox";

But:

browser = "Edge";

would not match the declared type.

Real-world use: Literal types are excellent when an application supports a small, known list of values.

13. void: A Function That Returns Nothing Useful

You’ll usually see void as a function return type.

function sayHello(): void {
  console.log("Hello");
}

The function performs an action but doesn’t return a useful value.

14. never: A Function That Never Normally Finishes

never is more advanced.

For example, a function that always throws an error never successfully returns:

function throwError(message: string): never {
  throw new Error(message);
}
Easy memory trick: never means normal completion never happens.

Type Inference: TypeScript Is a Detective

You don’t always have to explicitly specify a type.

const name = "Rahul";
const age = 10;
const passed = true;

TypeScript can infer:

  • name → string
  • age → number
  • passed → boolean

This is called type inference.

Type Narrowing: Let TypeScript Find the Right Type

Sometimes a value can have multiple types.

function printValue(value: string | number) {

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

  if (typeof value === "number") {
    console.log(value.toFixed(2));
  }
}

At first TypeScript thinks:

Could be string OR number.

After the typeof check, TypeScript can narrow the value to the appropriate type.

TypeScript Data Types in Playwright

If you are learning TypeScript for Playwright automation, these concepts become very practical.

const baseUrl: string = "https://example.com";

const timeout: number = 30000;

const headless: boolean = true;

const browsers: string[] = [
  "chromium",
  "firefox",
  "webkit"
];

Now your framework clearly communicates what each value represents.

Test Data Example

const username: string = "testuser";
const password: string = "Password123";
const rememberMe: boolean = true;
const retryCount: number = 3;

These values can then be used by your Playwright tests.

Real-World API Example

Imagine an API returns a user ID as either a number or a string.

let userId: string | number;

This is better than using any because you’ve clearly documented the two allowed possibilities.

Common Beginner Mistakes

Mistake 1: Using any Everywhere

Using any simply because TypeScript reports an error defeats much of the purpose of TypeScript.

Mistake 2: Confusing Arrays and Tuples

string[]

means an array of strings.

[string, number]

means a tuple with a string followed by a number.

Mistake 3: Using String Instead of string

Prefer the lowercase primitive type:

let name: string;

rather than using the boxed-object form:

let name: String;

Must-Know TypeScript Data Type Interview Questions

Interview Tip: Don’t just memorize the answer. Understand the simple idea behind it. Interviewers often ask follow-up questions.

1. What are data types in TypeScript?

Answer: Data types describe what kind of value a variable can contain. Examples include string, number, boolean, arrays, tuples, objects, unions, and other TypeScript types.

2. What are the common primitive types?

Answer: Common JavaScript primitive types include string, number, boolean, bigint, symbol, null, and undefined.

3. What is the difference between any and unknown?

Answer: any largely disables type checking for the value. unknown can hold any value, but TypeScript requires you to narrow or check the value before performing type-specific operations.

4. What is a union type?

Answer: A union type allows a value to be one of several possible types.
let id: string | number;

5. What is the difference between an array and a tuple?

Answer: An array generally represents a collection of values, while a tuple describes a fixed structure where each position can have a specific type.

6. What is type inference?

Answer: Type inference is TypeScript automatically determining the type of a value based on its context.
const age = 25;
TypeScript infers age as a number.

7. What is never?

Answer: never represents a value that never occurs. It is commonly used for functions that never successfully complete, such as functions that always throw an error.

8. What is void?

Answer: void is commonly used for functions that do not return a useful value.

9. What is the difference between null and undefined?

Answer: A simple explanation is that null usually represents an intentional absence of a value, while undefined commonly indicates that a value has not been assigned or provided.

10. Does TypeScript have separate integer and floating-point types?

Answer: No. Ordinary numeric values use the number type.

Scenario-Based Interview Questions

Scenario 1: API Returns String or Number

An API sometimes returns:

101

and sometimes:

"101"

Answer:

let userId: string | number;

Scenario 2: Completely Unknown API Response

Would you use any or unknown?

Answer: Prefer unknown when the data is genuinely unknown and you can validate or narrow it before using it.

Scenario 3: Browser Configuration

Your automation framework supports only:

  • chromium
  • firefox
  • webkit

A literal union is a good choice:

type BrowserName =
  | "chromium"
  | "firefox"
  | "webkit";

This prevents accidental values outside the supported list.

Final Interactive Type Detective Challenge

Don’t look at the answer immediately. Try to identify the type of every variable.

const name = "Deepesh";

const age = 35;

const isTrainer = true;

const skills = [
  "Python",
  "Playwright",
  "TypeScript"
];

const employee: [string, number] = [
  "Deepesh",
  35
];

const id: string | number = 101;

Answer

  • name → string
  • age → number
  • isTrainer → boolean
  • skills → string[]
  • employee → [string, number]
  • id → string | number

Practice Programs

Don’t stop after reading. Try these exercises yourself.

Practice 1: Student Information

Create variables for:

  • Name
  • Age
  • Grade
  • Whether the student passed

Practice 2: Shopping Cart

Create variables for product name, price, quantity, and stock status. Calculate the total price.

Practice 3: Employee

Create an employee object containing ID, name, department, experience, and active status.

Practice 4: Browser Configuration

Create a literal union containing chromium, firefox, and webkit.

Practice 5: User ID

Create a variable that accepts a number or string but rejects a boolean.

TypeScript Data Types Cheat Sheet

Type Easy Meaning Example
stringWords/text"Hello"
numberNumbers100
booleanYes/notrue
ArrayList[1,2,3]
TupleFixed positions["A",10]
ObjectRelated information{name:"A"}
anyAnythingany
unknownMystery valueunknown
nullIntentionally emptynull
undefinedNot providedundefined
UnionThis OR thatstring | number
voidNo useful returnvoid
neverNever normally completesnever

Final Takeaway

If you are a beginner, don’t try to memorize every TypeScript type today.

Instead, remember the idea behind each one.

String → text

Number → numbers

Boolean → yes or no

Array → collection

Tuple → fixed structure

Object → related information

any → “Don’t check me.”

unknown → “Check me first.”

Union → “This OR that.”

void → “I don’t return a useful value.”

never → “Normal completion never happens.”

Don’t just read TypeScript. Practice it.

Write code. Break it. Read the error. Fix it. Repeat.

Frequently Asked Questions

What are the most important TypeScript data types for beginners?

Start with string, number, boolean, array, and object. Then learn tuples, unions, any, unknown, null, and undefined. After that, move into literal types, void, never, generics, and utility types.

Is any bad in TypeScript?

any is not forbidden, but excessive use removes much of TypeScript’s type-safety benefit. When a value is genuinely unknown, unknown is often a safer choice.

What is the difference between an array and a tuple?

An array generally represents a collection of values, while a tuple represents a fixed structure where each position can have a specific type.

What is a union type?

A union type allows a value to be one of several possible types. For example, string | number means the value can be a string or a number.

Why is unknown safer than any?

With unknown, TypeScript requires you to check or narrow the value before performing operations that depend on a particular type. With any, those checks are largely bypassed.

Learning Tip: Save this article and revisit the cheat sheet whenever you forget a TypeScript data type. The fastest way to master these concepts is to write small programs and predict their output before running them.

TypeScript Variables: A Complete Beginner-to-Interview Guide

If you are learning TypeScript, one of the first things you need to understand is variables.

Don’t worry if the word “variable” sounds technical.

It is actually a very simple idea.

Imagine you have a small box.

You put a toy inside the box.

Later, you take the toy out and put another toy inside.

That box is similar to a variable in programming.

A variable is simply a named place where your program can store information.

For example:

let name = "Rahul";

Here, name is the variable and "Rahul" is the value stored inside it.

You can imagine it like this:

        Variable
           |
           ↓
    +-------------+
    |    name     |
    |-------------|
    |   "Rahul"   |
    +-------------+

But TypeScript adds something special.

It can also understand what kind of value the variable contains.

For example:

let age: number = 10;

Now TypeScript knows:

“The age variable should contain a number.”

This extra information is one of the reasons TypeScript is so useful for large applications, automation frameworks, and modern web development.

In this tutorial, we will learn TypeScript variables from the absolute beginning and gradually move toward real-world examples, common mistakes, interview questions, and Playwright automation examples.


What You Will Learn

By the end of this article, you will understand:

  • What a variable is
  • Why variables are needed
  • let
  • const
  • var
  • Variable naming rules
  • Type annotations
  • Type inference
  • Reassigning variables
  • Constants
  • Scope
  • Block scope
  • Global scope
  • Variables with objects
  • Variables with arrays
  • Variables in functions
  • Variables in TypeScript automation
  • Common mistakes
  • Interview questions and answers
  • Practice questions

Let’s start from the very beginning.


What Is a Variable?

A variable is a named container for storing a value.

That’s it.

You don’t need a complicated definition.

Think about your school bag.

Your bag may contain:

Books
Pencils
Eraser
Lunch Box
Water Bottle

Similarly, a program needs places to store information.

For example:

let studentName = "Rahul";
let studentAge = 10;
let studentMarks = 95;

We now have three variables.

studentName → "Rahul"

studentAge → 10

studentMarks → 95

You can use these values later.

console.log(studentName);
console.log(studentAge);
console.log(studentMarks);

Output:

Rahul
10
95

So why not simply write "Rahul" everywhere?

Because programs become much easier to manage when we give information meaningful names.

Imagine an application containing 100 different places where a user’s name is required.

Instead of repeatedly writing:

"Rahul"

you can use:

studentName

This makes your program easier to understand and maintain.


Why Do We Need Variables?

Let’s imagine you are creating an online shopping application.

You might need to store:

Product Name
Product Price
Product Quantity
Customer Name
Customer Email
Order Status
Payment Status

Without variables, managing this information would be extremely difficult.

With variables:

const productName = "Laptop";
const price = 75000;
let quantity = 2;
const customerName = "Rahul";
let paymentStatus = "Pending";

Now your program can work with this information.

For example:

const totalPrice = price * quantity;

console.log(totalPrice);

Output:

150000

The important idea is:

Variables allow programs to remember and work with information.


Creating Your First TypeScript Variable

The simplest syntax is:

let variableName = value;

For example:

let name = "Rahul";

Let’s break it down.

let     → tells TypeScript we are creating a variable

name    → variable name

=       → assignment operator

"Rahul" → value

So:

let name = "Rahul";

means:

Create a variable called name and store "Rahul" inside it.


The Three Ways to Declare Variables

TypeScript supports three JavaScript variable declaration keywords:

let
const
var

You will mostly use:

let
const

var is an older JavaScript approach that you may encounter in existing applications.

Let’s understand all three.


Understanding let

Use let when you expect the value to change.

Example:

let age = 20;

age = 21;

console.log(age);

Output:

21

Initially:

age → 20

After reassignment:

age → 21

Think of let as a box whose contents can be replaced.

+----------------+
|      age       |
|----------------|
|      20        |
+----------------+

        ↓

+----------------+
|      age       |
|----------------|
|      21        |
+----------------+

This is useful for values such as:

  • Counters
  • Scores
  • Status
  • Current page
  • Retry count
  • Shopping cart quantity

For example:

let retryCount = 0;

retryCount = retryCount + 1;

console.log(retryCount);

Output:

1

Understanding const

Now imagine you have a box with a special lock.

Once you put something inside, you don’t want to replace the box’s value.

That’s similar to const.

Example:

const country = "India";

You cannot do:

country = "USA";

TypeScript will report an error because a const variable cannot be reassigned.

Use const when the variable should not be reassigned.

For example:

const companyName = "SQA Tools";
const browserName = "Chrome";
const applicationUrl = "https://example.com";

These values are not expected to change during the execution of the program.


let vs const

This is one of the most common interview questions.

The basic difference is:

Featureletconst
Can be reassigned?YesNo
Block scoped?YesYes
Must be initialized immediately?NoYes
Modern JavaScript/TypeScriptYesYes
Common usageChanging valuesValues not reassigned

Example:

let score = 10;

score = 20;

Valid.

But:

const score = 10;

score = 20;

Invalid.

A simple rule:

Use const by default. Use let when you need reassignment.


What Is var?

Before let and const became standard, JavaScript developers commonly used:

var name = "Rahul";

TypeScript still supports var.

However, modern TypeScript development generally prefers:

let

and:

const

Why?

Because var has different scoping behavior that can lead to confusing situations.

For example:

if (true) {
    var message = "Hello";
}

console.log(message);

The variable can still be accessed outside the block.

Compare that with let:

if (true) {
    let message = "Hello";
}

console.log(message);

Here, message is not available outside the block.

This concept is called block scope.

We’ll look at it shortly.


Variable Naming Rules

You can’t give variables just any name you want.

For example:

let studentName = "Rahul";

is valid.

But:

let 123student = "Rahul";

is invalid.

Variable names generally:

  • Can contain letters
  • Can contain numbers
  • Can contain _
  • Can contain $
  • Cannot start with a number
  • Cannot contain spaces
  • Cannot use reserved keywords

For example:

let firstName = "Rahul";
let student1 = "Amit";
let _count = 10;
let $price = 500;

These are valid syntax.


Use Meaningful Variable Names

This is one of the most important programming habits.

Avoid:

let x = "Rahul";
let y = 25;
let z = true;

Someone reading the code may wonder:

What is x?

Instead:

let userName = "Rahul";
let userAge = 25;
let isLoggedIn = true;

Now the code explains itself.

Compare:

if (x) {
    // ...
}

with:

if (isLoggedIn) {
    // ...
}

Which one is easier to understand?

Obviously:

isLoggedIn

Good variable names are like good labels on boxes.


Camel Case in TypeScript

A common naming convention in TypeScript is camelCase.

Examples:

firstName
lastName
userName
emailAddress
phoneNumber
isLoggedIn
totalAmount
productPrice

The first word starts with lowercase, and the next words start with uppercase.

For example:

firstName
     ↑
   capital N

This convention is widely used in JavaScript and TypeScript codebases.


Type Annotations

Now let’s add the feature that makes TypeScript different from ordinary JavaScript.

You can explicitly tell TypeScript what type of value a variable should contain.

Example:

let age: number = 25;

The : number part is called a type annotation.

Another example:

let name: string = "Rahul";

And:

let isActive: boolean = true;

Think of this as putting a label on the box.

+---------------------+
| age                 |
|---------------------|
| TYPE: number        |
| VALUE: 25           |
+---------------------+

Now TypeScript understands exactly what you expect.


TypeScript Variables With Common Types

String

let userName: string = "Rahul";

A string stores text.


Number

let age: number = 25;

A number stores numeric values.


Boolean

let isLoggedIn: boolean = true;

A boolean stores:

true

or:

false

Type Inference

Here’s a very useful TypeScript feature.

You don’t always have to tell TypeScript the type.

For example:

let name = "Rahul";

TypeScript looks at "Rahul" and understands:

name → string

Similarly:

let age = 25;

TypeScript understands:

age → number

And:

let isActive = true;

TypeScript understands:

isActive → boolean

This is called type inference.

Think of TypeScript as a smart teacher.

You don’t need to tell the teacher:

“This is a red apple.”

The teacher can see the apple and understand what it is.

Similarly, TypeScript can often look at the initial value and determine the type.


Type Annotation vs Type Inference

Compare these:

let age: number = 25;

and:

let age = 25;

In many situations, TypeScript can infer that age is a number.

So you don’t necessarily need to write:

: number

every time.

A clean approach is:

const name = "Rahul";
const age = 25;
const isActive = true;

TypeScript understands their types automatically.


When Should You Use Type Annotations?

Type annotations are useful when they provide additional clarity or when TypeScript cannot infer the intended type.

For example:

let userName: string;

Later:

userName = "Rahul";

You can also use them when defining function parameters, object structures, API models, and other important contracts.

For example:

let userId: string | number;

Here the annotation communicates something important:

userId can be a string or a number.


Reassigning Variables

A variable declared with let can be reassigned.

let city = "Indore";

city = "Mumbai";

console.log(city);

Output:

Mumbai

But you cannot reassign a const variable:

const city = "Indore";

city = "Mumbai";

This produces an error.

The important word here is reassignment.


A Common const Confusion

Many beginners think:

“If I use const, absolutely nothing can change.”

That’s not completely correct.

Consider:

const user = {
    name: "Rahul",
    age: 25
};

You cannot replace the entire object:

user = {
    name: "Amit",
    age: 30
};

But you can potentially change a property:

user.name = "Amit";

Why?

Because const prevents reassignment of the variable binding.

It does not automatically make every object property immutable.

If you need that behavior, TypeScript provides other tools such as readonly.


Variables With Arrays

Variables can store arrays too.

const fruits: string[] = [
    "Apple",
    "Banana",
    "Mango"
];

You can access values:

console.log(fruits[0]);

Output:

Apple

You can also modify the array:

fruits.push("Orange");

The variable is still const.

Why?

Because we aren’t replacing the array itself.

We are modifying its contents.

This distinction is important when explaining const during interviews.


Variables With Objects

A variable can also store an object.

const user = {
    name: "Rahul",
    age: 25
};

You can access:

console.log(user.name);

Output:

Rahul

You can also create more complex objects:

const employee = {
    id: 101,
    name: "Rahul",
    department: "QA",
    experience: 8
};

This becomes especially important when we start learning interfaces.


Variables Inside Functions

Variables can be created inside functions.

function calculateTotal() {
    const price = 100;
    const quantity = 2;

    return price * quantity;
}

Here:

price
quantity

are local variables.

They exist inside the function.

You can think of a function as a room.

Variables created inside the room normally belong to that room.


Understanding Scope

Scope means where a variable can be accessed.

This is an important concept for interviews.

Consider:

let globalName = "Rahul";

function printName() {
    console.log(globalName);
}

The function can access globalName.

But consider:

function printName() {
    let name = "Rahul";
}

console.log(name);

This will not work because name exists only inside the function.


Block Scope

let and const are block scoped.

A block is usually represented by:

{
    // block
}

For example:

if (true) {
    let message = "Hello";

    console.log(message);
}

This works.

But:

if (true) {
    let message = "Hello";
}

console.log(message);

The second console.log() cannot access message.

The variable belongs to the block.


Why Scope Matters in Automation?

If you work with Playwright and TypeScript, scope becomes very important.

For example:

test("Login Test", async ({ page }) => {

    const username = "admin";

    await page.getByLabel("Username").fill(username);

});

Here, username belongs to that test block.

You cannot automatically expect it to be available everywhere else.

Understanding scope helps you avoid bugs related to variables being unavailable where you need them.


TypeScript Variables in Playwright

Now let’s connect the concept to something practical.

Suppose you are writing a Playwright test.

You might have:

const url = "https://example.com";

const username = "testuser";

const password = "Password123";

Then:

await page.goto(url);

await page.getByLabel("Username").fill(username);

await page.getByLabel("Password").fill(password);

Variables make your test much easier to maintain.

Imagine the URL changes.

Instead of searching through 50 test cases for:

https://example.com

you can update one variable.

This is one of the practical benefits of using meaningful variables.


Variables for Test Configuration

You might create:

const browserName = "chromium";

const baseUrl = "https://example.com";

const timeout = 30000;

const headless = true;

Now these values can be reused across your framework.

For example:

test.setTimeout(timeout);

This makes automation frameworks more configurable and easier to maintain.


Variables for Test Data

You can also create test data:

const username = "testuser";

const password = "Password123";

const expectedTitle = "Dashboard";

Then:

await page.getByLabel("Username").fill(username);

await page.getByLabel("Password").fill(password);

await expect(page).toHaveTitle(expectedTitle);

This is much cleaner than putting values directly into every statement.


Variables With Environment Values

In real automation frameworks, sensitive information should not normally be hardcoded directly into the test.

Instead of:

const password = "MyRealPassword";

you might read configuration from environment variables:

const password = process.env.PASSWORD;

Now your test can use an environment-specific value.

For example:

Development → Development password

QA → QA password

Staging → Staging password

Production → Production credentials

This is an important concept when you move from beginner automation scripts to professional frameworks.


Common Mistakes With TypeScript Variables

Mistake 1: Using var Everywhere

Avoid writing modern TypeScript like this:

var name = "Rahul";

Prefer:

const name = "Rahul";

or:

let name = "Rahul";

depending on whether reassignment is needed.


Mistake 2: Using let When const Is Better

Instead of:

let companyName = "ABC";

if you never reassign it, prefer:

const companyName = "ABC";

This communicates your intention more clearly.


Mistake 3: Poor Variable Names

Avoid:

let a = "Rahul";
let b = 25;
let c = true;

Prefer:

const userName = "Rahul";
const userAge = 25;
const isActive = true;

Mistake 4: Using any Without a Reason

Avoid:

let data: any;

when you know what the data should contain.

TypeScript’s biggest advantage is type safety.

Don’t throw that advantage away unnecessarily.


TypeScript Variable Interview Questions and Answers

Now let’s move into the part that many learners really need.

Interview preparation.

Understanding a topic is one thing.

Explaining it confidently during an interview is another.

Let’s practice.


Interview Question 1: What is a variable in TypeScript?

Answer:

A variable is a named container used to store a value.

For example:

let age = 25;

Here, age is the variable and 25 is its value.

TypeScript can also associate a type with the variable:

let age: number = 25;

Interview Question 2: What are the different ways to declare variables in TypeScript?

Answer:

TypeScript supports:

var
let
const

However, modern TypeScript development generally prefers let and const.

let is used when a variable may be reassigned, while const is used when the variable should not be reassigned.


Interview Question 3: What is the difference between let and const?

Answer:

The main difference is reassignment.

Example:

let count = 10;

count = 20;

This is valid.

But:

const count = 10;

count = 20;

This causes an error because const variables cannot be reassigned.

Both let and const are block scoped.


Interview Question 4: What is the difference between var, let, and const?

Answer:

var is function scoped, while let and const are block scoped.

let allows reassignment.

const does not allow reassignment after initialization.

Example:

let score = 10;
score = 20;

Valid.

const score = 10;
score = 20;

Invalid.

For modern TypeScript development, let and const are generally preferred over var.


Interview Question 5: What is type inference?

Answer:

Type inference is TypeScript’s ability to automatically determine the type of a variable from its value.

Example:

let name = "Rahul";

TypeScript automatically infers:

name → string

Similarly:

let age = 25;

is inferred as:

age → number

This means explicit type annotations are not always necessary.


Interview Question 6: What is a type annotation?

Answer:

A type annotation explicitly specifies the expected type of a variable.

Example:

let age: number = 25;

Here, : number is the type annotation.

Another example:

let name: string = "Rahul";

Interview Question 7: Can a const object be modified?

Answer:

Yes, its properties can generally be modified.

For example:

const user = {
    name: "Rahul"
};

user.name = "Amit";

This is allowed.

However, reassigning the entire object is not allowed:

user = {
    name: "Amit"
};

const prevents reassignment of the variable binding; it does not automatically make the object’s contents immutable.


Interview Question 8: What is variable scope?

Answer:

Scope defines where a variable can be accessed.

For example:

function test() {
    const name = "Rahul";
}

The name variable is available inside the function but not outside it.

Similarly, let and const are block scoped.


Interview Question 9: Why should we prefer const over let when possible?

Answer:

Because const communicates that the variable will not be reassigned.

For example:

const browser = "Chrome";

This tells other developers that browser is not expected to point to another value.

It also reduces accidental reassignment.


Interview Question 10: Why is var generally avoided in modern TypeScript?

Answer:

var has function-level scoping and behavior that can make code harder to reason about.

let and const provide block scope and clearer rules around reassignment.

Therefore, modern TypeScript code generally prefers let and const.


Scenario-Based Interview Questions

Now let’s make the questions more realistic.

These are the kinds of questions that can separate a beginner from someone who understands the concept.


Scenario 1: Which declaration would you use?

You have:

const baseUrl = "https://example.com";

The URL never changes.

Would you use let or const?

Answer:

Use:

const baseUrl = "https://example.com";

because the variable does not need reassignment.


Scenario 2: Retry Counter

You have:

let retryCount = 0;

Every time an API request fails, you increase the counter.

Would you use let or const?

Answer:

Use:

let retryCount = 0;

retryCount++;

because the value changes.


Scenario 3: Playwright Browser

You have:

const browserName = "chromium";

The browser name does not change during the test.

Which declaration should you use?

Answer:

const.

const browserName = "chromium";

Quick Interview Revision

Before an interview, remember these five points:

1. Variable = named storage location

2. let = can be reassigned

3. const = cannot be reassigned

4. var = older function-scoped declaration

5. Type inference = TypeScript automatically determines the type

If you understand these five points, you already have a strong foundation.


Practice Programs

Don’t stop after reading.

Try writing these programs yourself.

Program 1

Create variables for:

Name
Age
City
IsStudent

Use appropriate types.


Program 2

Create a variable called score.

Start it with:

50

Then change it to:

100

Use the correct variable declaration.


Program 3

Create an array containing five programming languages.

Example:

const languages: string[] = [
    "JavaScript",
    "TypeScript"
];

Add three more.


Program 4

Create an object representing a QA engineer.

Include:

name
experience
primarySkill
isAutomationEngineer

Program 5

Create variables representing a Playwright test:

baseUrl
username
password
timeout
browser

Choose appropriate variable declarations.


Mini Challenge

Can you predict the output?

let score = 10;

score = 20;

console.log(score);

Answer:

20

Now try:

const score = 10;

score = 20;

console.log(score);

What happens?

The program produces a TypeScript error because score is declared using const and cannot be reassigned.


TypeScript Variables Cheat Sheet

Keyword/ConceptMeaning
letVariable that can be reassigned
constVariable that cannot be reassigned
varOlder function-scoped declaration
Type annotationExplicitly defines a type
Type inferenceTypeScript automatically determines the type
ScopeWhere a variable can be accessed
Block scopeScope inside {}
stringText
numberNumeric value
booleantrue or false

Final Takeaway

Variables are one of the smallest concepts in TypeScript, but they are also one of the most important.

Every time your application remembers something, there is a good chance a variable is involved.

A username?

Variable.

A password?

Variable.

A product price?

Variable.

A Playwright URL?

Variable.

A test timeout?

Variable.

A retry counter?

Variable.

Once you understand variables, you have started learning the language’s basic building blocks.

The most important rules to remember are simple:

const name = "Rahul";

Use const when you don’t need reassignment.

let score = 10;
score = 20;

Use let when the value needs to change.

And:

let age: number = 25;

Use a type annotation when you want to explicitly communicate the expected type.

Finally, remember this:

Don’t learn TypeScript by memorizing syntax. Learn it by understanding why the syntax exists.

When you understand that a variable is simply a named place for storing information, let, const, types, scope, and type inference become much easier.

And this is only the beginning.

The next step is to understand TypeScript data types, where we will explore strings, numbers, booleans, arrays, tuples, objects, unions, any, unknown, null, undefined, and practical examples.


Frequently Asked Questions

1. What is a variable in TypeScript?

A variable is a named container used to store a value.

let age = 25;

Here, age stores the value 25.


2. Should I use let or const?

Use const by default when you don’t need to reassign the variable.

Use let when the value needs to change.

const name = "Rahul";

let score = 10;
score = 20;

3. Is var still supported in TypeScript?

Yes, TypeScript supports var because it is part of JavaScript.

However, modern TypeScript code generally prefers let and const because they provide clearer block-scoping behavior.


4. What is type inference in TypeScript?

Type inference is when TypeScript automatically determines the type of a value.

const name = "Rahul";

TypeScript understands that name is a string without you explicitly writing:

const name: string = "Rahul";

5. Can a const object be changed?

The object variable itself cannot be reassigned:

const user = {
    name: "Rahul"
};

But its properties can generally be changed:

user.name = "Amit";

If you need properties that cannot be reassigned, TypeScript provides features such as readonly.


AI + QA Automation Interview Questions and Answers | Playwright, MCP & AI Agents

Senior QA / SDET / Automation Architect Interview Preparation

AI is rapidly becoming an important part of modern QA automation. Senior QA engineers and SDETs are increasingly expected to understand not only traditional automation frameworks such as Playwright and Selenium, but also AI-assisted testing, AI agents, MCP, intelligent test generation, debugging, and self-healing automation.

This guide contains practical AI + QA Automation interview questions and answers designed for Senior QA Engineers, SDETs, QA Automation Architects, and professionals working with Playwright and AI-assisted automation.

Interview Tip: AI should be presented as an engineering accelerator, not a replacement for QA engineering judgment. The QA engineer remains responsible for business validation, test coverage, risk assessment, and final automation quality.

1. How Are You Using AI in Your Day-to-Day QA Automation Work?

Interview Answer

I use AI as an engineering assistant across the complete QA lifecycle rather than only for generating automation scripts. I use it for requirement analysis, test-case generation, identifying edge cases, creating Playwright scripts, locator suggestions, debugging failures, improving existing automation, generating test data, code reviews, and documentation.

In my Playwright automation work, I also use AI with Playwright MCP to allow an AI agent to interact with the application, explore the UI, understand the actual behavior, identify locators, and then generate or refine automation code.

I do not blindly trust AI-generated output. I treat AI as an accelerator while the QA engineer remains responsible for validating business logic, test coverage, framework standards, and final automation quality.

Key Points

  • Requirement analysis
  • Test design and edge cases
  • Playwright automation
  • Locator identification
  • Failure analysis and debugging
  • Refactoring and code review
  • Test data generation
  • Documentation

2. How Do You Use AI to Generate Test Cases From a Requirement?

Interview Answer

I provide the requirement, acceptance criteria, business rules, and relevant application context. I ask AI to analyze the requirement and generate positive, negative, boundary, accessibility, integration, error-handling, and regression scenarios.

I then review the generated cases against the actual business requirements because AI can produce technically valid but business-incorrect scenarios. After validation, I categorize the cases and decide which scenarios should be automated.

Example

For a subscription purchase workflow, AI can help identify scenarios such as:

  • Valid payment
  • Invalid card
  • Expired card
  • Payment failure
  • Session timeout
  • Address boundary conditions
  • Keyboard navigation
  • Screen-reader behavior
  • Security of sensitive data

A useful practice is to ask AI to clearly distinguish between explicit requirements and assumptions.


3. How Do You Use AI With Playwright to Generate Automation Scripts?

Interview Answer

I use a structured workflow instead of simply asking AI to generate a Playwright test.

First, I provide the requirement and scenario. Then I allow the AI to understand the application and existing framework. Where appropriate, I use Playwright MCP to explore the application and identify actual UI elements and behavior.

AI can then generate the Playwright test using existing page objects, fixtures, utilities, assertions, and coding conventions.

I execute the test, analyze failures, refine the implementation, and perform a human review before committing the code.

AI-Assisted Playwright Workflow

Requirement
    ↓
AI Analysis
    ↓
Test Scenario
    ↓
Browser Exploration
    ↓
Locator Discovery
    ↓
Code Generation
    ↓
Test Execution
    ↓
Failure Analysis
    ↓
Refinement
    ↓
Human Review

What Should You Validate?

  • Fixtures
  • Page objects
  • Test data
  • Assertions
  • Cleanup
  • Stable locators
  • Framework conventions

4. What Is Playwright MCP and How Does It Help QA Automation?

Interview Answer

Playwright MCP is an MCP-based integration that allows an AI agent to interact with a browser through Playwright capabilities.

Instead of relying only on static source code, the agent can explore the application, inspect the UI, perform browser actions, observe results, and use that context to generate or refine Playwright automation.

Traditional AI Automation

Requirement + Code/Context
          ↓
     Generated Script

MCP-Assisted Automation

Requirement
     ↓
AI Agent
     ↓
Browser Exploration
     ↓
Inspect Elements
     ↓
Perform Actions
     ↓
Observe Results
     ↓
Generate/Refine Test

Playwright MCP improves application interaction, but it does not automatically provide business understanding. Requirements and business rules remain essential.


5. What Are Playwright Planner, Generator and Healer?

Interview Answer

These can be understood as three stages of an AI-assisted automation workflow.

Planner

The Planner determines what should be tested. It analyzes requirements, acceptance criteria, and user journeys to create a test plan.

Generator

The Generator determines how the test should be automated. It converts the test plan into Playwright tests, locators, assertions, and supporting code.

Healer

The Healer analyzes why automation failed and proposes a repair. For example, it may identify changed locators or synchronization issues.

Important: AI-based healing should be treated as a proposed fix that requires validation. Automatic healing can otherwise hide a real application regression.

6. How Do You Use AI When a Playwright Test Fails?

Interview Answer

I use AI as a debugging assistant. I provide the test code, failure message, stack trace, screenshot, trace information, and relevant application behavior.

AI can help classify the failure as an application defect, locator problem, synchronization issue, test-data issue, environment problem, or automation defect.

I reproduce and validate the proposed fix before accepting it.

Information I Analyze

  • Stack trace
  • Playwright trace
  • Screenshot
  • DOM snapshot
  • Console errors
  • Network failures
  • Test code

The objective is root-cause analysis, not merely producing a passing test.

Avoid using arbitrary hard waits such as waitForTimeout() simply to make a test green.


7. Can AI Completely Automate Testing? What Are Its Limitations?

Interview Answer

AI can automate a significant portion of repetitive QA activities, but completely autonomous testing is risky.

Testing requires business understanding, risk assessment, exploratory thinking, and judgment. AI can misunderstand requirements, hallucinate APIs or locators, generate redundant tests, create incorrect assertions, or incorrectly heal tests.

Major Limitations of AI in Testing

  • Business-context limitations
  • Hallucinations and incorrect assumptions
  • False confidence from realistic-looking tests
  • Incorrect assertions
  • Maintenance and self-healing risks
  • Exploratory and usability testing limitations
  • Security and privacy concerns
  • Dependence on the quality of provided context

8. How Do You Validate AI-Generated Test Cases or Automation Code?

Interview Answer

I use a human-in-the-loop validation process.

I validate the AI-generated output against requirements, acceptance criteria, business rules, framework standards, and existing automation patterns.

Then I execute the test and verify that the assertions actually prove the intended behavior.

Validation Checklist

  • Requirement coverage
  • Positive scenarios
  • Negative scenarios
  • Boundary scenarios
  • Locator stability
  • Meaningful assertions
  • Synchronization
  • Framework compatibility
  • Test isolation
  • Data management
  • Cleanup
  • Security
  • CI execution
  • Flakiness

A particularly important validation is to run the test repeatedly and verify that the test actually fails when the intended application behavior is broken.


9. Which AI Tools Have You Used in QA Automation?

Interview Answer

I have worked with AI coding assistants such as GitHub Copilot and Claude-based coding workflows, and I have explored AI-assisted browser automation using Playwright MCP.

I use different tools for different parts of the QA lifecycle rather than expecting one tool to solve everything.

AI Tool Typical Usage
GitHub Copilot Code generation, refactoring and test generation
Claude Code Repository-level reasoning, codebase analysis and implementation assistance
Playwright MCP Browser exploration and AI-assisted Playwright automation
ChatGPT Requirement analysis, test design, debugging and documentation
AI Agents Multi-step exploration and automation workflows
Interview Tip: Only mention AI tools that you can genuinely describe and demonstrate from your own experience.

10. Give Me a Real Project Example Where AI Improved Your QA Automation Process

Interview Answer

In a web QA automation project, I worked on subscription and checkout-related functionality involving user journeys such as subscription selection, checkout, payment, cancellation, and switching plans.

I used AI-assisted automation to accelerate test design and Playwright implementation. I provided requirements and acceptance criteria to identify important journeys and edge cases, used browser exploration to understand the actual UI and locators, and used AI to generate an initial Playwright implementation following the existing framework.

After execution, I used AI to analyze failures and distinguish application issues from automation, synchronization, test-data, or environment problems.

The improvement was not simply generating code faster. AI reduced repetitive analysis, scaffolding, locator discovery, debugging, and documentation while I remained responsible for business validation and final test quality.

Important Interview Tip

Do not invent productivity percentages. If asked for metrics, provide measured figures only.

Useful metrics include:

  • Automation development time
  • Maintenance effort
  • Execution stability
  • Defect coverage

11. How Would You Integrate GitHub Copilot or Claude Code With Your VS Code Project?

Interview Answer

I would not expect an AI coding assistant to understand the framework simply because it is installed in VS Code.

I would provide structured repository context and engineering instructions. I would make framework conventions explicit through documentation, architecture guidance, coding standards, test examples, and the supported repository instruction mechanism for the chosen AI tool.

I would also ask the agent to inspect the repository before generating code.

Repository Areas to Analyze

  • package.json
  • playwright.config.ts
  • Tests
  • Page objects
  • Fixtures
  • Utilities
  • Configuration
  • Test data

Framework Information to Document

  • Automation architecture
  • Page-object guidelines
  • Locator strategy
  • Authentication approach
  • Test-data strategy
  • Coding standards

After implementation, validate the generated code using linting, TypeScript compilation, Playwright execution, code review, and CI.


12. If Copilot Generates an Automation Script, What Would You Validate?

Interview Answer

I would validate the requirement coverage, test objective, locator quality, assertions, synchronization, framework compatibility, test isolation, test data, cleanup, error handling, maintainability, security, CI compatibility, and flakiness.

Validation Checklist

  • Requirement and business behavior
  • Stable locators
  • Meaningful assertions
  • No unnecessary hard waits
  • Reuse of fixtures, page objects and utilities
  • Independent test execution
  • Reliable test data and cleanup
  • No secrets or sensitive data
  • Repeated execution
  • CI execution
  • Failure behavior
  • Maintainability

13. How Would You Leverage Copilot to Understand Your Framework and Codebase?

Interview Answer

I would use Copilot as a codebase analysis assistant before using it as a code generator.

I would ask it to map the architecture, test execution flow, authentication mechanism, fixtures, page objects, utilities, configuration, and existing patterns.

Once it understands those relationships, I would ask it to identify the best existing components to reuse before creating new code.

Useful Questions to Ask the AI Assistant

  • Explain the automation architecture.
  • Trace how a Playwright test moves from the spec through fixtures, configuration and browser initialization.
  • Identify the authentication fixture for a new authenticated test.
  • Find page objects and their public methods.
  • Identify the recommended test-data approach.
  • Find similar existing tests and explain their common pattern.
  • Identify impacted tests before modifying a shared page object.
  • Detect duplicate utilities before creating new ones.

Advanced: AI-Powered QA Automation Architecture

A senior-level AI-powered QA automation architecture can be represented as follows:

Requirement
    ↓
AI Test Planner
    ↓
Test Scenarios
    ↓
Browser Exploration / Playwright MCP
    ↓
AI Test Generator
    ↓
Playwright Tests
    ↓
Execution
    ↓
 ┌───────────────┐
 │               │
PASS            FAIL
 │               │
 ↓               ↓
CI/CD       AI Debugger / Healer
                    ↓
              Proposed Fix
                    ↓
              Human Review
                    ↓
                  CI/CD

The objective is intelligent, context-aware and maintainable automation rather than blindly autonomous testing.

Key Interview Language for AI + QA Roles

  • Human-in-the-loop
  • Requirement-to-test automation
  • Context-aware code generation
  • Repository-level understanding
  • Browser exploration
  • Root-cause analysis
  • Self-healing / AI-assisted healing
  • Test coverage optimization
  • Framework-aware automation
  • AI as an accelerator, not a replacement

60-Second AI + QA Interview Introduction

I have been using AI as an engineering accelerator across different stages of the QA automation lifecycle, rather than using it only for code generation.

For requirement analysis, I use AI to identify test scenarios, edge cases, negative scenarios and potential risks. With Playwright automation, I use AI-assisted workflows and Playwright MCP to explore the application, understand the UI, identify locators and generate automation code based on actual application behavior.

I also use AI for debugging Playwright failures by analyzing stack traces, screenshots, traces, locators and application behavior.

For repository-level development, I use AI coding assistants such as GitHub Copilot or Claude-based workflows to understand existing framework patterns, fixtures, page objects and utilities before generating new automation.

The key principle I follow is human-in-the-loop automation: I validate requirements, business logic, test coverage, locator stability, assertions, framework compatibility and execution results before committing automation.


Strong Closing Statement for the Interview

My approach is to use AI to reduce repetitive engineering effort while increasing the depth and speed of testing.

I use AI for requirement analysis, test design, automation generation, browser exploration, debugging, code review and documentation.

With Playwright and MCP-based workflows, I can move toward a more agentic approach where the system can understand a requirement, explore the application, create a test plan, generate automation and analyze failures.

However, I don’t consider AI-generated output automatically trustworthy. The QA engineer still owns business validation, risk assessment, test coverage and the final quality of the automation.

The goal is intelligent, context-aware and maintainable automation with humans making the important quality decisions.


Final Takeaway

For senior QA and SDET interviews, the important message is not simply that you know how to use AI to generate test scripts.

You should be able to explain how AI fits into the complete QA lifecycle:

  1. Understand the requirement.
  2. Generate and analyze test scenarios.
  3. Explore the application.
  4. Identify reliable locators.
  5. Generate framework-compatible automation.
  6. Execute the tests.
  7. Analyze failures.
  8. Propose fixes.
  9. Validate the fixes.
  10. Maintain high-quality automation through CI/CD.

The strongest approach is to combine AI capabilities with QA engineering expertise. AI can accelerate repetitive engineering activities, but human judgment remains essential for business validation, risk assessment, test coverage, and quality.

200 Playwright + TypeScript Interview Questions and Answers

Below is a full set of 100 interview questions with answers, organized by topic. Each answer is written to be interview-ready.


Section 1: Fundamentals & Architecture (Q1–Q15)

Q1. What is Playwright?
Playwright is an open-source end-to-end testing framework by Microsoft that drives Chromium, Firefox, and WebKit through a single API. It supports auto-waiting, network interception, multiple browser contexts, and cross-language bindings including TypeScript.

Q2. Why use TypeScript with Playwright?
TypeScript gives static typing for locators, fixtures, and page objects, catching errors at compile time. It also provides IDE autocompletion for the Playwright API and makes refactoring safer in large suites.

Q3. What are the core components of Playwright?
Browser, BrowserContext, Page, Locator, Frame, Request/Response, and the Test Runner (@playwright/test).

Q4. Difference between Playwright and Selenium?
Playwright communicates with browsers via the DevTools protocol and its own patched builds, giving auto-waiting, network interception, and multi-context isolation out of the box. Selenium relies on WebDriver and generally requires explicit waits and separate drivers per browser.

Q5. What is auto-waiting?
Before performing an action, Playwright waits for the element to be attached, visible, stable, enabled, and to receive events. This eliminates most manual sleep calls.

Q6. What browsers does Playwright support?
Chromium (Chrome/Edge), Firefox, and WebKit (Safari engine).

Q7. What is a BrowserContext?
An isolated session inside a browser with its own cookies, localStorage, and cache. It behaves like an incognito window and is the unit of test isolation.

Q8. Can one browser have multiple contexts?
Yes. One browser instance can host many contexts, each fully isolated, enabling parallel tests and multi-user scenarios.

Q9. What is the difference between page and context?
A context is the session; a page is a single tab within that session. Multiple pages can exist in one context and share cookies.

Q10. What is a Locator?
A Locator is a lazy reference to an element that is re-resolved on every action, so it never goes stale after a re-render.

Q11. Difference between Locator and ElementHandle?
Locators are auto-waiting, re-resolved, and preferred. ElementHandles are static references that can become detached and are considered legacy.

Q12. What test runners can be used with Playwright?
The official @playwright/test runner, plus Jest, Mocha, Vitest, or Cucumber if configured manually.

Q13. What is playwright.config.ts?
The central configuration file defining test directory, timeouts, retries, reporters, projects, and global setup/teardown.

Q14. How do you run a single test file?
npx playwright test tests/login.spec.ts

Q15. How do you run tests in headed mode?
npx playwright test --headed


Section 2: Locators & Selectors (Q16–Q30)

Q16. Why prefer getByRole() over CSS selectors?
It targets elements by ARIA role and accessible name, matching how users perceive the page. It survives DOM and class-name refactoring.

Q17. List Playwright’s built-in locators.
getByRole, getByText, getByLabel, getByPlaceholder, getByAltText, getByTitle, getByTestId, and locator() for CSS/XPath.

Q18. How do you locate by test ID?
page.getByTestId('submit-btn') — the attribute is configurable via testIdAttribute in config.

Q19. Difference between getByText and getByRole?
getByText matches visible text; getByRole matches semantic role plus optional name. Role-based is more robust.

Q20. How do you chain locators?
page.getByRole('row').filter({ hasText: 'Alice' }).getByRole('button', { name: 'Edit' })

Q21. What does .filter({ hasText }) do?
Narrows a locator to elements containing the given text.

Q22. How do you select the nth element?
locator.nth(0) for the first, locator.first(), locator.last().

Q23. What is locator.all()?
Returns an array of all matching elements resolved at call time — useful for iteration but not for auto-waiting.

Q24. How do you handle strict mode violations?
Make the locator more specific, or use .first(), .nth(), or .filter().

Q25. What is strict mode?
Playwright throws if a locator resolves to more than one element during an action, preventing ambiguous interactions.

Q26. Can you use XPath?
Yes: page.locator('xpath=//button[@id="save"]'), though it’s discouraged.

Q27. How do you locate inside an iframe?
page.frameLocator('#frame').getByRole('button')

Q28. How do you locate inside shadow DOM?
Playwright pierces open shadow DOM automatically with CSS locators.

Q29. What is locator.waitFor()?
Waits for the locator to reach a state: attached, detached, visible, or hidden.

Q30. How do you count matching elements?
await locator.count()


Section 3: Actions & Assertions (Q31–Q45)

Q31. How do you click an element?
await page.getByRole('button', { name: 'Submit' }).click()

Q32. Difference between fill() and type()?
fill() sets the value directly and is fast; type() (deprecated in favor of pressSequentially) simulates keystrokes.

Q33. How do you handle a dropdown?
await page.getByLabel('Country').selectOption('IN')

Q34. How do you check a checkbox?
await page.getByLabel('Terms').check()

Q35. How do you hover?
await page.getByText('Menu').hover()

Q36. How do you drag and drop?
await page.locator('#src').dragTo(page.locator('#dst'))

Q37. How do you upload a file?
await page.getByLabel('Upload').setInputFiles('path/to/file.pdf')

Q38. How do you handle a native dialog?

typescript

page.on('dialog', async d => { await d.accept(); });

Q39. Difference between expect(locator).toBeVisible() and locator.isVisible()?
expect retries until timeout; isVisible() returns immediately and does not wait.

Q40. What is web-first assertion?
An assertion that retries automatically, e.g. await expect(locator).toHaveText('Done').

Q41. How do you assert a URL?
await expect(page).toHaveURL(/dashboard/)

Q42. How do you assert a title?
await expect(page).toHaveTitle('Home')

Q43. How do you assert an attribute?
await expect(locator).toHaveAttribute('disabled', '')

Q44. How do you assert element count?
await expect(locator).toHaveCount(3)

Q45. What is expect.soft()?
A soft assertion that records failure but continues the test instead of aborting.


Section 4: Waits & Timing (Q46–Q55)

Q46. Why is page.waitForTimeout() discouraged?
It introduces fixed delays, making tests slow and flaky. Prefer web-first assertions or waitFor.

Q47. How do you wait for a network response?
await page.waitForResponse(r => r.url().includes('/api/users') && r.status() === 200)

Q48. How do you wait for navigation?
await page.waitForURL('**/dashboard')

Q49. How do you wait for load state?
await page.waitForLoadState('networkidle')

Q50. What are the load states?
load, domcontentloaded, and networkidle.

Q51. What is a floating promise?
An async call without await, causing the next line to run before the action completes — the top cause of flakiness.

typescript

page.getByRole('button').click();          // ❌ floating
await page.getByRole('button').click();    // ✅

Q52. How do you set a custom timeout for one action?
await locator.click({ timeout: 10000 })

Q53. How do you override the global timeout?
Set timeout in playwright.config.ts, or pass --timeout on the CLI.

Q54. What is the default test timeout?
30 seconds.

Q55. What is the default assertion timeout?
5 seconds, configurable via expect.timeout.


Section 5: Network & API Testing (Q56–Q65)

Q56. How do you intercept a request?

typescript

await page.route('**/api/users', route => route.fulfill({ status: 200, body: '[]' }));

Q57. How do you abort a request?
await page.route('**/*.png', route => route.abort())

Q58. How do you modify a response?
Fetch the original with route.fetch(), modify the body, then route.fulfill().

Q59. Difference between page.request and request fixture?
page.request shares the page’s cookie jar (authenticated as the user); the request fixture has its own isolated cookie jar.

Q60. How do you mock an API response?
await page.route('**/api/login', r => r.fulfill({ json: { token: 'abc' } }))

Q61. How do you wait for multiple responses?
await Promise.all([page.waitForResponse(...), page.click(...)])

Q62. How do you test a REST API directly?
const res = await request.get('/users'); expect(res.ok()).toBeTruthy();

Q63. Can Playwright test GraphQL?
Yes, via request.post() with a JSON query body, or by intercepting the endpoint.

Q64. How do you record HAR?
await page.routeFromHAR('trace.har', { update: true }) or via config recordHar.

Q65. How do you replay a HAR?
await page.routeFromHAR('trace.har')


Section 6: Authentication & State (Q66–Q72)

Q66. How do you reuse authentication across tests?
Save storageState once during setup, then reference it via test.use({ storageState: 'auth.json' }).

Q67. How do you save storage state?
await context.storageState({ path: 'auth.json' })

Q68. What does storageState contain?
Cookies and localStorage origins.

Q69. How do you run a setup project?
Define a project named setup in config and add dependencies: ['setup'] to test projects.

Q70. How do you log in via API instead of UI?
Use the request fixture to POST credentials, then save the resulting storage state.

Q71. How do you test multiple users in parallel?
Create separate contexts with different storage states.

Q72. How do you clear cookies mid-test?
await context.clearCookies()


Section 7: Fixtures & Test Structure (Q73–Q82)

Q73. What is a fixture?
A reusable setup/teardown unit injected into tests, replacing beforeEach boilerplate.

Q74. How do you define a custom fixture?

typescript

export const test = base.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
});

Q75. Difference between test and test.extend?
test is the base runner; test.extend creates a customized runner with additional fixtures.

Q76. What are worker-scoped fixtures?
Fixtures defined with { scope: 'worker' } that are created once per worker rather than per test.

Q77. How do you override a fixture in one file?
test.use({ storageState: 'admin.json' }) at the top of the file.

Q78. What does beforeAll do?
Runs once before all tests in a file.

Q79. What does beforeEach do?
Runs before every test in a file.

Q80. What is test.describe?
Groups related tests and can apply hooks or test.use at the group level.

Q81. What is test.step?
A labeled sub-section inside a test that appears in reports, improving readability.

Q82. How do you skip a test?
test.skip(), test.skip(condition), or test.fixme().


Section 8: Page Object Model & TypeScript (Q83–Q90)

Q83. What is the Page Object Model?
A design pattern where each page is represented by a class encapsulating locators and actions.

Q84. How do you type a page object in TypeScript?

typescript

export class LoginPage {
  constructor(private readonly page: Page) {}
  readonly username = this.page.getByLabel('Username');
  async login(u: string, p: string): Promise<DashboardPage> {
    await this.username.fill(u);
    await this.page.getByLabel('Password').fill(p);
    await this.page.getByRole('button', { name: 'Sign in' }).click();
    return new DashboardPage(this.page);
  }
}

Q85. Why should navigation methods return the next page object?
It enforces flow correctness at compile time and chains steps fluently.

Q86. How do you share page objects across tests?
Expose them as fixtures in a custom test.extend.

Q87. What is as const used for in Playwright tests?
Freezing literal types, e.g. role arrays or config maps, so TypeScript narrows them correctly.

Q88. How do you type a locator array?
const rows: Locator[] = await page.getByRole('row').all();

Q89. How do you type a network response body?
const data = await res.json() as UserDto; or use a Zod schema for runtime validation.

Q90. Should page objects contain assertions?
Prefer keeping assertions in tests; page objects should expose state and actions. Some teams allow lightweight assertions in page objects for reuse.


Section 9: Debugging & Reporting (Q91–Q95)

Q91. How do you debug a failing test?
Run with --debug (Playwright Inspector), --headed, or use page.pause().

Q92. What is the Trace Viewer?
A tool that replays a test with DOM snapshots, network, console, and source — enabled via trace: 'on-first-retry'.

Q93. How do you enable tracing?
In config: use: { trace: 'on' } or per context: context.tracing.start({ screenshots: true, snapshots: true }).

Q94. How do you view a trace?
npx playwright show-trace trace.zip

Q95. What reporters ship with Playwright?
list, line, dot, html, json, junit, and blob.


Section 10: CI/CD & Best Practices (Q96–Q100)

Q96. How do you run Playwright in CI?
Install browsers with npx playwright install --with-deps, then run npx playwright test. Use the official Docker image or GitHub Action.

Q97. How do you parallelize tests?
Playwright parallelizes by file by default. Use fullyParallel: true for test-level parallelism and configure workers.

Q98. How do you handle retries?
Set retries: 2 in config or --retries=2. Combine with trace: 'on-first-retry' for diagnosis.

Q99. How do you shard tests across CI machines?
npx playwright test --shard=1/4

Q100. What are the top best practices for Playwright + TypeScript?

  1. Always await async calls (avoid floating promises).
  2. Prefer role/test-id locators over CSS/XPath.
  3. Use web-first assertions instead of manual waits.
  4. Isolate tests with contexts and storage state.
  5. Encapsulate pages in typed page objects.
  6. Enable traces on retry.
  7. Keep tests independent and parallel-safe.
  8. Validate API responses with schemas (e.g., Zod).
  9. Run in CI with sharding and retries.
  10. Treat flakiness as a bug, not noise.

100 Advanced Playwright + TypeScript Interview Questions and Answers

Below is a full set of 100 advanced interview questions with answers, assuming you have already mastered the fundamentals covered in the previous set. Each answer is written to be interview-ready and focuses on production-grade patterns, edge cases, and architectural decisions.


Section 1: Advanced Locators & Auto-Waiting (Q1–Q10)

Q1. How does Playwright’s auto-waiting actually work under the hood?
Playwright injects an actionability check before every interaction. The element must pass five conditions: attached to DOM, visible, stable (not animating), enabled, and receiving events (not obscured). Each check retries until timeout. This is implemented via injected scripts that poll the element state and a set of heuristics comparing bounding boxes across animation frames.

Q2. What causes “element is not stable” errors and how do you fix them?
The element’s bounding box changed between two consecutive animation frames, typically due to CSS animations, transitions, or layout shifts. Fixes: disable animations in config (animations: 'disabled'), wait for a stable parent, or target a non-animated ancestor.

Q3. When does getByRole fail to find an element that exists?
When the element lacks an implicit or explicit ARIA role, or when its accessible name is computed from hidden content. Also, custom components that render interactive elements without proper roles (e.g., a <div> styled as a button without role="button") won’t be found.

Q4. How do you handle a locator that matches multiple elements intentionally?
Use .filter(), .nth(), or iterate with .all(). For lists, prefer scoping: page.getByRole('row').filter({ hasText: 'Alice' }).getByRole('button'). Never rely on .first() in production tests; it’s a flakiness signal.

Q5. What is the difference between locator.waitFor({ state: 'attached' }) and locator.waitFor({ state: 'visible' })?
attached means the element exists in the DOM even if hidden. visible means it has a non-empty bounding box and is not display: none or visibility: hidden. For interaction, you almost always want visible.

Q6. How do you locate an element by its text that is split across child elements?
getByText normalizes whitespace and matches against the element’s full text content, so <span>Hello <b>World</b></span> is matched by getByText('Hello World'). For partial matches, pass { exact: false } (default) or use a regex.

Q7. What is a “strict mode violation” and when is it desirable?
Playwright throws when a locator resolves to more than one element during an action. This is desirable because it forces you to write specific locators. To opt out for intentional multi-element operations, use .all(), .nth(), or .first() with explicit intent.

Q8. How do you handle elements inside closed shadow DOM?
You cannot pierce closed shadow DOM from the page context. Options: expose a test hook on the component, use evaluate to access the shadow root if the host exposes it, or switch to component testing where you mount the component directly.

Q9. What is the “receives events” check and when does it cause false negatives?
Playwright checks that the element at the click point is the target or a descendant. It fails when an overlay, tooltip, or invisible element intercepts the click. Fix: dismiss the overlay, use force: true (last resort), or click the actual receiving element.

Q10. How do you write a locator that survives a component library upgrade?
Avoid internal class names, data attributes not part of the public contract, and deeply nested selectors. Prefer role-based locators, accessible names, and data-testid attributes that the team commits to maintaining.


Section 2: Network Interception & API Testing (Q11–Q25)

Q11. What is the difference between route.fulfill(), route.continue(), and route.fetch()?
fulfill() returns a mock response without hitting the server. continue() lets the request proceed, optionally with modified headers, method, or body. fetch() sends the request to the real server and returns the response so you can modify it before fulfilling .

Q12. How do you modify a response body while preserving the original headers and status?
Use route.fetch(), parse the response, modify the data, then route.fulfill({ response, body: JSON.stringify(modified) }). Passing the original response object preserves headers and status .

Q13. What is the page.request vs request fixture distinction in advanced scenarios?
page.request shares the page’s cookie jar and is authenticated as the current user. The request fixture has its own isolated cookie jar, making it suitable for admin operations, setup/teardown, or unauthenticated API checks .

Q14. How do you record and replay network traffic with HAR files?
Record with routeFromHAR(path, { update: true }) during development. Replay with routeFromHAR(path) in tests. HAR files capture request/response headers, bodies, and timings, providing realistic test data without hitting live services .

Q15. How do you simulate network throttling in Playwright?
Use CDP: client.send('Network.emulateNetworkConditions', { offline: false, downloadThroughput: speed * 1024 / 8, uploadThroughput: speed * 1024 / 8, latency: ms }). This lets you test loading states, timeouts, and retry logic .

Q16. What is the “Cookie header override” caveat with route.continue()?
You cannot override the Cookie header via route.continue({ headers }). The browser fills cookies from its own cookie jar. To change cookie state, use context.addCookies() or modify storageState .

Q17. How do you test a WebSocket connection in Playwright?
Playwright doesn’t have native WebSocket mocking, but you can: (a) evaluate window.ws.readyState in the page context , (b) use MSW (Mock Service Worker) to intercept WebSocket handlers , or (c) expose the socket on window and assert its state.

Q18. How do you validate API response schemas in Playwright tests?
Use Zod, AJV, or the playwright-schema-validator plugin. Fetch the response, then run schema.parse(await response.json()). This catches contract violations that status code checks miss .

Q19. How do you handle OAuth/SSO flows without driving the IdP UI every time?
Best practice: perform token exchange via API in a setup project, inject tokens into localStorage via addInitScript, navigate to the app, then save storageState. Keep one slow, real SSO test tagged @auth-integration for nightly runs .

Q20. How do you test retry logic with Playwright’s route mocking?
Set up a counter in the route handler. Return 503 for the first N requests, then 200. Assert the UI eventually shows the success state and that the counter equals the expected retry count .

Q21. What is the maxRedirects option in route.fetch() and when would you use it?
It controls how many redirects route.fetch() follows before returning the response. Useful for auth flows that redirect through multiple endpoints; you can assert on the final response or simulate a redirect loop failure.

Q22. How do you abort requests selectively by resource type?
Use route.request().resourceType() inside the handler: if (['image', 'font'].includes(type)) route.abort(); else route.continue();. This speeds up tests and lets you test offline/blocked-resource behavior .

Q23. What is route.fallback() and when do you need it?
fallback() continues to the next matching route handler instead of fulfilling immediately. Use it when you have multiple route handlers for the same URL and want conditional logic layered across them.

Q24. How do you test that your app handles a 500 error gracefully?
Intercept the API route, call route.fetch() to get the real response (or skip it), then route.fulfill({ status: 500, body: ... }). Assert the error UI appears .

Q25. Can you intercept requests made by a service worker?
By default, service workers bypass Playwright’s route interception. Set serviceWorkers: 'block' in context options to force all requests through the page’s network stack, making them interceptable .


Section 3: Advanced Fixtures & Test Architecture (Q26–Q40)

Q26. What is the difference between test.extend and mergeTests?
test.extend creates a new test object with additional fixtures, typically in one file. mergeTests combines multiple extended test objects into one, useful for composing fixtures from different domains (API, UI, auth) .

Q27. How do you create a fixture that depends on another fixture?
Declare the dependency in the fixture function’s argument destructuring: myFixture: async ({ page, request }, use) => { ... }. Playwright resolves dependencies automatically.

Q28. What is the “automatic fixture” pattern and when is it useful?
An automatic fixture runs even if not explicitly requested, via test.extend({ autoFixture: [async ({}, use) => { ... }, { auto: true }] }). Useful for global setup like console error monitoring or network logging.

Q29. How do you scope a fixture to run once per worker instead of per test?
Use { scope: 'worker' } in the fixture options. The fixture is initialized when the worker starts and torn down when it exits, ideal for expensive resources like database connections.

Q30. What is the “boxed fixture” pattern for safe teardown?
Wrap the value in an object with a dispose method so teardown always runs even if the test fails mid-use. Playwright 1.50+ supports explicit resource management with await using.

Q31. How do you share authentication state across parallel workers?
Create the auth state file in a setup project, then reference it via test.use({ storageState: 'auth/user.json' }). All workers read the same file, avoiding redundant login flows .

Q32. What is a “fixture override” and when should you avoid it?
Overriding a built-in fixture (like page) changes behavior for all tests in scope. Avoid it unless you need global instrumentation; prefer creating a new fixture that wraps the original.

Q33. How do you pass parameters to fixtures from test files?
Use a test.use({ myOption: value }) at the file or describe level, and declare the option as a fixture that reads the value. This is the “option fixture” pattern.

Q34. What is the difference between test.describe.serial and test.describe.parallel?
serial runs tests in order and stops on first failure, useful for dependent flows. parallel is the default and runs tests concurrently within the describe block.

Q35. How do you create a fixture that provides a typed page object?

typescript

export const test = base.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
});

This gives type-safe access to loginPage in every test .

Q36. What is the “fixture composition” pattern for cross-cutting concerns?
Layer fixtures: base.extend(authFixtures).extend(apiFixtures).extend(loggingFixtures). Each layer adds one concern, keeping fixtures focused and testable.

Q37. How do you clean up test data in a worker-scoped fixture?
Create a unique namespace per worker (e.g., worker-${workerInfo.workerIndex}), seed data in the fixture setup, and delete all data matching that namespace in teardown.

Q38. What is the “test data factory” pattern and how does it integrate with fixtures?
A factory is a function that creates entities via API with unique names. Expose it as a fixture that also tracks created entities and cleans them up in teardown.

Q39. How do you handle flaky tests at the framework level?
Set retries: 2 in config, trace: 'on-first-retry' for diagnosis, and use test.fail() to mark known flaky tests. Quarantine persistent flakes in a separate project and fix root causes rather than relying on retries.

Q40. What is test.step and how does it improve debuggability?
test.step('Login as admin', async () => { ... }) creates labeled sections in the trace viewer and reports. Steps can be nested and are useful for documenting multi-phase tests.


Section 4: Component Testing (Q41–Q50)

Q41. What is Playwright component testing and how does it differ from E2E?
Component testing mounts individual UI components in a real browser without the full application. It tests rendering, props, and interactions in isolation, running faster than E2E .

Q42. Which frameworks does Playwright component testing support?
React (@playwright/experimental-ct-react), Vue, and Svelte. It requires a separate config (playwright-ct.config.ts) and uses the mount fixture .

Q43. What is the “story” concept in Playwright’s newer component testing model?
A story is a wrapper that embeds a component in a specific scenario (props, providers, mock data). Stories live in *.story.tsx files and are rendered by a “gallery” page .

Q44. How does the mount fixture work?
const component = await mount(<App />) renders the component in the gallery and returns a Locator scoped to the component root. You can then use component.getByRole(...) for interactions .

Q45. How do you test a component that makes API calls?
Use route interception in the component test just like E2E: page.route('**/api/data', ...). Alternatively, pass mock data via props or a provider wrapper.

Q46. Can you use Playwright’s trace viewer with component tests?
Yes. Enable trace: 'on-first-retry' in playwright-ct.config.ts. The trace shows component interactions, DOM snapshots, and network calls .

Q47. What is the “gallery” in Playwright component testing?
A single page served by your dev server that exposes window.mount() and window.unmount(). It discovers *.story.* files and renders the selected story into a root element .

Q48. How do you test component state changes (e.g., toggles, accordions)?
Mount the component, assert initial state via toContainText or toHaveAttribute, perform the interaction, then assert the new state. The trace captures each step for debugging .

Q49. What are the limitations of Playwright component testing?
It’s experimental; the API may change. It doesn’t test routing, full-page layout, or backend integration. You still need E2E tests for those concerns .

Q50. When would you choose component testing over E2E?
For design system components, complex form controls, and UI states that are hard to trigger in a full app (e.g., error states, loading skeletons). Component tests run faster and are less flaky.


Section 5: Visual Regression & Accessibility (Q51–Q60)

Q51. How does toHaveScreenshot() work?
On first run, it captures a baseline PNG. On subsequent runs, it compares the current screenshot pixel-by-pixel against the baseline within configured tolerances .

Q52. What are the key config options for reducing false positives in visual tests?
animations: 'disabled' freezes CSS animations, caret: 'hide' hides text cursors, scale: 'css' normalizes DPI differences, and maxDiffPixelRatio: 0.01 allows 1% pixel difference .

Q53. How do you mask dynamic content in a screenshot?
Pass a mask array of locators: toHaveScreenshot({ mask: [page.locator('.timestamp')] }). The masked areas are rendered as solid rectangles in both baseline and comparison .

Q54. How do you update visual baselines when the UI changes intentionally?
Run npx playwright test --update-snapshots and commit the new PNGs. Review the diff before committing to ensure changes are intentional .

Q55. What is the “stability gate” pattern in visual testing?
Before taking a screenshot, assert that a critical element is visible (e.g., a heading or data row). This ensures the page has fully rendered and prevents capturing a blank or mid-render state .

Q56. How do you test accessibility with Playwright?
Use @axe-core/playwright: const results = await new AxeBuilder({ page }).analyze(); expect(results.violations).toEqual([]);. Run it as a separate test or integrate into existing tests.

Q57. What are the limitations of visual regression testing?
It catches pixel changes but not semantic regressions (e.g., a button that looks right but has wrong behavior). It can be flaky across OS/browser versions. It doesn’t test responsiveness unless you run multiple viewports.

Q58. How do you handle visual tests across different browsers?
Playwright generates separate baselines per browser and platform by default (e.g., screenshot-chromium-darwin.png). Commit all variants or run visual tests on a single canonical browser in CI.

Q59. What is toMatchSnapshot() vs toHaveScreenshot()?
toMatchSnapshot() compares arbitrary data (strings, objects). toHaveScreenshot() compares rendered page images. Use the former for DOM snapshots or API responses.

Q60. How do you test responsive design with Playwright?
Create projects with different viewport sizes (devices['iPhone 13'], devices['Desktop Chrome']), or use page.setViewportSize() within a test. Assert layout changes via element visibility or position.


Section 6: Debugging & Diagnostics (Q61–Q70)

Q61. What is the Trace Viewer and what does it capture?
A timeline of every action, DOM snapshot before/after each step, network requests, console logs, and source code. Enable via trace: 'on-first-retry' and view with npx playwright show-trace trace.zip .

Q62. How do you use page.pause() effectively?
It opens the Playwright Inspector and pauses execution, letting you step through actions, inspect locators, and resume. Use it for interactive debugging, not in committed tests.

Q63. What is the “floating promise” pattern and how do you detect it?
Calling an async method without await: page.click('button') instead of await page.click('button'). ESLint rules (@typescript-eslint/no-floating-promises) and Playwright’s own warnings help detect it.

Q64. How do you debug a test that fails only in CI?
Enable trace: 'on-first-retry', run with --workers=1 to rule out concurrency, check for timezone/locale differences, and compare CI viewport to local. The trace viewer is the primary tool.

Q65. What does the “Call log” in the trace viewer show?
A chronological list of Playwright API calls with their parameters, return values, and timing. It helps identify which action hung or returned unexpected data.

Q66. How do you capture console errors during a test?

typescript

const errors: string[] = [];
page.on('console', msg => {
  if (msg.type() === 'error') errors.push(msg.text());
});
// after test:
expect(errors).toEqual([]);

Q67. What is the “Snapshot” tab in the trace viewer?
A DOM snapshot of the page at the selected point in the timeline. You can inspect elements, see computed styles, and verify what the page looked like when an action ran.

Q68. How do you debug locator resolution?
Use page.locator('...').highlight() to visually highlight matched elements, or inspect the locator in the Playwright Inspector. The trace viewer shows which element each action targeted.

Q69. What is the “Network” tab in the trace viewer?
A list of all network requests with headers, bodies, timings, and status codes. Useful for diagnosing API failures, slow responses, or unexpected requests.

Q70. How do you record a video of a test run?
Set video: 'retain-on-failure' or 'on' in config. Videos are attached to the test result and viewable in the HTML report.


Section 7: Performance & Advanced Configuration (Q71–Q80)

Q71. How do you parallelize tests across files vs within files?
By default, Playwright parallelizes by file. fullyParallel: true enables test-level parallelism within files, requiring tests to be independent .

Q72. What is sharding and how do you use it?
npx playwright test --shard=1/4 splits tests across 4 machines. Each shard runs a deterministic subset, enabling horizontal scaling in CI.

Q73. How do you configure multiple projects for different browsers and devices?
In playwright.config.ts, define a projects array with different use options: { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, etc. Tests run against all projects by default.

Q74. What is the webServer config option?
It starts a dev server before tests and shuts it down after. Configure with command, url, and reuseExistingServer: !process.env.CI .

Q75. How do you set per-project timeouts?
Add timeout to the project’s use object, or override at the test level with test.setTimeout(ms).

Q76. What is globalSetup vs a setup project?
globalSetup is a single function that runs once before all projects. A setup project is a full Playwright project that runs before dependent projects and can use fixtures, reporters, and traces.

Q77. How do you run tests only on changed files in CI?
Use --only-changed (Playwright 1.42+) or integrate with your CI’s change detection to pass specific file paths.

Q78. What is the blob reporter and when is it useful?
It produces a report.zip that can be merged from multiple shards into a single HTML report using npx playwright merge-reports.

Q79. How do you configure retries per project?
Add retries: 2 to the project’s use object. Different projects can have different retry policies (e.g., flaky visual tests get more retries).

Q80. What is expect.configure() and when would you use it?
It creates a customized expect with default options: const slowExpect = expect.configure({ timeout: 10000 }). Useful for slow-running assertions in specific test suites.


Section 8: CI/CD & Production Patterns (Q81–Q90)

Q81. How do you cache Playwright browsers in GitHub Actions?
Cache ~/.cache/ms-playwright keyed on the Playwright version from package.json. This avoids re-downloading browsers on every run.

Q82. How do you handle test data isolation in parallel CI runs?
Use unique prefixes per worker/run (e.g., run-${process.env.GITHUB_RUN_ID}-worker-${workerIndex}). Clean up in worker teardown.

Q83. What is the “smoke test” project pattern?
A subset of critical tests tagged @smoke that run on every PR. Full suite runs on merge to main or nightly.

Q84. How do you report test results to a dashboard like TestDino or Allure?
Add the reporter to playwright.config.ts and set the API token via environment variable. Results stream during the run .

Q85. How do you handle secrets in CI?
Use GitHub Actions secrets or your CI’s secret store. Never commit credentials. For auth state, generate it in a setup step and pass the file path via env var.

Q86. What is the “merge reports” workflow for sharded runs?
Each shard uploads a blob report. A final job downloads all blobs and runs npx playwright merge-reports --reporter=html ./blobs.

Q87. How do you prevent flaky tests from blocking merges?
Run flaky tests in a separate non-blocking job, quarantine persistent flakes, and use test.fixme() with a tracking issue. Retries buy time but don’t fix root causes.

Q88. How do you test against a staging environment with real data?
Use environment-specific configs (baseURL from env), seed test data via API in setup, and clean up in teardown. Never run destructive tests against staging data.

Q89. What is the “canary” pattern for test selection?
Run a small, fast subset of tests on every commit, a medium suite on PR, and the full suite nightly. This balances feedback speed with coverage.

Q90. How do you version and migrate visual baselines?
Store baselines in the repo, update them when UI changes are intentional, and review diffs in PR. Use a dedicated branch for baseline updates to avoid noise.


Section 9: TypeScript-Specific Patterns (Q91–Q100)

Q91. How do you type a page object that returns the next page?

typescript

async login(user: string, pass: string): Promise<DashboardPage> {
  // ... actions
  return new DashboardPage(this.page);
}

This creates a fluent, type-safe navigation chain .

Q92. What is the satisfies operator and how does it help Playwright tests?
const config = { retries: 2 } satisfies PlaywrightTestConfig validates the shape without widening the type. Useful for config objects where you want both validation and literal inference.

Q93. How do you type network response bodies safely?
Define a Zod schema and parse: const data = UserSchema.parse(await response.json()). This gives you a typed, validated object at runtime.

Q94. What is the Locator type and how do you store locators in a page object?
Locator is the return type of page.locator() and page.getByRole(). Store them as readonly class properties initialized in the constructor.

Q95. How do you type a custom fixture that wraps a page object?

typescript

type Fixtures = { loginPage: LoginPage };
export const test = base.extend<Fixtures>({ ... });

The generic parameter gives full type safety for test and test.use.

Q96. What is the Page vs Locator return type distinction?
Page represents a tab; Locator represents an element. Page objects typically take Page in the constructor and expose Locator properties.

Q97. How do you use discriminated unions for test data?

typescript

type User = { role: 'admin'; permissions: string[] } | { role: 'viewer' };

This forces exhaustive handling and prevents invalid combinations in test factories.

Q98. What is the as const pattern for locator arrays?
const roles = ['button', 'link'] as const preserves literal types, enabling roles.map(r => page.getByRole(r)) with proper narrowing.

Q99. How do you type page.evaluate() return values?
const title = await page.evaluate<string>(() => document.title) — pass the expected return type as a generic. For complex objects, define an interface and cast.

Q100. What is the “branded type” pattern for test IDs?

typescript

type TestId = string & { __brand: 'TestId' };

This prevents accidentally passing a raw string where a test ID is expected, catching mix-ups at compile time.


Summary of Advanced Topics Covered

SectionFocusKey Patterns
1Advanced LocatorsAuto-waiting internals, strict mode, shadow DOM
2Network & APIRoute interception, HAR, OAuth bypass, WebSockets
3FixturesComposition, worker scope, boxed teardown
4Component TestingMount fixture, stories, gallery
5Visual & A11yBaseline management, masking, axe-core
6DebuggingTrace viewer, floating promises, console capture
7PerformanceSharding, parallelization, webServer
8CI/CDCaching, secrets, smoke tests, merge reports
9TypeScriptPage object typing, Zod validation, branded types

Playwright BDD Framework Step By Step

To build an automated test framework using Playwright, Cucumber (BDD), and the Page Object Model (POM) pattern with TypeScript, follow this structured setup.

Cucumber Framework Official docs

1.Initialize Project & Dependencies:Node.js required.

Create a project directory and install the necessary dependencies for Playwright, @cucumber/cucumber, and TypeScript.

Bash

mkdir playwright-cucumber-pom
cd playwright-cucumber-pom
npm init -y
npm install --save-dev @playwright/test @cucumber/cucumber ts-node typescript @types/node
npx playwright install

2.Configure TypeScript & Cucumber:Set up root configuration files.

Create a tsconfig.json file in your root folder:

JSON

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true
  }
}

Create a cucumber.json file in your root folder to define execution profiles:

JSON

{
  "default": {
    "formatOptions": {
      "snippetInterface": "async-await"
    },
    "paths": ["src/tests/features/**/*.feature"],
    "require": ["src/tests/steps/**/*.ts", "src/hooks/hooks.ts"],
    "requireModule": ["ts-node/register"],
    "format": ["progress", "html:reports/cucumber-report.html"]
  }
}

3.Set Up Browser Hooks & Context Management:Manages browser lifecycle.

Create src/hooks/hooks.ts to manage launching and closing the browser instance before and after scenarios.

TypeScript

import { Before, After, BeforeAll, AfterAll } from '@cucumber/cucumber';
import { Browser, BrowserContext, Page, chromium } from '@playwright/test';

let browser: Browser;
let context: BrowserContext;
export let page: Page;

BeforeAll(async () => {
  browser = await chromium.launch({ headless: false });
});

Before(async () => {
  context = await browser.newContext();
  page = await context.newPage();
});

After(async () => {
  await page.close();
  await context.close();
});

AfterAll(async () => {
  await browser.close();
});

4.Create the Page Object Model (POM):Encapsulate page UI elements and actions.

Create src/pages/LoginPage.ts to store page selectors and interaction logic.

TypeScript

import { Page, Locator } from '@playwright/test';

export class LoginPage {
  private page: Page;
  private usernameInput: Locator;
  private passwordInput: Locator;
  private loginButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.usernameInput = page.locator('#user-name');
    this.passwordInput = page.locator('#password');
    this.loginButton = page.locator('#login-button');
  }

  async navigateTo(url: string) {
    await this.page.goto(url);
  }

  async login(username: string, password: string) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }
}

5.Write the Feature File:Define BDD scenarios using Gherkin.

Create src/tests/features/login.feature:

Gherkin

Feature: User Authentication

  Scenario: Successful login with valid credentials
    Given I navigate to "https://www.saucedemo.com/"
    When I log in with username "standard_user" and password "secret_sauce"
    Then I should see the inventory page title "Swag Labs"

6.Implement Step Definitions:Map Feature steps to Page Object code.

Create src/tests/steps/loginSteps.ts:

TypeScript

import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from '@playwright/test';
import { page } from '../../hooks/hooks';
import { LoginPage } from '../../pages/LoginPage';

let loginPage: LoginPage;

Given('I navigate to {string}', async (url: string) => {
  loginPage = new LoginPage(page);
  await loginPage.navigateTo(url);
});

When('I log in with username {string} and password {string}', async (username: string, password: string) => {
  await loginPage.login(username, password);
});

Then('I should see the inventory page title {string}', async (expectedTitle: string) => {
  const actualTitle = await page.title();
  expect(actualTitle).toBe(expectedTitle);
});

7.Add Execution Script & Run Tests:Execute tests via npm.

Update your package.json file to include a test command:

JSON

"scripts": {
  "test": "cucumber-js"
}

Execute your framework in the terminal:

Bash

npm test

Configure Reporting in Cucumber Framework

1. Install Reporter Package:

Bash

npm install --save-dev multiple-cucumber-html-reporter

2. Configure JSON Output in cucumber.json:

Ensure Cucumber outputs test results in JSON format so the reporter engine can process them:

JSON

{
  "default": {
    "requireModule": ["ts-node/register"],
    "require": ["src/hooks/**/*.ts", "src/tests/steps/**/*.ts"],
    "format": ["json:reports/cucumber_report.json"]
  }
}

3. Create Report Generation Generator Script (reporter.js):

Create reporter.js at the root of your project:

JavaScript

const report = require('multiple-cucumber-html-reporter');

report.generate({
  jsonDir: './reports/',
  reportPath: './reports/html-report/',
  metadata: {
    browser: { name: 'chromium', version: 'latest' },
    device: 'Local Test Engine',
    platform: { name: 'Windows/Linux' }
  },
  customData: {
    title: 'Execution Info',
    data: [
      { label: 'Project', value: 'Playwright Cucumber Framework' },
      { label: 'Environment', value: 'Staging' }
    ]
  }
});

3. Update your package.json to unify test execution, reporting, and linting into seamless npm commands:

JSON

"scripts": {
  "test": "cucumber-js || npm run report",
  "test:smoke": "cucumber-js --tags '@smoke' || npm run report",
  "test:parallel": "cucumber-js --parallel 4 || npm run report",
  "report": "node reporter.js"
}

JavaScript Operators Assignment: 60+ Real-Life Practice Questions

Learning JavaScript operators becomes much easier when you practice them with real-world programming problems instead of solving only simple mathematical examples.

In this JavaScript Operators Assignment, you will solve more than 60 practical programming problems based on shopping bills, salary calculations, banking, loan eligibility, electricity bills, cab fares, student results, QA automation reports, and other real-life scenarios.

The exercises cover Arithmetic, Assignment, Comparison, Logical, Unary, Ternary, Optional Chaining, Nullish Coalescing, in, and instanceof operators.

Recommended for: JavaScript beginners, automation testers, QA engineers, SDET candidates, and students preparing for JavaScript or Playwright interviews.


What You Will Practice

  • Arithmetic Operators
  • Assignment Operators
  • Comparison Operators
  • Logical Operators
  • Unary Operators
  • Ternary Operator
  • Optional Chaining Operator
  • Nullish Coalescing Operator
  • in Operator
  • instanceof Operator
  • Combination of multiple JavaScript operators
  • Formula-based JavaScript programs
  • Real-world business calculations

Prerequisites

Before attempting these exercises, you should understand JavaScript variables, data types, basic expressions, and console.log().

You should also be familiar with the basic JavaScript operators explained in the JavaScript Operators tutorial.


1. Arithmetic Operators – Assignment Questions

Arithmetic operators are commonly used for calculations. The main operators include +, -, *, /, %, and **.

Assignment 1: E-Commerce Shopping Bill

An online shopping website has the following products:

1 Laptop = ₹55,000
1 Mouse = ₹750
1 Keyboard = ₹1,500

A customer purchases:

1 Laptop
2 Mouse
1 Keyboard

Write a JavaScript program to calculate the total shopping amount.

Formula:

Total =
(Laptop Price × Quantity)
+ (Mouse Price × Quantity)
+ (Keyboard Price × Quantity)

Assignment 2: Restaurant Bill Split

Four friends visit a restaurant and the total bill is ₹3,850.

Calculate how much each person should pay if the bill is divided equally.

Formula:

Amount Per Person = Total Bill / Number of People

Assignment 3: Employee Net Salary

An employee has:

Basic Salary = ₹40,000
HRA = ₹8,000
Bonus = ₹5,000
Tax = ₹4,500

Calculate the gross salary and net salary.

Formula:

Gross Salary = Basic Salary + HRA + Bonus

Net Salary = Gross Salary - Tax

Assignment 4: Simple Interest Calculator

Create a program using:

Principal = ₹100,000
Rate = 8%
Time = 3 years

Calculate simple interest and total amount.

Formula:

SI = (Principal × Rate × Time) / 100

Total Amount = Principal + SI

Assignment 5: Compound Interest Calculator

Calculate compound interest for:

Principal = ₹50,000
Rate = 10%
Time = 2 years

Use the exponentiation operator **.

Formula:

Amount = Principal × (1 + Rate / 100) ** Time

Compound Interest = Amount - Principal

Assignment 6: Celsius to Fahrenheit

A weather application receives temperature in Celsius.

Celsius = 35

Convert it into Fahrenheit.

Formula:

Fahrenheit = (Celsius × 9 / 5) + 32

Assignment 7: BMI Calculator

A person’s weight is 72 kg and height is 1.75 meters.

Calculate BMI.

Formula:

BMI = Weight / Height²

Use the ** operator to calculate the square of the height.

Assignment 8: Rectangle Area and Perimeter

Create a program for:

Length = 15
Width = 8

Calculate:

Area = Length × Width

Perimeter = 2 × (Length + Width)

Assignment 9: Student Group Calculation

A teacher has 53 students and wants to create groups of 5 students.

Calculate:

  • Number of complete groups
  • Number of remaining students

Use both / and %.

Assignment 10: Convert Minutes into Hours

A training video has a duration of 367 minutes.

Convert the duration into hours and remaining minutes.

Expected format:

6 Hours 7 Minutes

Use division and modulus operators.


2. Assignment Operators – Assignment Questions

Assignment operators are useful when a variable needs to be updated based on its existing value.

Assignment 11: Bank Account Transactions

A customer’s bank balance is ₹50,000.

Perform these transactions:

Salary credited = ₹25,000
Electricity Bill = ₹3,500
Shopping = ₹7,000

Use += and -= to update the balance.

Assignment 12: Shopping Cart Update

The shopping cart initially contains products worth ₹2,000.

Add:

Shoes = ₹3,500
Shirt = ₹1,200

Then remove an item worth ₹800.

Use assignment operators to calculate the final cart value.

Assignment 13: Product Price Increase

A laptop costs ₹50,000. The manufacturer increases the price by 10%.

Update the price using an assignment operator.

Hint:

price += price * 10 / 100;

Assignment 14: Employee Salary Hike

An employee earns ₹60,000 per month. The company gives a 15% salary hike.

Update the salary using an assignment operator.

Assignment 15: Game Score Calculator

A player starts with a score of 100.

Perform the following:

Enemy defeated → +50
Bonus collected → +100
Penalty → -30
Score doubled → ×2

Use +=, -=, and *=.


3. Comparison Operators – Assignment Questions

Comparison operators return Boolean values such as true or false. They are commonly used in validation and decision-making logic.

Assignment 16: Voting Eligibility

A person is 20 years old.

Check whether the person is eligible to vote.

Condition:

Age >= 18

Assignment 17: Exam Passing Status

A student scores 39 marks and the passing marks are 40.

Determine whether the student has passed.

Assignment 18: Free Delivery Eligibility

An e-commerce website provides free delivery for orders of ₹999 or more.

Create an order amount and check whether the customer qualifies for free delivery.

Assignment 19: Loose vs Strict Equality

Consider:

let otpFromServer = 123456;
let otpFromUser = "123456";

Compare the values using both:

==
===

Observe the result and explain why the two operators behave differently.

Assignment 20: Product Stock Validation

A store has 10 units of a product, but the customer requests 12 units.

Determine whether sufficient stock is available.

Use:

Requested Quantity <= Available Quantity

Assignment 21: Speed Limit Checker

A car is travelling at 85 km/h on a road where the speed limit is 80 km/h.

Write a program to determine whether the driver has exceeded the speed limit.


4. Logical Operators – Assignment Questions

Logical operators such as &&, ||, and ! are used to combine or reverse conditions.

Assignment 22: Bank Loan Eligibility

A bank approves a loan when:

Age >= 21
AND
Monthly Salary >= ₹25,000
AND
Credit Score >= 700

Use the && operator to determine eligibility.

Assignment 23: Login Validation

A user should be allowed to log in only when both the username and password are correct.

Username = "admin"
Password = "admin123"

Use &&.

Assignment 24: Discount Eligibility

A shopping website gives a discount when either:

  • The customer is a Premium Member.
  • The order amount is ₹5,000 or more.

Use the || operator.

Assignment 25: Job Eligibility

A company requires:

Experience >= 3 years
JavaScript Skill = true
Automation Skill = true

Determine whether the candidate is eligible.

Assignment 26: Movie Ticket Eligibility

A person can watch a movie when either:

  • Age is 18 or above.
  • Parent permission is available.

Use the || operator.

Assignment 27: Account Status

Given:

let accountBlocked = false;

Use the ! operator to determine whether the account is active.


5. Unary Operators – Assignment Questions

Unary operators work with a single operand. Common examples include ++, --, typeof, and !.

Assignment 28: Website Visitor Counter

A website currently has 1,000 visitors.

When a new visitor opens the website, increase the count using ++.

Assignment 29: Inventory Counter

A store has 50 units of a product.

A customer purchases one item. Reduce the stock using --.

Assignment 30: Cricket Score Counter

A batsman has scored 49 runs and scores one additional run.

Use ++ to update the score.

Assignment 31: Identify JavaScript Data Types

Create variables for:

name = "Rahul"
age = 25
isEmployee = true
salary = undefined

Use typeof to determine the data type of each variable.

Assignment 32: Login Status Reversal

Given:

let isLoggedIn = false;

Use ! to reverse the login status.


6. Ternary Operator – Assignment Questions

The ternary operator provides a concise way to write simple conditional expressions.

Syntax:

condition ? value1 : value2;

Assignment 33: Student Pass or Fail

A student scores 65 marks.

Display Pass when marks are 40 or above; otherwise display Fail.

Use the ternary operator.

Assignment 34: Adult or Minor

A person’s age is 17.

Use the ternary operator to display either Adult or Minor.

Assignment 35: Free Delivery

An online order is worth ₹1,200.

Display:

Free Delivery

when the order amount is at least ₹999; otherwise display:

Delivery Charge ₹100

Assignment 36: Even or Odd

Given:

let number = 27;

Use the modulus operator together with the ternary operator to determine whether the number is even or odd.

Assignment 37: Maximum of Two Numbers

Given:

Number1 = 45
Number2 = 78

Use the ternary operator to determine the larger number.

Assignment 38: Employee Bonus

An employee receives a ₹10,000 bonus when years of service are 5 or more. Otherwise, the employee receives ₹5,000.

Calculate the bonus using the ternary operator.


7. Optional Chaining Operator ?.

Optional chaining is useful when working with objects or API responses where a property may not exist.

Assignment 39: Customer Address

let customer = {
    name: "Rahul",
    address: {
        city: "Bhopal"
    }
};

Safely retrieve:

customer.address.city
customer.address.pincode

Use optional chaining so that the program does not throw an error when the property does not exist.

Assignment 40: API Response Validation

let response = {
    user: {
        profile: {
            name: "Amit"
        }
    }
};

Safely access:

response.user.profile.name
response.user.profile.mobile
response.user.address.city

Use ?..


8. Nullish Coalescing Operator ??

The nullish coalescing operator can be used to provide a default value when a variable is null or undefined.

Assignment 41: Default Username

let username = null;

Display Guest User when the username is null or undefined.

Use ??.

Assignment 42: Default Product Discount

An API returns:

let discount = null;

If the discount is unavailable, use 0 as the default value.

Assignment 43: Employee Salary

let employee = {
    name: "John"
};

Safely retrieve the employee’s salary.

If the salary is unavailable, display:

Salary Not Available

Combine ?. and ??.


9. in Operator – Assignment Questions

The in operator is useful when checking whether a property exists in an object.

Assignment 44: User Profile Validation

let user = {
    name: "Rahul",
    email: "rahul@gmail.com"
};

Check whether the following properties exist:

name
email
mobile

Use the in operator.

Assignment 45: Product Information Validation

let product = {
    id: 101,
    name: "Laptop",
    price: 55000
};

Check whether the following properties exist:

price
discount
stock

10. instanceof Operator – Assignment Questions

Assignment 46: Employee Object Validation

Create an Employee class:

class Employee {
    constructor(name) {
        this.name = name;
    }
}

Create an employee object and use instanceof to verify that the object belongs to the Employee class.

Assignment 47: Array Validation

Create:

let products = ["Laptop", "Mouse", "Keyboard"];

Use instanceof to determine whether products is an instance of Array.


11. Mixed JavaScript Operator Challenges

The following problems are designed to simulate real application scenarios. You will need to combine multiple JavaScript operators.

Assignment 48: E-Commerce Final Bill Calculator

A customer purchases:

Laptop = ₹50,000
Mouse = ₹1,000
Keyboard = ₹2,000

Discount rules:

Purchase >= ₹50,000 → 10% discount
Otherwise → 5% discount

GST is 18%.

Calculate:

  • Subtotal
  • Discount
  • Amount after discount
  • GST
  • Final amount

Formulas:

Subtotal = Laptop + Mouse + Keyboard

Discount = Subtotal × DiscountPercentage / 100

AmountAfterDiscount = Subtotal - Discount

GST = AmountAfterDiscount × 18 / 100

FinalAmount = AmountAfterDiscount + GST

Assignment 49: Employee Monthly Salary Calculator

Employee details:

Basic Salary = ₹50,000
HRA = 20%
DA = 10%
PF = 12%
Tax = 5%

Calculate HRA, DA, gross salary, PF deduction, tax deduction, and net salary.

Formulas:

HRA = Basic × 20 / 100
DA = Basic × 10 / 100
Gross = Basic + HRA + DA
PF = Basic × 12 / 100
Tax = Gross × 5 / 100
Net Salary = Gross - PF - Tax

Assignment 50: Electricity Bill Calculator

A customer consumes 350 units.

Billing rules:

First 100 units → ₹5/unit
Next 200 units → ₹7/unit
Above 300 units → ₹10/unit

Calculate the final electricity bill.

Challenge: Calculate the bill slab by slab rather than multiplying all units by ₹10.

Assignment 51: Cab Fare Calculator

A cab company charges:

Base Fare = ₹50
Per KM Charge = ₹12
Distance = 25 KM

If the distance exceeds 20 KM, add a long-distance charge of ₹100.

Calculate the final fare.

Formula:

Fare = Base Fare + Distance × PerKM

Assignment 52: EMI Calculator

Calculate the approximate monthly EMI for:

Loan Amount = ₹500,000
Annual Interest Rate = 10%
Loan Duration = 5 Years

Use:

Monthly Rate = Annual Rate / 12 / 100

Number of Payments = Years × 12

EMI =
P × R × (1 + R)^N
-----------------
(1 + R)^N - 1

Where P is the principal, R is the monthly interest rate, and N is the number of monthly payments.

Assignment 53: Student Result Calculator

A student has the following marks:

Math = 78
Science = 82
English = 69
Computer = 91
Hindi = 74

Calculate:

  • Total marks
  • Percentage
  • Pass/Fail status
  • Grade

Grade rules:

Percentage >= 90 → A+
Percentage >= 80 → A
Percentage >= 70 → B
Percentage >= 60 → C
Percentage >= 40 → D
Below 40 → Fail

The student must also fail if any individual subject mark is below 40.

Assignment 54: Online Shopping Checkout

Customer details:

Cart Amount = ₹4,500
Premium Member = true
Coupon Available = true

Rules:

  • Premium Member → 10% discount
  • Coupon Available → Additional ₹500 discount
  • Orders of ₹3,000 or more → Free delivery
  • Otherwise → ₹100 delivery charge

Calculate the final payable amount.

Use arithmetic, comparison, logical, ternary, and assignment operators.

Assignment 55: Bank Loan EMI Eligibility

Customer details:

Age = 30
Monthly Salary = ₹60,000
Existing EMI = ₹10,000
Credit Score = 750
Requested EMI = ₹18,000

Eligibility rules:

Age >= 21 AND Age <= 60

Credit Score >= 700

Total EMI <= 50% of Monthly Salary

Calculate:

Total EMI = Existing EMI + Requested EMI

Maximum Allowed EMI = Salary × 50 / 100

Display Loan Approved or Loan Rejected.


12. Bonus JavaScript Operator Challenges

Assignment 56: Food Delivery Order Calculator

Create a food delivery billing program with:

Food Amount = ₹850
Delivery Distance = 7 KM
Premium Member = false
Coupon Discount = ₹100

Rules:

  • Food Amount of ₹500 or more → 5% discount
  • Premium Member → Free delivery
  • Non-premium and distance <= 5 KM → ₹40 delivery
  • Non-premium and distance > 5 KM → ₹80 delivery
  • GST = 5%

Calculate the final payable amount.

Assignment 57: Fuel Cost Calculator

A car travels 650 KM.

Distance = 650 KM
Mileage = 18 KM/L
Petrol Price = ₹105/L

Calculate:

Fuel Required = Distance / Mileage

Fuel Cost = Fuel Required × Petrol Price

If the fuel cost exceeds ₹3,000, calculate the amount exceeding the budget.

Assignment 58: Profit Percentage Calculator

A shopkeeper purchases a laptop for ₹45,000 and sells it for ₹52,000.

Calculate:

Profit = Selling Price - Cost Price

Profit Percentage =
Profit / Cost Price × 100

Finally determine whether the transaction resulted in profit or loss.

Assignment 59: Attendance Calculator

A student has:

Total Classes = 120
Classes Attended = 92

Calculate:

Attendance Percentage =
Classes Attended / Total Classes × 100

The student is eligible for the examination only when attendance is at least 75%.

Assignment 60: QA Automation Test Report

An automation test suite contains:

Total Tests = 250
Passed = 210
Failed = 25
Skipped = 15

Calculate:

  • Pass Percentage
  • Failure Percentage
  • Skipped Percentage

Formula:

Percentage = Count / Total Tests × 100

The build is considered PASSED when:

Pass Percentage >= 90
AND
Failed Tests <= 10

Otherwise, display FAILED.


13. Final Mini Project – Employee Payroll System

This is the final challenge. Build a complete employee payroll calculator using multiple JavaScript operators.

Employee Information

Employee Name = "Rahul"
Basic Salary = ₹60,000
Performance Rating = 4.5
Years of Service = 6

Salary Components

HRA = 20% of Basic Salary
DA = 10% of Basic Salary
PF = 12% of Basic Salary

Performance Bonus Rules

Rating >= 4.5 → 15% Bonus
Rating >= 4.0 → 10% Bonus
Rating >= 3.0 → 5% Bonus
Otherwise → No Bonus

Loyalty Bonus

If years of service are 5 or more, the employee receives a ₹10,000 loyalty bonus.

Calculate

  • HRA
  • DA
  • Performance Bonus
  • Loyalty Bonus
  • Gross Salary
  • PF
  • Net Salary

Expected Output Format

Employee: Rahul
Basic Salary: ₹60000
HRA: ₹____
DA: ₹____
Performance Bonus: ₹____
Loyalty Bonus: ₹____
Gross Salary: ₹____
PF Deduction: ₹____
Net Salary: ₹____

How to Solve These JavaScript Assignments

Follow these steps for every problem:

  1. Read the problem carefully.
  2. Identify the input values.
  3. Identify the required output.
  4. Write the mathematical formula if applicable.
  5. Identify which JavaScript operators are required.
  6. Create meaningful variables.
  7. Implement the calculation or condition.
  8. Display the result using console.log().
  9. Test the program with different input values.

Assignment Rules for Students

  1. Use meaningful variable names.
  2. Do not hardcode calculated results.
  3. Use the operator relevant to each assignment.
  4. Display intermediate calculations where appropriate.
  5. Add comments explaining important formulas.
  6. For challenge programs, combine multiple operator types.
  7. Test each program with at least two different sets of input values.
  8. Use console.log() to clearly display the final output.
  9. For formula-based problems, write the formula as a comment before implementing it.
  10. Try to solve the problem independently before looking at a solution.

JavaScript Operators Assignment – Learning Outcome

After completing these assignments, you should be comfortable using JavaScript operators to solve practical programming problems involving calculations, validation, decision-making, object handling, and business logic.

These exercises are particularly useful for students preparing for JavaScript interviews, QA automation, SDET interviews, Selenium JavaScript automation, and Playwright with TypeScript.

Frequently Asked Questions

What are JavaScript operators?

JavaScript operators are symbols or keywords used to perform operations on values and variables. They are commonly used for calculations, comparisons, assignments, logical conditions, and object-related operations.

What are the main types of JavaScript operators?

The commonly used categories include arithmetic, assignment, comparison, logical, unary, ternary, and special operators such as optional chaining, nullish coalescing, in, and instanceof.

Which JavaScript operators should beginners learn first?

Beginners should first learn arithmetic, assignment, comparison, logical, and ternary operators. Once these are comfortable, they can move to unary and modern operators such as ?. and ??.

Why should JavaScript operators be practiced using real-life problems?

Real-life problems help students understand how operators are used in applications such as shopping carts, banking systems, payroll applications, billing systems, login validation, and test automation reports.

Are these JavaScript operator exercises suitable for interviews?

Yes. These exercises are useful for building the calculation and logical reasoning skills commonly tested in JavaScript and automation interviews.

Conclusion

Practicing JavaScript operators through real-world scenarios is one of the best ways to strengthen JavaScript fundamentals. Instead of memorizing operator definitions, solve problems involving shopping bills, salaries, banking, loans, electricity bills, student results, and automation reports.

Start with the basic arithmetic assignments and gradually move toward the mixed operator challenges and the final employee payroll project.

Tip: Do not immediately look for the solution. First identify the required formula, select the appropriate operators, write the logic yourself, and then test your program with different inputs.

Next Step: After completing these assignments, practice JavaScript Arrays, JavaScript Functions, objects, loops, and scenario-based JavaScript programs to build stronger programming skills.

JavaScript Interview Questions and Answers

Are you preparing for a JavaScript interview? This comprehensive guide covers 100+ JavaScript interview questions and answers from beginner to advanced levels.

These questions are useful for JavaScript Developers, Frontend Developers, QA Automation Engineers, SDETs, and Playwright Automation Engineers.

The questions are organized into different categories, including JavaScript fundamentals, variables, data types, functions, arrays, objects, promises, asynchronous JavaScript, closures, OOP, event handling, and advanced JavaScript concepts.


JavaScript Interview Questions for Beginners

1. What is JavaScript?

Answer: JavaScript is a high-level programming language used to create dynamic and interactive web applications.

JavaScript can run in:

  • Web browsers
  • Node.js
  • Playwright
  • Backend applications
  • Desktop and mobile applications
let name = "Deepesh";

console.log(`Hello ${name}`);

2. What are the features of JavaScript?

Answer: Some important features of JavaScript are:

  • Lightweight
  • Dynamically typed
  • Object-oriented
  • Prototype-based
  • Event-driven
  • Supports asynchronous programming
  • Supports functional programming
  • Supports first-class functions
  • Cross-platform
  • Supports modules

3. Is JavaScript the same as Java?

Answer: No. JavaScript and Java are completely different programming languages.

JavaScript Java
Dynamically typed Statically typed
Primarily used for web applications and scripting General-purpose programming language
Prototype-based Class-based
Runs in browsers and Node.js Runs on the JVM

JavaScript Variables and Data Types

4. What is a variable in JavaScript?

Answer: A variable is a named container used to store data.

JavaScript provides three keywords for declaring variables:

var x = 10;
let y = 20;
const z = 30;

Modern JavaScript generally prefers let and const.


5. What is the difference between var, let, and const?

Feature var let const
Scope Function Block Block
Redeclaration Yes No No
Reassignment Yes Yes No
Hoisting Yes Yes, TDZ Yes, TDZ
let age = 25;

age = 30;

const country = "India";

// country = "USA"; // Error

6. What are the data types in JavaScript?

Answer: JavaScript data types can be broadly divided into primitive and non-primitive types.

Primitive Data Types

  • String
  • Number
  • BigInt
  • Boolean
  • Undefined
  • Null
  • Symbol

Non-Primitive Data Types

  • Object
  • Array
  • Function
let name = "John";
let age = 25;
let active = true;
let value;
let data = null;
let user = {};
let numbers = [1, 2, 3];

7. What is the difference between null and undefined?

Answer:

undefined generally means that a variable has been declared but has not been assigned a value.

let x;

console.log(x); // undefined

null represents an intentional absence of a value.

let user = null;

8. What is typeof in JavaScript?

Answer: typeof is an operator used to determine the type of a value.

console.log(typeof "Hello"); // string
console.log(typeof 10);      // number
console.log(typeof true);    // boolean
console.log(typeof {});      // object

One well-known JavaScript behavior is:

console.log(typeof null);

Output:

object

This is a historical behavior of JavaScript.


JavaScript Operators

9. What is the difference between == and ===?

Answer:

== performs loose equality and may perform type conversion.

=== performs strict equality and checks both value and type.

console.log(5 == "5");  // true
console.log(5 === "5"); // false

In modern JavaScript, === is generally preferred.


10. What is the difference between != and !==?

5 != "5";   // false
5 !== "5";  // true

!= performs loose inequality, while !== performs strict inequality.


JavaScript Functions

11. What is a function in JavaScript?

Answer: A function is a reusable block of code designed to perform a specific task.

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

console.log(add(10, 20));

Output:

30

12. What is a function expression?

Answer: A function expression is a function assigned to a variable.

const add = function(a, b) {
    return a + b;
};

console.log(add(10, 20));

13. What is an arrow function?

Answer: Arrow functions provide a shorter syntax for writing functions.

const add = (a, b) => {
    return a + b;
};

It can also be written as:

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

14. What is the difference between a normal function and an arrow function?

One of the most important differences is how they handle this.

const user = {
    name: "John",

    greet: function() {
        console.log(this.name);
    }
};

user.greet();

Arrow functions do not create their own this. They inherit this from their surrounding lexical scope.


JavaScript Scope and Hoisting

15. What is scope?

Answer: Scope determines where a variable can be accessed in a JavaScript program.

Common types of scope include:

  • Global scope
  • Function scope
  • Block scope
  • Module scope

16. What is hoisting?

Answer: Hoisting describes how JavaScript processes declarations before executing code within their relevant scope.

console.log(x);

var x = 10;

Output:

undefined

With let:

console.log(x);

let x = 10;

This results in a ReferenceError because the variable is in the Temporal Dead Zone until initialization.


17. What is the Temporal Dead Zone?

Answer: The Temporal Dead Zone, commonly called TDZ, is the period between entering a scope and the point where a let or const variable is initialized.

console.log(name);

let name = "John";

The variable cannot be accessed before its initialization.


JavaScript Arrays

18. What is an array?

Answer: An array is an ordered collection of values.

const numbers = [10, 20, 30, 40];

console.log(numbers[0]); // 10

19. What is the difference between map(), filter(), and forEach()?

map()

Creates a new array by transforming every element.

const numbers = [1, 2, 3];

const result = numbers.map(n => n * 2);

console.log(result);

Output:

[2, 4, 6]

filter()

Creates a new array containing elements that satisfy a condition.

const numbers = [1, 2, 3, 4];

const result = numbers.filter(n => n % 2 === 0);

console.log(result);

Output:

[2, 4]

forEach()

Executes a function for each element.

numbers.forEach(n => console.log(n));

Interview Tip: Use map() when you need a transformed array, filter() when you need selected elements, and forEach() when you simply want to perform an action for each element.


20. What is reduce()?

Answer: reduce() is used to reduce an array to a single value.

const numbers = [10, 20, 30];

const total = numbers.reduce((sum, value) => {
    return sum + value;
}, 0);

console.log(total);

Output:

60

21. What is find()?

Answer: find() returns the first element that satisfies a condition.

const users = [
    { id: 1, name: "John" },
    { id: 2, name: "David" },
    { id: 3, name: "Alex" }
];

const user = users.find(user => user.id === 2);

console.log(user);

Output:

{ id: 2, name: "David" }

22. What is the difference between find() and filter()?

find() returns the first matching element.

numbers.find(n => n > 10);

filter() returns all matching elements.

numbers.filter(n => n > 10);

23. What are some() and every()?

some() checks whether at least one element satisfies a condition.

const numbers = [1, 3, 5, 8];

console.log(
    numbers.some(n => n % 2 === 0)
);

Output:

true

every() checks whether all elements satisfy a condition.

const numbers = [2, 4, 6, 8];

console.log(
    numbers.every(n => n % 2 === 0)
);

Output:

true

JavaScript Objects

24. What is an object?

Answer: An object stores data in key-value pairs.

const user = {
    name: "John",
    age: 30,
    city: "Delhi"
};

console.log(user.name);

25. How can you access object properties?

There are several ways to access object properties.

user.name;
user["name"];

You can also use a variable:

const key = "name";

console.log(user[key]);

26. What is object destructuring?

Answer: Destructuring allows values to be extracted from objects into variables.

const user = {
    name: "John",
    age: 30
};

const { name, age } = user;

console.log(name);
console.log(age);

27. What is array destructuring?

const numbers = [10, 20];

const [a, b] = numbers;

console.log(a);
console.log(b);

28. What is the spread operator?

Answer: The spread operator ... expands the elements of an iterable or properties of an object.

const arr1 = [1, 2, 3];

const arr2 = [...arr1, 4, 5];

console.log(arr2);

Output:

[1, 2, 3, 4, 5]

Object example:

const user = {
    name: "John",
    age: 30
};

const updatedUser = {
    ...user,
    city: "Delhi"
};

29. What is the rest operator?

Answer: The rest operator collects multiple values into a single array.

function add(...numbers) {

    return numbers.reduce(
        (sum, n) => sum + n,
        0
    );
}

console.log(add(10, 20, 30));

Output:

60

JavaScript Closures

30. What is a closure?

Answer: A closure occurs when an inner function remembers and can access variables from its outer function even after the outer function has finished executing.

function outer() {

    let count = 0;

    return function() {
        count++;
        return count;
    };
}

const counter = outer();

console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

Closures are commonly used for:

  • Data privacy
  • Counters
  • Callbacks
  • Function factories
  • Event handlers

JavaScript this Keyword

31. What is this in JavaScript?

Answer: this is a context-dependent value. Its value depends on how a function is called.

const user = {

    name: "John",

    greet() {
        console.log(this.name);
    }

};

user.greet();

In this example, this refers to the user object.


32. What are call(), apply(), and bind()?

These methods are used to control the value of this.

call()

function greet(city) {
    console.log(this.name, city);
}

const user = {
    name: "John"
};

greet.call(user, "Delhi");

apply()

apply() accepts arguments as an array.

greet.apply(user, ["Delhi"]);

bind()

bind() returns a new function.

const newFunction = greet.bind(user, "Delhi");

newFunction();

Asynchronous JavaScript

33. What is synchronous execution?

Answer: Synchronous code executes one operation at a time in sequence.

console.log("A");
console.log("B");
console.log("C");

Output:

A
B
C

34. What is asynchronous JavaScript?

Answer: Asynchronous JavaScript allows certain operations to complete later without blocking the execution of other code.

Common examples include:

  • API requests
  • Timers
  • File operations
  • Database operations
  • Browser events

35. What is a callback function?

Answer: A callback is a function passed to another function to be executed later.

function greet(name, callback) {

    console.log("Hello " + name);

    callback();
}

greet("John", function() {
    console.log("Welcome!");
});

JavaScript Promises

36. What is a Promise?

Answer: A Promise represents the eventual completion or failure of an asynchronous operation.

A Promise has three states:

  1. Pending
  2. Fulfilled
  3. Rejected
const promise = new Promise((resolve, reject) => {

    const success = true;

    if (success) {
        resolve("Operation successful");
    } else {
        reject("Operation failed");
    }

});

37. How do you consume a Promise?

promise
    .then(result => {
        console.log(result);
    })
    .catch(error => {
        console.log(error);
    })
    .finally(() => {
        console.log("Completed");
    });

38. What is async/await?

Answer: async/await provides a cleaner syntax for working with Promises.

async function getData() {

    const response =
        await fetch("https://example.com/api/users");

    const data = await response.json();

    console.log(data);
}

await pauses execution of the current async function until the awaited Promise settles. It does not block the entire JavaScript runtime.


39. What is Promise.all()?

Answer: Promise.all() waits for multiple Promises and fulfills when all of them fulfill.

const p1 = Promise.resolve("A");
const p2 = Promise.resolve("B");
const p3 = Promise.resolve("C");

const result = await Promise.all([
    p1,
    p2,
    p3
]);

console.log(result);

Output:

["A", "B", "C"]

If one Promise rejects, Promise.all() rejects.


40. What is Promise.allSettled()?

Answer: Promise.allSettled() waits for all Promises to settle, regardless of whether they fulfill or reject.

const results = await Promise.allSettled([
    Promise.resolve("Success"),
    Promise.reject("Failed")
]);

console.log(results);

41. What is Promise.race()?

Answer: Promise.race() returns the result of the first Promise that settles.

const p1 = new Promise(resolve => {
    setTimeout(() => resolve("First"), 1000);
});

const p2 = new Promise(resolve => {
    setTimeout(() => resolve("Second"), 500);
});

const result = await Promise.race([p1, p2]);

console.log(result);

Output:

Second

42. What is Promise.any()?

Answer: Promise.any() fulfills when the first Promise fulfills.

const result = await Promise.any([
    Promise.reject("Error 1"),
    Promise.resolve("Success"),
    Promise.resolve("Success 2")
]);

console.log(result);

Output:

Success

JavaScript Event Loop

43. What is the event loop?

Answer: The event loop is a mechanism that allows JavaScript to handle asynchronous operations while JavaScript execution itself is single-threaded.

A simplified model is:

Call Stack
    ↓
Host APIs
    ↓
Task / Microtask Queues
    ↓
Event Loop
    ↓
Call Stack

44. What are microtasks and tasks?

Answer:

Microtasks include Promise reactions and queueMicrotask().

Tasks include operations such as timer callbacks and certain host events.

console.log("1");

setTimeout(() => {
    console.log("2");
}, 0);

Promise.resolve().then(() => {
    console.log("3");
});

console.log("4");

Output:

1
4
3
2

The synchronous code executes first, followed by the Promise microtask, and then the timer task.


JavaScript Error Handling

45. What is exception handling?

Answer: JavaScript provides try, catch, finally, and throw for handling errors.

try {

    let result = 10 / 0;

    console.log(result);

} catch (error) {

    console.log(error.message);

} finally {

    console.log("Execution completed");
}

46. What is throw?

Answer: The throw statement allows developers to manually generate an error.

function validateAge(age) {

    if (age < 18) {
        throw new Error("Age must be 18 or above");
    }

    return true;
}

JavaScript Object-Oriented Programming

47. Does JavaScript support OOP?

Answer: Yes. JavaScript supports object-oriented programming using prototypes and modern class syntax.

class Employee {

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

    display() {
        console.log(this.name, this.salary);
    }
}

const emp = new Employee("John", 50000);

emp.display();

48. What is inheritance?

Answer: Inheritance allows one class to reuse properties and methods from another class.

class Animal {

    eat() {
        console.log("Eating");
    }
}

class Dog extends Animal {

    bark() {
        console.log("Barking");
    }
}

const dog = new Dog();

dog.eat();
dog.bark();

49. What is a prototype?

Answer: JavaScript uses a prototype-based inheritance model.

Objects can inherit properties and methods through the prototype chain.

const user = {
    name: "John"
};

console.log(Object.getPrototypeOf(user));

Advanced JavaScript Interview Questions

50. What is shallow copy and deep copy?

A shallow copy copies the outer structure, but nested objects can still be shared.

const user = {
    name: "John",
    address: {
        city: "Delhi"
    }
};

const copy = {
    ...user
};

copy.address still refers to the same nested object.

For many data structures, a modern deep-copy approach is:

const deepCopy = structuredClone(user);

51. What is optional chaining?

Answer: Optional chaining ?. allows safe access to potentially missing properties.

const user = {};

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

The result is undefined instead of throwing an error.


52. What is the nullish coalescing operator?

Answer: The ?? operator provides a default value when the left-hand side is null or undefined.

const username = null;

console.log(username ?? "Guest");

Output:

Guest

53. What is the difference between || and ???

console.log(0 || 100);  // 100
console.log(0 ?? 100);  // 0

console.log(false || true); // true
console.log(false ?? true); // false

|| checks for falsy values, while ?? checks specifically for null and undefined.


54. What is an IIFE?

Answer: IIFE stands for Immediately Invoked Function Expression.

(function() {
    console.log("Executed immediately");
})();

Arrow function syntax can also be used:

(() => {
    console.log("Hello");
})();

55. What is a JavaScript module?

Answer: A module is a JavaScript file that can export functionality for use in another file.

Export:

export function add(a, b) {
    return a + b;
}

Import:

import { add } from "./calculator.js";

console.log(add(10, 20));

56. What is the difference between named export and default export?

Named export:

export function login() {
    console.log("Login");
}

Import:

import { login } from "./auth.js";

Default export:

export default function login() {
    console.log("Login");
}

Import:

import login from "./auth.js";

57. What is strict mode?

Answer: Strict mode enables a stricter set of JavaScript rules.

"use strict";

x = 10;

This produces an error because x was not declared.

JavaScript modules are automatically executed in strict mode.


58. What is type coercion?

Answer: Type coercion is the conversion of one data type into another.

console.log("5" + 2);

Output:

52

Here JavaScript converts the number into a string.

Another example:

console.log("5" - 2);

Output:

3

Here JavaScript converts the string into a number.


59. What are truthy and falsy values?

Answer: A value is truthy if JavaScript treats it as true in a Boolean context.

Common falsy values include:

false
0
-0
0n
""
null
undefined
NaN

Most other values are truthy, including:

[]
{}
"false"
"0"

60. What is short-circuit evaluation?

Answer: JavaScript logical operators can stop evaluating once the result is known.

const name = "";

const result = name || "Guest";

console.log(result);

Output:

Guest

JavaScript Map and Set

61. What is Map in JavaScript?

Answer: Map is a collection of key-value pairs.

const users = new Map();

users.set(1, "John");
users.set(2, "David");

console.log(users.get(1));

Output:

John

Common methods include:

  • set()
  • get()
  • has()
  • delete()
  • clear()

62. What is Set in JavaScript?

Answer: A Set is a collection of unique values.

const numbers = new Set([1, 2, 2, 3, 3]);

console.log(numbers);

A common use case is removing duplicates:

const numbers = [1, 2, 2, 3, 3];

const unique = [...new Set(numbers)];

console.log(unique);

Performance and Advanced Concepts

63. What is garbage collection?

Answer: Garbage collection is the process through which the JavaScript runtime identifies objects that are no longer reachable and reclaims their memory.

let user = {
    name: "John"
};

user = null;

If no other reference points to the original object, it can become eligible for garbage collection.


64. What is a memory leak?

Answer: A memory leak occurs when memory that is no longer needed remains reachable and therefore cannot be reclaimed.

Common causes include:

  • Unremoved event listeners
  • Long-lived global variables
  • Forgotten timers
  • Unnecessary object references
  • Unbounded caches

65. What is currying?

Answer: Currying converts a function with multiple arguments into a sequence of functions that each accept one argument.

function add(a) {

    return function(b) {
        return a + b;
    };

}

console.log(add(10)(20));

Output:

30

66. What is function composition?

Answer: Function composition means combining multiple functions so that the output of one function becomes the input of another.

const double = x => x * 2;

const square = x => x * x;

const result = square(double(5));

console.log(result);

Output:

100

67. What is memoization?

Answer: Memoization is an optimization technique where function results are cached so that repeated calculations can be avoided.

function memoize(fn) {

    const cache = new Map();

    return function(value) {

        if (cache.has(value)) {
            return cache.get(value);
        }

        const result = fn(value);

        cache.set(value, result);

        return result;
    };
}

68. What is debouncing?

Answer: Debouncing delays function execution until a specified period has passed without another call.

It is commonly used for:

  • Search boxes
  • Auto-save
  • Input validation
  • Window resize events
function debounce(fn, delay) {

    let timer;

    return function(...args) {

        clearTimeout(timer);

        timer = setTimeout(() => {
            fn(...args);
        }, delay);
    };
}

69. What is throttling?

Answer: Throttling limits how frequently a function can execute.

It is commonly used for:

  • Scroll events
  • Mouse movement
  • Window resize
  • Continuous browser events

Important difference:

  • Debouncing: Executes after activity stops.
  • Throttling: Executes at controlled intervals during activity.

DOM and Event Handling

70. What is the DOM?

Answer: DOM stands for Document Object Model.

The browser represents an HTML document as a tree-like object structure that JavaScript can interact with.

document.getElementById("username");

71. What is event bubbling?

Answer: Event bubbling means an event triggered on a child element can propagate upward through its parent elements.

button
   ↓
div
   ↓
body
   ↓
document

72. What is event capturing?

Answer: Event capturing is the propagation of an event from an outer ancestor toward the target element.

document
   ↓
body
   ↓
div
   ↓
button

73. What is event delegation?

Answer: Event delegation means attaching an event listener to a parent instead of adding separate listeners to every child element.

document
    .getElementById("products")
    .addEventListener("click", event => {

        if (event.target.matches(".product")) {
            console.log(event.target.textContent);
        }

    });

Event delegation is useful when:

  • There are many child elements.
  • Elements are dynamically created.
  • You want fewer event listeners.

74. What is preventDefault()?

Answer: preventDefault() prevents the browser’s default action for an event.

document
    .querySelector("form")
    .addEventListener("submit", event => {

        event.preventDefault();

        console.log("Default submission prevented");

    });

75. What is stopPropagation()?

Answer: stopPropagation() prevents an event from continuing to propagate through the DOM.

button.addEventListener("click", event => {
    event.stopPropagation();
});

Difference:

  • preventDefault() prevents the browser’s default action.
  • stopPropagation() prevents event propagation.

Scenario-Based JavaScript Interview Questions

76. How do you remove duplicate values from an array?

const numbers = [1, 2, 2, 3, 3, 4];

const unique = [...new Set(numbers)];

console.log(unique);

Output:

[1, 2, 3, 4]

77. How do you find the largest number in an array?

const numbers = [10, 50, 20, 90, 30];

const largest = Math.max(...numbers);

console.log(largest);

Output:

90

78. How do you reverse a string?

const text = "JavaScript";

const reversed = text
    .split("")
    .reverse()
    .join("");

console.log(reversed);

79. How do you check whether a string is a palindrome?

function isPalindrome(text) {

    const reversed = text
        .split("")
        .reverse()
        .join("");

    return text === reversed;
}

console.log(isPalindrome("madam"));

Output:

true

80. How do you count occurrences of elements in an array?

const fruits = [
    "apple",
    "banana",
    "apple",
    "orange",
    "banana"
];

const count = fruits.reduce((result, fruit) => {

    result[fruit] = (result[fruit] || 0) + 1;

    return result;

}, {});

console.log(count);

Output:

{
    apple: 2,
    banana: 2,
    orange: 1
}

JavaScript Output-Based Interview Questions

81. What is the output?

console.log(a);

var a = 10;

Answer:

undefined

82. What is the output?

console.log(a);

let a = 10;

Answer:

ReferenceError

The variable is in the Temporal Dead Zone before initialization.


83. What is the output?

console.log("A");

setTimeout(() => {
    console.log("B");
}, 0);

console.log("C");

Answer:

A
C
B

The timer callback executes after the synchronous code.


84. What is the output?

console.log(1);

setTimeout(() => {
    console.log(2);
}, 0);

Promise.resolve().then(() => {
    console.log(3);
});

console.log(4);

Answer:

1
4
3
2

The Promise callback is a microtask, which is processed before the timer task.


85. What is the output?

const user = {
    name: "John"
};

const copy = user;

copy.name = "David";

console.log(user.name);

Answer:

David

Both variables reference the same object.


86. What is the output?

const user = {
    name: "John"
};

const copy = {
    ...user
};

copy.name = "David";

console.log(user.name);

Answer:

John

The spread operator creates a new outer object. However, nested objects are still shared because the copy is shallow.


JavaScript Interview Questions for QA and SDET Engineers

If you are preparing for a QA Automation, SDET, Selenium, or Playwright interview, you should pay special attention to the following JavaScript concepts:

  • Variables and data types
  • Functions
  • Scope
  • Hoisting
  • Closures
  • this
  • Arrow functions
  • Callbacks
  • Promises
  • async/await
  • Promise.all()
  • Event loop
  • Microtasks and tasks
  • Arrays and array methods
  • Objects
  • Destructuring
  • Spread and rest operators
  • Exception handling
  • Classes and inheritance
  • Modules
  • JSON manipulation
  • API handling

Top 20 JavaScript Interview Questions for Quick Revision

# Question Key Concept
1What is JavaScript?Fundamentals
2What is the difference between var, let, and const?Variables
3What is hoisting?Execution
4What is TDZ?let/const
5What is the difference between == and ===?Equality
6What is a closure?Scope
7What is this?Context
8Arrow function vs normal function?Functions
9What is a Promise?Asynchronous JavaScript
10What is async/await?Asynchronous JavaScript
11What is Promise.all()?Concurrency
12What is the event loop?Runtime
13Microtask vs task?Async execution
14map() vs filter()?Arrays
15find() vs filter()?Arrays
16Spread vs rest?ES6
17What is destructuring?ES6
18What is prototype inheritance?OOP
19What are JavaScript modules?Code organization
20What is event delegation?DOM

JavaScript Interview Preparation Strategy

Beginner Level

Start with:

  • Variables
  • Data types
  • Operators
  • Conditions
  • Loops
  • Functions
  • Arrays
  • Objects

Intermediate Level

Next, learn:

  • Scope
  • Hoisting
  • Closures
  • Destructuring
  • Spread/rest operators
  • Array methods
  • Exception handling
  • Promises
  • async/await
  • Classes
  • Modules

Advanced Level

Finally, focus on:

  • Event loop
  • Microtasks and tasks
  • Prototype chain
  • this
  • call(), apply(), and bind()
  • Currying
  • Memoization
  • Debouncing
  • Throttling
  • Event delegation
  • Memory management
  • Promise concurrency methods

Final Interview Tip

For JavaScript interviews, do not focus only on memorizing definitions. Interviewers frequently provide a short code snippet and ask:

“What will be the output and why?”

Therefore, practice code execution, scope, closures, this, Promises, async/await, event-loop behavior, arrays, objects, and asynchronous programming.

For QA Automation and SDET roles, combine JavaScript knowledge with practical scenarios involving API responses, JSON manipulation, Playwright, asynchronous test execution, Promise handling, and browser automation.

Conclusion

JavaScript is an essential skill for modern automation engineers, developers, and SDETs. A strong understanding of JavaScript fundamentals makes it much easier to learn technologies such as Playwright, Cypress, Node.js, React, and API automation.

Start with the fundamentals, practice small coding problems every day, and gradually move toward advanced concepts such as closures, Promises, event loops, prototypes, and asynchronous programming.

Keep practicing and focus on understanding why the code works, not just memorizing the answer.

TypeScript Interview Questions and Answers for Beginners

Are you preparing for a TypeScript interview as a beginner or fresher? This guide covers the most commonly asked TypeScript interview questions and answers, starting from basic concepts and gradually moving toward practical and coding-based questions.

TypeScript is widely used in modern web development, Node.js applications, Angular, React projects, and test automation frameworks such as Playwright with TypeScript.

This article contains 60 TypeScript interview questions and answers covering variables, data types, interfaces, classes, generics, functions, type guards, TypeScript configuration, and practical coding questions.

Table of Contents

1. What is TypeScript?

Answer: TypeScript is a strongly typed programming language developed by Microsoft. It is a superset of JavaScript, which means valid JavaScript code can generally be used in a TypeScript project.

TypeScript adds several features to JavaScript, including:

  • Static typing
  • Interfaces
  • Generics
  • Enums
  • Access modifiers
  • Better IDE support
  • Compile-time type checking

TypeScript code is transformed into JavaScript before it runs in a browser or Node.js environment.

2. What is the difference between JavaScript and TypeScript?

JavaScript TypeScript
Dynamically typed Supports static typing
Uses .js files Uses .ts files
Many type errors are discovered at runtime Many type errors can be detected during development
Does not require TypeScript compilation Usually transpiled to JavaScript
Less type information Provides rich type information

3. Why do we use TypeScript?

TypeScript makes JavaScript development safer and easier to maintain, particularly in large applications.

Major advantages include:

  • Early error detection
  • Better code completion
  • Improved refactoring
  • Static type checking
  • Better maintainability
  • Support for object-oriented programming
  • Improved developer productivity

4. Is TypeScript a programming language?

Answer: Yes. TypeScript is a programming language developed and maintained by Microsoft. It extends JavaScript by adding static typing and other development features.

5. What is the TypeScript compiler?

The TypeScript compiler converts TypeScript source code into JavaScript.

The compiler command is commonly called tsc.

tsc app.ts

For example:

let message: string = "Hello TypeScript";

console.log(message);

The TypeScript compiler generates JavaScript that can execute in a JavaScript environment.

6. How do you install TypeScript?

First install Node.js and npm. Then install TypeScript using:

npm install -g typescript

Verify the installation:

tsc --version

7. What is a .ts file?

A .ts file is a TypeScript source file.

Examples:

app.ts
login.ts
user.ts
employee.ts

TypeScript code is normally written in .ts files and then compiled or transpiled into JavaScript.

8. What are the basic data types in TypeScript?

Some commonly used TypeScript types are:

string
number
boolean
array
tuple
enum
any
unknown
void
null
undefined
never
object

Example:

let name: string = "Deepesh";
let age: number = 35;
let isTrainer: boolean = true;

9. What is type annotation?

Type annotation means explicitly specifying the type of a variable.

let username: string = "admin";
let age: number = 30;
let active: boolean = true;

Here, string, number, and boolean are type annotations.

10. What is type inference?

Type inference means TypeScript automatically determines the type of a variable based on its assigned value.

let name = "Deepesh";
let age = 30;

TypeScript infers:

name: string
age: number

11. What is the any type?

The any type disables most TypeScript type checking for a value.

let data: any = "Hello";

data = 100;
data = true;
data = { name: "John" };

Although any can be useful when migrating JavaScript code or dealing with genuinely untyped APIs, excessive use of it removes many of the benefits of TypeScript.

12. What is the unknown type?

The unknown type is used when the type of a value is not known.

let value: unknown = "Hello";

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

Unlike any, TypeScript requires you to perform an appropriate type check before using an unknown value in type-specific operations.

13. What is an array in TypeScript?

An array stores multiple values.

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

let names: string[] = ["John", "David", "Alex"];

Another syntax is:

let numbers: Array<number> = [10, 20, 30];

14. What is a tuple?

A tuple defines a fixed structure for an array, including the expected type and position of its elements.

let employee: [number, string] = [101, "John"];

In this example:

  • The first element must be a number.
  • The second element must be a string.

15. What is an interface?

An interface defines the structure or contract of an object.

interface Employee {
    id: number;
    name: string;
    salary: number;
}

const employee: Employee = {
    id: 101,
    name: "John",
    salary: 50000
};

Interfaces are commonly used to define the expected structure of objects and contracts implemented by classes.

16. What is an optional property?

A property followed by ? is optional.

interface Employee {
    id: number;
    name: string;
    email?: string;
}

Therefore, the following object is valid:

const employee: Employee = {
    id: 101,
    name: "John"
};

17. What is a type alias?

A type alias allows you to create a custom name for a type.

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

const emp: Employee = {
    id: 101,
    name: "John"
};

Type aliases can also represent unions and other type expressions.

type ID = string | number;

let userId: ID = 101;
userId = "EMP101";

18. What is the difference between interface and type?

Both interfaces and type aliases can describe object structures.

interface User {
    name: string;
}

Equivalent object type:

type User = {
    name: string;
};

Interfaces are especially useful for object/class contracts and can be extended. Type aliases are more flexible for unions, intersections, tuples, and other type expressions.

19. What is a union type?

A union type allows a variable to have more than one possible type.

let id: string | number;

id = 101;
id = "EMP101";

The | symbol represents a union.

20. What is an intersection type?

An intersection type combines multiple types into a single type.

type Person = {
    name: string;
};

type Employee = {
    salary: number;
};

type EmployeeDetails = Person & Employee;

const emp: EmployeeDetails = {
    name: "John",
    salary: 50000
};

The & operator represents an intersection type.

21. What is an enum?

An enum allows you to define a collection of named constants.

enum Status {
    Pending,
    Approved,
    Rejected
}

let currentStatus: Status = Status.Approved;

Enums can be useful when working with a predefined set of values.

22. How do you define a function in TypeScript?

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

console.log(add(10, 20));

The parameter types are defined using : number, and the return type is also specified as number.

23. What is an optional parameter?

An optional parameter is defined using ?.

function greet(name: string, age?: number) {
    console.log(name);
}

greet("John");
greet("John", 30);

The age parameter is optional.

24. What are default parameters?

A default parameter receives a default value when no value is provided.

function greet(name: string = "Guest") {
    console.log(`Hello ${name}`);
}

greet();

Output:

Hello Guest

25. What is a class in TypeScript?

A class is a blueprint for creating objects.

class Employee {
    name: string;

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

    display() {
        console.log(this.name);
    }
}

const emp = new Employee("John");

emp.display();

TypeScript classes support object-oriented programming concepts such as encapsulation, inheritance, abstraction, and polymorphism.

26. What are access modifiers in TypeScript?

TypeScript supports the following common access modifiers:

  • public – accessible from anywhere
  • private – accessible only inside the declaring class
  • protected – accessible inside the class and subclasses
class Employee {
    public name: string;
    private salary: number;
    protected department: string;

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

27. What is inheritance?

Inheritance allows one class to reuse members of another class.

class Animal {
    eat() {
        console.log("Eating");
    }
}

class Dog extends Animal {
    bark() {
        console.log("Barking");
    }
}

const dog = new Dog();

dog.eat();
dog.bark();

Here, Dog inherits from Animal.

28. What are generics in TypeScript?

Generics allow you to create reusable code that works with different types while preserving type safety.

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

let numberValue = identity<number>(100);
let stringValue = identity<string>("Hello");

Here, T acts as a type parameter.

29. What is type assertion?

Type assertion tells TypeScript that you know more about the type of a value than the compiler currently does.

let value: unknown = "Hello";

let message = value as string;

console.log(message.toUpperCase());

The as syntax is commonly used for type assertions.

30. What is void?

The void type is commonly used for functions that do not return a meaningful value.

function printMessage(): void {
    console.log("Hello");
}

31. What is null and undefined?

undefined generally means a value has not been assigned.

let name: string | undefined;

console.log(name);

null represents an intentional absence of a value.

let user: string | null = null;

32. What is never in TypeScript?

never represents a value that never occurs. It is commonly used for functions that never successfully complete.

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

33. What is strict mode?

Strict mode enables a collection of stronger type-checking rules.

{
    "compilerOptions": {
        "strict": true
    }
}

Strict checking helps identify potential problems during development.

34. What is tsconfig.json?

tsconfig.json is the configuration file for a TypeScript project.

Example:

{
    "compilerOptions": {
        "target": "ES2022",
        "module": "commonjs",
        "strict": true,
        "outDir": "./dist"
    },
    "include": [
        "src/**/*.ts"
    ]
}

35. What is the target option?

The target option specifies the JavaScript version that TypeScript should generate.

{
    "compilerOptions": {
        "target": "ES2022"
    }
}

36. What is the module option?

The module option controls the module system used by the generated JavaScript.

{
    "compilerOptions": {
        "module": "commonjs"
    }
}

Modern projects may use module settings such as ESNext depending on the runtime and build system.

37. What is type narrowing?

Type narrowing means reducing a broad type to a more specific type based on a condition.

function printValue(value: string | number) {

    if (typeof value === "string") {
        console.log(value.toUpperCase());
    } else {
        console.log(value.toFixed(2));
    }
}

38. What is typeof used for?

The typeof operator can be used to check the runtime type of a value and can also help TypeScript narrow a union type.

function display(value: string | number) {

    if (typeof value === "string") {
        console.log("String:", value);
    } else {
        console.log("Number:", value);
    }
}

39. What is a type guard?

A type guard is a condition or function that helps TypeScript determine the specific type of a value.

function isString(value: unknown): value is string {
    return typeof value === "string";
}

const data: unknown = "Hello";

if (isString(data)) {
    console.log(data.toUpperCase());
}

40. What is instanceof?

The instanceof operator checks whether an object is an instance of a particular class or constructor.

class Employee {
}

const emp = new Employee();

console.log(emp instanceof Employee);

Output:

true

41. What is optional chaining?

Optional chaining ?. allows you to safely access properties that may not exist.

interface User {
    name: string;
    address?: {
        city: string;
    };
}

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

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

42. What is the nullish coalescing operator?

The ?? operator provides a fallback value when the left-hand side is null or undefined.

let username: string | undefined;

let result = username ?? "Guest";

console.log(result);

Output:

Guest

43. What is readonly?

The readonly modifier prevents a property from being assigned a new value after initialization.

interface Employee {
    readonly id: number;
    name: string;
}

const employee: Employee = {
    id: 101,
    name: "John"
};

employee.name = "David";

// Error:
// employee.id = 102;

44. What is the difference between const and readonly?

const applies to a variable binding, while readonly applies to a property or type member.

const employee = {
    name: "John"
};

employee.name = "David";

The object reference cannot be reassigned, but its property can still be changed.

With readonly:

interface Employee {
    readonly id: number;
}

The id property cannot be reassigned after initialization.

45. Can an interface extend another interface?

Yes. An interface can extend another interface.

interface Person {
    name: string;
}

interface Employee extends Person {
    employeeId: number;
}

const emp: Employee = {
    name: "John",
    employeeId: 101
};

46. Can a class implement an interface?

Yes. A class can implement one or more interfaces.

interface Employee {
    name: string;
    display(): void;
}

class Manager implements Employee {

    name: string;

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

    display(): void {
        console.log(this.name);
    }
}

47. What is the difference between extends and implements?

extends is generally used for inheritance.

class Dog extends Animal {
}

implements is used when a class agrees to follow an interface contract.

class Employee implements Person {
}

48. Can an interface have methods?

Yes. An interface can define method signatures.

interface Employee {
    name: string;
    display(): void;
}

A class implementing the interface must provide the required method.

49. What is function overloading?

Function overloading allows you to define multiple function signatures for different valid calling patterns.

function add(a: number, b: number): number;
function add(a: string, b: string): string;

function add(a: number | string, b: number | string) {
    return (a as any) + (b as any);
}

console.log(add(10, 20));
console.log(add("Hello ", "World"));

50. What is the purpose of generics?

Generics allow developers to create reusable, type-safe functions, classes, and interfaces.

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

const numberValue = getValue(100);
const stringValue = getValue("Hello");
const booleanValue = getValue(true);

TypeScript Coding Interview Questions

51. Write a function to calculate the sum of two numbers.

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

console.log(sum(10, 20));

52. Create an interface for a Student.

interface Student {
    id: number;
    name: string;
    age: number;
}

const student: Student = {
    id: 1,
    name: "John",
    age: 22
};

53. Create an interface with an optional property.

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

54. Create a function that accepts string or number.

function display(value: string | number): void {
    console.log(value);
}

display("Hello");
display(100);

55. Create a generic function.

function getData<T>(data: T): T {
    return data;
}

console.log(getData<number>(100));
console.log(getData<string>("TypeScript"));

Practical TypeScript Interview Questions

56. What will be the output?

let value: string | number = "100";

if (typeof value === "string") {
    console.log("String");
} else {
    console.log("Number");
}

Answer:

String

Because the value is initially assigned a string.

57. Is this code valid?

let age: number = 25;

age = "25";

Answer: No.

TypeScript reports a type error because age was declared as a number.

58. Is this valid TypeScript?

let data: any = 100;

data = "Hello";
data = true;

Answer: Yes.

The any type allows the variable to contain values of different types. However, it should be used carefully.

59. What is the output?

let username: string | undefined;

console.log(username ?? "Guest");

Answer:

Guest

Because username is undefined.

60. What is the main advantage of generics?

Generics allow you to write reusable code while preserving type information.

function getData<T>(data: T): T {
    return data;
}

const numberData = getData(100);
const stringData = getData("Hello");

TypeScript can infer the appropriate type for T.

Important TypeScript Interview Topics for Beginners

If you have limited preparation time, focus on the following topics:

  1. What is TypeScript?
  2. TypeScript vs JavaScript
  3. Advantages of TypeScript
  4. Type annotations
  5. Type inference
  6. any vs unknown
  7. Union types
  8. Intersection types
  9. Interfaces
  10. Type aliases
  11. Interface vs type
  12. Arrays
  13. Tuples
  14. Enums
  15. Functions
  16. Optional parameters
  17. Classes
  18. Access modifiers
  19. Inheritance
  20. extends vs implements
  21. Generics
  22. Type assertions
  23. Type guards
  24. null and undefined
  25. never
  26. void
  27. readonly
  28. Optional chaining
  29. Nullish coalescing
  30. tsconfig.json

TypeScript Interview Preparation Tips

For a beginner TypeScript interview, don’t just memorize definitions. Try to understand each concept with a small coding example.

  • Understand JavaScript fundamentals first.
  • Practice TypeScript data types.
  • Write interfaces for real-world objects.
  • Practice classes and inheritance.
  • Understand union and intersection types.
  • Practice generics with simple examples.
  • Learn type narrowing and type guards.
  • Understand any, unknown, never, and void.
  • Practice TypeScript coding questions.
  • Learn how tsconfig.json works.

TypeScript for Playwright Automation

TypeScript is particularly useful for test automation because it provides type safety, better autocomplete, and improved maintainability.

For example, a Playwright test can be written as:

import { test, expect } from '@playwright/test';

test('Login test', async ({ page }) => {

    await page.goto('https://example.com');

    await expect(page).toHaveTitle(/Example/);

});

Understanding TypeScript fundamentals is therefore highly recommended if you are preparing for a Playwright with TypeScript or SDET interview.

Frequently Asked Questions

Is TypeScript difficult for beginners?

No. If you already know JavaScript fundamentals, TypeScript can be learned progressively by adding types and understanding interfaces, classes, generics, and other features.

Do I need to learn JavaScript before TypeScript?

Yes, it is highly recommended. TypeScript builds on JavaScript, so understanding variables, functions, arrays, objects, classes, promises, and asynchronous programming will make TypeScript much easier to learn.

Is TypeScript used in automation testing?

Yes. TypeScript is widely used with modern automation frameworks, including Playwright. It provides type checking, IDE support, and improved maintainability for automation frameworks.

What should I learn after TypeScript basics?

After learning the fundamentals, you should study advanced TypeScript concepts such as generics, utility types, mapped types, conditional types, decorators, modules, namespaces, advanced type narrowing, and TypeScript configuration.

Conclusion

TypeScript is an important skill for modern web development and test automation. Beginners should first understand the fundamentals such as data types, type annotations, type inference, interfaces, type aliases, functions, classes, union types, and generics.

Once these concepts are clear, you can move toward advanced TypeScript concepts and frameworks such as Playwright, Angular, React, Node.js, and automation framework development.

Practicing the questions and coding examples in this article will help you build a strong foundation for TypeScript interviews.

Next Recommended Topic: TypeScript Interview Questions and Answers for Intermediate and Advanced Developers.

Scenario-Based JavaScript Array Programs

Below is a collection of real-world, scenario-based JavaScript array programs designed for QA/SDET training, coding practice, and interviews. They progress from beginner to advanced.


1. Find the Highest Product Price

Scenario

An e-commerce application stores product prices. Find the most expensive product.

const prices = [1200, 4500, 2300, 8900, 1500];

let maxPrice = prices[0];

for (let price of prices) {
    if (price > maxPrice) {
        maxPrice = price;
    }
}

console.log("Highest Price:", maxPrice);

Expected Output:

Highest Price: 8900

2. Find the Lowest Product Price

const prices = [1200, 4500, 2300, 8900, 1500];

let minPrice = prices[0];

for (let price of prices) {
    if (price < minPrice) {
        minPrice = price;
    }
}

console.log("Lowest Price:", minPrice);

3. Calculate Total Shopping Cart Amount

Scenario

A shopping cart contains multiple product prices. Calculate the total.

const cart = [499, 1299, 799, 2499];

let total = 0;

for (let price of cart) {
    total += price;
}

console.log("Cart Total:", total);

Output:

Cart Total: 5096

4. Find Products Above ₹1000

const prices = [500, 1200, 2500, 700, 1800, 450];

const result = [];

for (let price of prices) {
    if (price > 1000) {
        result.push(price);
    }
}

console.log(result);

Output:

[1200, 2500, 1800]

5. Remove Duplicate Product IDs

Scenario

During automation testing, duplicate product IDs are received from an API.

const productIds = [101, 102, 103, 101, 104, 102, 105];

const uniqueIds = [];

for (let id of productIds) {
    if (!uniqueIds.includes(id)) {
        uniqueIds.push(id);
    }
}

console.log(uniqueIds);

Output:

[101, 102, 103, 104, 105]

6. Find Duplicate Values

const ids = [101, 102, 103, 101, 104, 102, 105];

const duplicates = [];

for (let i = 0; i < ids.length; i++) {
    for (let j = i + 1; j < ids.length; j++) {

        if (ids[i] === ids[j] && !duplicates.includes(ids[i])) {
            duplicates.push(ids[i]);
        }
    }
}

console.log("Duplicates:", duplicates);

Output:

Duplicates: [101, 102]

7. Find Second Highest Salary

Scenario

An HR application stores employee salaries. Find the second-highest salary without sorting.

const salaries = [45000, 75000, 55000, 90000, 65000];

let highest = -Infinity;
let secondHighest = -Infinity;

for (let salary of salaries) {

    if (salary > highest) {
        secondHighest = highest;
        highest = salary;
    } 
    else if (salary > secondHighest && salary !== highest) {
        secondHighest = salary;
    }
}

console.log("Highest:", highest);
console.log("Second Highest:", secondHighest);

Output:

Highest: 90000
Second Highest: 75000

8. Count Passed and Failed Students

Scenario

A training institute stores student marks. Determine how many students passed.

const marks = [85, 45, 72, 30, 90, 55, 28];

let passed = 0;
let failed = 0;

for (let mark of marks) {

    if (mark >= 40) {
        passed++;
    } else {
        failed++;
    }
}

console.log("Passed:", passed);
console.log("Failed:", failed);

9. Calculate Average Marks

const marks = [80, 75, 90, 65, 85];

let total = 0;

for (let mark of marks) {
    total += mark;
}

const average = total / marks.length;

console.log("Average:", average);

10. Find Students Who Scored Above Average

const marks = [80, 45, 90, 65, 85];

let total = 0;

for (let mark of marks) {
    total += mark;
}

const average = total / marks.length;

const aboveAverage = [];

for (let mark of marks) {
    if (mark > average) {
        aboveAverage.push(mark);
    }
}

console.log("Average:", average);
console.log("Above Average:", aboveAverage);

11. Find Missing Test Case IDs

Scenario

A QA automation suite should execute test cases from 1 to 10, but some test cases are missing.

const executedTests = [1, 2, 3, 5, 6, 8, 10];

const missingTests = [];

for (let i = 1; i <= 10; i++) {

    if (!executedTests.includes(i)) {
        missingTests.push(i);
    }
}

console.log("Missing Tests:", missingTests);

Output:

Missing Tests: [4, 7, 9]

12. Count Even and Odd Numbers

Scenario

An application receives transaction IDs and needs to classify them.

const transactionIds = [101, 202, 303, 404, 505, 606];

let even = 0;
let odd = 0;

for (let id of transactionIds) {

    if (id % 2 === 0) {
        even++;
    } else {
        odd++;
    }
}

console.log("Even:", even);
console.log("Odd:", odd);

13. Reverse an Array Without reverse()

const users = ["John", "David", "Mike", "Alex"];

const reversed = [];

for (let i = users.length - 1; i >= 0; i--) {
    reversed.push(users[i]);
}

console.log(reversed);

Output:

["Alex", "Mike", "David", "John"]

14. Find a Specific User

const users = ["John", "David", "Mike", "Alex"];

const searchUser = "Mike";

if (users.includes(searchUser)) {
    console.log("User Found");
} else {
    console.log("User Not Found");
}

15. Count Occurrence of a Value

Scenario

Find how many times a particular product was purchased.

const products = [
    "Laptop",
    "Mobile",
    "Laptop",
    "Tablet",
    "Laptop",
    "Mobile"
];

const searchProduct = "Laptop";

let count = 0;

for (let product of products) {
    if (product === searchProduct) {
        count++;
    }
}

console.log("Laptop purchased:", count, "times");

16. Find Common Elements Between Two Arrays

Scenario

Find users who are present in both applications.

const app1Users = ["John", "David", "Mike", "Alex"];
const app2Users = ["Mike", "Alex", "Robert", "Sam"];

const commonUsers = [];

for (let user of app1Users) {

    if (app2Users.includes(user)) {
        commonUsers.push(user);
    }
}

console.log(commonUsers);

Output:

["Mike", "Alex"]

17. Find Unique Elements From Two Arrays

const teamA = ["John", "David", "Mike"];
const teamB = ["Mike", "Alex", "David"];

const result = [];

for (let user of [...teamA, ...teamB]) {

    if (!result.includes(user)) {
        result.push(user);
    }
}

console.log(result);

18. Find Failed Test Cases

Scenario

A Playwright test execution produces test statuses.

const statuses = [
    "passed",
    "failed",
    "passed",
    "skipped",
    "failed",
    "passed"
];

const failedTests = [];

for (let status of statuses) {

    if (status === "failed") {
        failedTests.push(status);
    }
}

console.log("Failed Tests:", failedTests.length);

19. Separate Positive and Negative Numbers

const numbers = [10, -5, 20, -8, 15, -2];

const positive = [];
const negative = [];

for (let number of numbers) {

    if (number >= 0) {
        positive.push(number);
    } else {
        negative.push(number);
    }
}

console.log("Positive:", positive);
console.log("Negative:", negative);

20. Move All Zeros to the End

Scenario

An API returns an array containing zero values. Move all zero values to the end.

const numbers = [0, 5, 0, 3, 8, 0, 2];

const result = [];

let zeroCount = 0;

for (let number of numbers) {

    if (number === 0) {
        zeroCount++;
    } else {
        result.push(number);
    }
}

for (let i = 0; i < zeroCount; i++) {
    result.push(0);
}

console.log(result);

Output:

[5, 3, 8, 2, 0, 0, 0]

21. Find First Non-Repeated Element

Scenario

Find the first unique transaction ID.

const ids = [101, 102, 101, 103, 102, 104];

for (let id of ids) {

    let count = 0;

    for (let value of ids) {

        if (id === value) {
            count++;
        }
    }

    if (count === 1) {
        console.log("First non-repeated ID:", id);
        break;
    }
}

Output:

First non-repeated ID: 103

22. Find Maximum Consecutive Number

const numbers = [10, 20, 30, 25, 50, 60];

let maxDifference = 0;
let firstNumber;
let secondNumber;

for (let i = 0; i < numbers.length - 1; i++) {

    const difference = numbers[i + 1] - numbers[i];

    if (difference > maxDifference) {
        maxDifference = difference;
        firstNumber = numbers[i];
        secondNumber = numbers[i + 1];
    }
}

console.log(firstNumber, secondNumber);

23. Pagination Scenario

Scenario

An API returns 50 records. Display records for page 3 where each page contains 10 records.

const users = Array.from({ length: 50 }, (_, i) => `User-${i + 1}`);

const page = 3;
const pageSize = 10;

const startIndex = (page - 1) * pageSize;

const pageData = users.slice(
    startIndex,
    startIndex + pageSize
);

console.log(pageData);

Output:

[
 "User-21",
 "User-22",
 ...
 "User-30"
]

24. Search Products by Keyword

const products = [
    "iPhone 15",
    "Samsung Galaxy",
    "MacBook Pro",
    "iPad Air",
    "Samsung TV"
];

const keyword = "Samsung";

const result = products.filter(product =>
    product.toLowerCase().includes(keyword.toLowerCase())
);

console.log(result);

25. Apply Discount to Product Prices

Scenario

Apply a 10% discount to all products.

const prices = [1000, 2500, 5000, 7500];

const discountedPrices = prices.map(price => {
    return price - (price * 10 / 100);
});

console.log(discountedPrices);

Output:

[900, 2250, 4500, 6750]

26. Find Products Within a Price Range

const prices = [500, 1200, 2500, 3500, 4500, 6000];

const min = 1000;
const max = 4000;

const result = prices.filter(price =>
    price >= min && price <= max
);

console.log(result);

Output:

[1200, 2500, 3500]

27. Find the Most Frequent Element

Scenario

Find the product that appears most frequently in orders.

const products = [
    "Laptop",
    "Mobile",
    "Laptop",
    "Tablet",
    "Mobile",
    "Laptop"
];

let maxCount = 0;
let mostFrequent;

for (let product of products) {

    let count = 0;

    for (let value of products) {
        if (product === value) {
            count++;
        }
    }

    if (count > maxCount) {
        maxCount = count;
        mostFrequent = product;
    }
}

console.log("Most Frequent:", mostFrequent);
console.log("Count:", maxCount);

28. Compare Expected and Actual Results

Scenario

This is especially useful for API/UI automation validation.

const expected = ["Login", "Dashboard", "Logout"];
const actual = ["Login", "Dashboard", "Logout"];

let isMatching = true;

if (expected.length !== actual.length) {
    isMatching = false;
} else {

    for (let i = 0; i < expected.length; i++) {

        if (expected[i] !== actual[i]) {
            isMatching = false;
            break;
        }
    }
}

console.log("Result:", isMatching);

29. Find Missing Values Between Two Arrays

const expected = [101, 102, 103, 104, 105];
const actual = [101, 103, 105];

const missing = [];

for (let id of expected) {

    if (!actual.includes(id)) {
        missing.push(id);
    }
}

console.log("Missing:", missing);

Output:

Missing: [102, 104]

30. Flatten Nested Array

Scenario

An API returns nested categories.

const categories = [
    ["Electronics", "Mobile"],
    ["Furniture", "Chair"],
    ["Books", "Novel"]
];

const result = categories.flat();

console.log(result);

Output:

[
    "Electronics",
    "Mobile",
    "Furniture",
    "Chair",
    "Books",
    "Novel"
]

31. QA/SDET Scenario: Validate API Response IDs

const apiResponse = [
    { id: 101, name: "John" },
    { id: 102, name: "David" },
    { id: 103, name: "Mike" }
];

const ids = apiResponse.map(user => user.id);

const expectedIds = [101, 102, 103];

console.log(
    JSON.stringify(ids) === JSON.stringify(expectedIds)
        ? "Test Passed"
        : "Test Failed"
);

32. QA/SDET Scenario: Find Failed Test Names

const testResults = [
    { name: "Login Test", status: "passed" },
    { name: "Search Test", status: "failed" },
    { name: "Checkout Test", status: "passed" },
    { name: "Payment Test", status: "failed" }
];

const failedTests = testResults
    .filter(test => test.status === "failed")
    .map(test => test.name);

console.log(failedTests);

Output:

["Search Test", "Payment Test"]

33. QA/SDET Scenario: Calculate Test Execution Summary

const results = [
    "passed",
    "passed",
    "failed",
    "skipped",
    "passed",
    "failed",
    "passed"
];

const summary = {
    passed: 0,
    failed: 0,
    skipped: 0
};

for (let result of results) {
    summary[result]++;
}

console.log(summary);

Output:

{
    passed: 4,
    failed: 2,
    skipped: 1
}

34. Find Duplicate Test Case IDs

const testCases = [
    "TC001",
    "TC002",
    "TC003",
    "TC001",
    "TC004",
    "TC002"
];

const duplicates = [];

for (let i = 0; i < testCases.length; i++) {

    for (let j = i + 1; j < testCases.length; j++) {

        if (
            testCases[i] === testCases[j] &&
            !duplicates.includes(testCases[i])
        ) {
            duplicates.push(testCases[i]);
        }
    }
}

console.log("Duplicate Test Cases:", duplicates);

Output:

Duplicate Test Cases: ["TC001", "TC002"]

35. Advanced Interview Scenario: Find Two Numbers Whose Sum Equals Target

Scenario

Find two product prices whose total is ₹3000.

const prices = [500, 1200, 1800, 2500, 1500];

const target = 3000;

for (let i = 0; i < prices.length; i++) {

    for (let j = i + 1; j < prices.length; j++) {

        if (prices[i] + prices[j] === target) {
            console.log(
                prices[i],
                "+",
                prices[j],
                "=",
                target
            );
        }
    }
}

Output:

1200 + 1800 = 3000
1500 + 1500 = 3000

Interview Practice Set

For your JavaScript/Playwright/SDET training sessions, these are particularly good interview exercises:

Beginner

  1. Find maximum number.
  2. Find minimum number.
  3. Calculate array sum.
  4. Calculate average.
  5. Count even and odd numbers.
  6. Reverse an array.
  7. Search an element.
  8. Find positive and negative numbers.
  9. Remove duplicates.
  10. Count occurrences.

Intermediate

  1. Find second maximum without sorting.
  2. Find duplicate elements.
  3. Find missing numbers.
  4. Find common elements between arrays.
  5. Find unique elements.
  6. Move zeros to the end.
  7. Find first non-repeated element.
  8. Find most frequent element.
  9. Find elements above average.
  10. Find values within a range.

Advanced

  1. Two-sum problem.
  2. Compare two arrays.
  3. Find array intersection.
  4. Find array difference.
  5. Flatten nested arrays.
  6. Group test results.
  7. Validate API response arrays.
  8. Find duplicate test case IDs.
  9. Implement pagination using arrays.
  10. Process and summarize test execution results.

Github Action Example Pipeline

1. Git & GitHub Fundamentals

GitHub Actions relies on Git object mechanics. Workflows are declared as code and committed directly to your repository inside the .github/workflows/ directory. GitHub observes changes to Git references (refs/heads/<branch>, refs/tags/<tag>, refs/pull/<number>/merge) to evaluate trigger rules.

How Git Mechanics Map to Actions

  • Commits & Pushes: Every git push transmits a SHA hash and branch ref that GitHub Actions uses to check out the exact state of your code.
  • Pull Requests: A PR trigger creates a virtual merge commit (refs/pull/PR_NUMBER/merge) combining your source branch with the target branch to test collision-free execution.
  • Tags & Releases: Semantic version tags (v1.0.0) trigger build, package, and publish workflows.

Bash

# Developer Workflow triggering CI
git checkout -b feature/user-auth
git commit -m "feat: implement JWT login"
git push origin feature/user-auth # Triggers push / pull_request events on GitHub

2. YAML Syntax

Workflows are written in YAML (YAML Ain’t Markup Language). YAML uses strictly spaces for indentation (tabs are forbidden) and relies on key-value pairs, scalar types, arrays, and dictionaries.

Key Syntax Rules for GitHub Actions

  • Indentation: standard is 2 spaces per level.
  • Lists/Sequences: Prefixed with a hyphen and a space (- item).
  • Multi-line Strings: Use | to preserve line breaks or > to fold line breaks into spaces.
  • Booleans & Numbers: true, false, 10, 3.14 (unquoted). Quoting forces string representation ("true").

YAML

# Demonstration of YAML data structures
string_scalar: "Hello World"
number_scalar: 42
boolean_scalar: true

# List / Array syntax
browser_list:
  - chromium
  - firefox
  - webkit

# Dictionary / Object syntax
runner_config:
  os: ubuntu-latest
  timeout: 30

# Multi-line script execution block (| preserves newlines)
multi_line_command: |
  echo "Line 1"
  echo "Line 2"
  npm test

3. Creating Your First Workflow

A minimal workflow file must be stored in .github/workflows/main.yml. It requires three root keys: name, on, and jobs.

YAML

# .github/workflows/first-workflow.yml
name: First Workflow

# Event trigger
on:
  push:
    branches: [ "main" ]

# Execution unit
jobs:
  welcome-job:
    runs-on: ubuntu-latest
    steps:
      - name: Print welcome message
        run: echo "GitHub Actions workflow executed successfully!"

      - name: Output commit metadata
        run: |
          echo "Commit SHA: ${{ github.sha }}"
          echo "Triggered by: ${{ github.actor }}"

4. Events and Triggers (on)

The on key determines when a workflow runs. You can listen to single events, array events, or apply fine-grained activity filters and path filters.

Filtering Rules

  • branches: Restrict runs to specific target branches.
  • paths / paths-ignore: Trigger only when specific files change.
  • tags: Trigger on Git tags matching glob patterns.

YAML

name: Event Trigger Masterclass

on:
  # Trigger on pushes to main or release branches, but only if code inside src/ changed
  push:
    branches:
      - main
      - 'releases/v*'
    paths:
      - 'src/**'
      - 'package.json'
    paths-ignore:
      - '**.md'

  # Trigger on Pull Requests targeting main branch when opened or synchronized
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

  # Trigger on tag creation matching v1.0.0, v2.1.0, etc.
  push:
    tags:
      - 'v*.*.*'

5. Jobs and Steps

  • Job: A set of steps running on a specific host environment (runs-on). Jobs run in parallel by default.
  • Step: An individual task executed sequentially inside a job. Steps share a filesystem and environment state on the runner VM.

YAML

name: Job Dependency Pipeline

on: push

jobs:
  compile:
    runs-on: ubuntu-latest
    steps:
      - name: Step 1 - Build
        run: echo "Compiling assets..."

  test:
    needs: compile # Forces 'test' to wait until 'compile' finishes successfully
    runs-on: ubuntu-latest
    steps:
      - name: Step 1 - Unit Test
        run: echo "Executing unit tests..."

  deploy:
    needs: [compile, test] # Waits for both upstream jobs
    runs-on: ubuntu-latest
    steps:
      - name: Step 1 - Deploy
        run: echo "Deploying to production..."

6. Using Marketplace Actions

Instead of writing raw shell scripts for every task, you can import community and official actions from the GitHub Marketplace using the uses: keyword.

Versioning Actions

  • @v4 (Major version tag – receives non-breaking updates).
  • @v4.1.2 (Exact semantic version).
  • @8f4b7f8... (Full commit SHA – most secure, immutable).

YAML

jobs:
  setup-environment:
    runs-on: ubuntu-latest
    steps:
      # Action 1: Clone repository code
      - name: Checkout Repository
        uses: actions/checkout@v4

      # Action 2: Provision Node.js runtime environment
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      # Step 3: Native shell execution
      - name: Install dependencies
        run: npm ci

7. Environment Variables and Secrets

GitHub Actions handles environment configuration at the workflow, job, or step scope. Sensitive credentials must be stored as Encrypted Secrets in repository/organization settings.

YAML

name: Environment & Secrets Demo

on: push

# Workflow-level environment variables
env:
  GLOBAL_APP_NAME: "MyEnterpriseApp"
  LOG_LEVEL: "debug"

jobs:
  process-payment:
    runs-on: ubuntu-latest
    # Job-level environment variables
    env:
      JOB_ENV: "payment-processor"

    steps:
      - name: Secure API Call
        # Step-level environment variables
        env:
          PAYMENT_API_KEY: ${{ secrets.PAYMENT_GATEWAY_KEY }} # Injecting secret
          DYNAMIC_VAR: "step-specific-value"
        run: |
          echo "Processing payment for $GLOBAL_APP_NAME"
          # Accessing secret safely via environment variable in shell
          python process.py --key "$PAYMENT_API_KEY"

      - name: Set dynamic environment variable for subsequent steps
        run: |
          echo "BUILD_TIMESTAMP=$(date +'%Y-%m-%d_%H-%M-%S')" >> $GITHUB_ENV

      - name: Read dynamic variable
        run: |
          echo "Timestamp was: $BUILD_TIMESTAMP"

8. Expressions and Conditional Execution

Expressions let you evaluate programmatic conditions and access context objects (github, env, runner, steps, secrets). Use if: directives to skip jobs or steps dynamically.

YAML

jobs:
  conditional-job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Execute on main branch only
        if: github.ref == 'refs/heads/main'
        run: echo "This step runs ONLY on pushes to main."

      - name: Execute step with custom status check functions
        id: test_step
        run: npm test

      # Runs ONLY if the previous step failed
      - name: Report Failure
        if: failure() && steps.test_step.outcome == 'failure'
        run: echo "The test execution step failed!"

      # Runs ALWAYS, regardless of prior step pass/fail state
      - name: Always Cleanup
        if: always()
        run: echo "Cleaning up temporary test artifacts..."

9. Matrix Strategy

A Matrix Strategy generates multiple job runs by defining input variables across OSs, runtimes, or database versions. GitHub Actions expands these configurations into a parallel execution grid.

YAML

jobs:
  matrix-build:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false # Keep running other matrix jobs even if one fails
      max-parallel: 4 # Limit concurrent runners
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18, 20]
        # Include specific additional configuration
        include:
          - os: ubuntu-latest
            node-version: 22
            experimental: true
        # Exclude invalid or unwanted combinations
        exclude:
          - os: windows-latest
            node-version: 18

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: node -v

10. Caching Dependencies

To speed up workflow runs, use actions/cache or built-in caching inside setup actions. Caches persist archive files between workflow runs using a unique cache key (typically hashed from lockfiles).

YAML

jobs:
  cache-demo:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # Method A: Built-in setup action caching (Recommended)
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip' # Automatically hashes requirements.txt/Pipfile

      # Method B: Manual Cache Configuration via actions/cache
      - name: Cache Custom Directory
        uses: actions/cache@v4
        with:
          path: ~/.custom_cache_dir
          # Primary lookup key
          key: ${{ runner.os }}-custom-${{ hashFiles('**/lockfile.json') }}
          # Fallback lookup keys if primary key misses
          restore-keys: |
            ${{ runner.os }}-custom-

      - name: Install Dependencies
        run: pip install -r requirements.txt

11. Uploading and Downloading Artifacts

Artifacts allow you to persist output files (build binaries, test reports, coverage HTML) beyond the runner’s ephemeral lifecycle and pass data between independent jobs.

YAML

jobs:
  build-job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: mkdir dist && echo "Compiled App Code" > dist/app.bin

      # Upload artifacts
      - name: Upload Build Binaries
        uses: actions/upload-artifact@v4
        with:
          name: compiled-app-artifact
          path: dist/
          retention-days: 7

  deploy-job:
    needs: build-job # Must wait for build-job to upload
    runs-on: ubuntu-latest
    steps:
      # Download artifacts generated in upstream job
      - name: Download Build Binaries
        uses: actions/download-artifact@v4
        with:
          name: compiled-app-artifact
          path: downloaded-dist/

      - name: Verify Downloaded Assets
        run: cat downloaded-dist/app.bin

12. Manual and Scheduled Workflows

Workflows can run on cron schedules or be triggered manually via the GitHub UI/API with custom user inputs (workflow_dispatch).

YAML

name: Manual & Scheduled Execution

on:
  # Cron trigger: Runs every Monday at 08:00 UTC
  schedule:
    - cron: '0 8 * * 1'

  # Manual trigger with interactive input forms
  workflow_dispatch:
    inputs:
      target_env:
        description: 'Target Deployment Environment'
        type: choice
        required: true
        options:
          - staging
          - production
      perform_cleanup:
        description: 'Run deep database cleanup?'
        type: boolean
        default: false

jobs:
  execute:
    runs-on: ubuntu-latest
    steps:
      - name: Print Trigger Context
        run: |
          echo "Trigger event: ${{ github.event_name }}"
          echo "Selected Environment: ${{ inputs.target_env }}"
          echo "Perform Cleanup: ${{ inputs.perform_cleanup }}"

13. Reusable Workflows and Composite Actions

Composite Action (.github/actions/setup-stack/action.yml)

Bundles multiple run steps into a single reusable step block.

YAML

name: 'Setup Project Stack'
description: 'Standardized setup steps for project'
runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: '20'
    - run: npm ci
      shell: bash # Shell directive is mandatory for composite actions

Reusable Workflow (.github/workflows/reusable-ci.yml)

Encapsulates complete jobs for consumption across callers.

YAML

name: Reusable CI Core
on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: '20'
jobs:
  run-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm test

Calling Workflow (.github/workflows/caller.yml)

YAML

jobs:
  invoke-reusable:
    uses: ./.github/workflows/reusable-ci.yml
    with:
      node-version: '22'

14. Docker Integration

GitHub Actions runners come pre-configured with Docker, enabling step containers, containerized jobs, service containers (databases/queues), and image publishing.

YAML

jobs:
  # Integration testing using a Database Service Container
  database-tests:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: mysecretpassword
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        # Health checks to ensure DB is ready before test steps start
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - name: Verify PostgreSQL Connection
        run: |
          PGPASSWORD=mysecretpassword psql -h localhost -U postgres -d testdb -c "SELECT 1;"

  # Build & Publish Docker Container Image
  docker-build-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Log in to GitHub Container Registry (GHCR)
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}/app:latest

15. Cloud Deployments (AWS/Azure/GCP)

To deploy safely to modern cloud infrastructure, never use long-lived access key secrets. Use OpenID Connect (OIDC) to request temporary, short-lived tokens using IAM Role assumption.

YAML

name: Keyless OIDC AWS Deployment

on:
  push:
    branches: [ "main" ]

jobs:
  deploy-aws:
    runs-on: ubuntu-latest
    # Required permission to request the OIDC JWT token from GitHub
    permissions:
      id-token: write
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Authenticate with AWS via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeploymentRole
          aws-region: us-east-1

      - name: Deploy to S3 Static Bucket
        run: |
          aws s3 sync ./dist s3://my-production-app-bucket --delete

16. Enterprise CI/CD Patterns and Security

Production enterprise workflows require strict security practices: Zero Trust permissions, Pinned Action Hashes, Concurrency Controls, and Protected Environments.

YAML

name: Enterprise Secure Pipeline

on:
  push:
    branches: [ "main" ]

# Prevent race conditions by cancelling in-progress runs on same branch
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# Hardened Security Rule 1: Set Least-Privilege permissions globally
permissions: {}

jobs:
  secure-build:
    runs-on: ubuntu-latest
    permissions:
      contents: read # Read-only code access
      id-token: write # For OIDC deployment step

    # Attach workflow to protected GitHub Environment (Approval rules & secrets)
    environment:
      name: production
      url: https://my-app.company.com

    steps:
      # Hardened Security Rule 2: Pin Actions to full immutable commit SHAs
      - name: Checkout Code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Setup Node.js
        uses: actions/setup-node@39370e3970a6d050c080011e813206d11393d18e # v4.1.0
        with:
          node-version: '20'

      - name: Run Dependency Vulnerability Audit
        run: npm audit --audit-level=high

      - name: Build Application
        run: npm run build