TypeScript Functions — Declarations, Arrow Functions, Async & Generics with Playwright

Functions are the building blocks of clean, reusable code. In test automation, you’ll use them everywhere — from page actions to custom assertions to utility helpers. This guide covers every function pattern in TypeScript with real Playwright examples.

📖 What You’ll Learn: Function declarations, arrow functions, optional & default parameters, rest parameters, return types, overloads, generics in functions, and async functions in Playwright.


🔧 Function Declarations & Expressions

// Function declaration
function greet(name: string): string {
  return `Hello, ${name}!`;
}

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

// Arrow function (most common in modern TypeScript)
const multiply = (a: number, b: number): number => a * b;

// Void function — returns nothing
function logMessage(msg: string): void {
  console.log(`[LOG]: ${msg}`);
}

console.log(greet('QA Engineer'));  // Hello, QA Engineer!
console.log(add(5, 3));            // 8
console.log(multiply(4, 7));       // 28

⚙️ Optional, Default & Rest Parameters

// Optional parameter (use ?)
function createUser(name: string, email?: string): string {
  return email ? `${name} (${email})` : name;
}
console.log(createUser('Deepesh'));                    // Deepesh
console.log(createUser('Deepesh', 'test@test.com'));   // Deepesh (test@test.com)

// Default parameter
function wait(ms: number = 1000): void {
  console.log(`Waiting ${ms}ms...`);
}
wait();      // Waiting 1000ms...
wait(3000);  // Waiting 3000ms...

// Rest parameters (variable number of args)
function logAll(...messages: string[]): void {
  messages.forEach((msg, i) => console.log(`${i + 1}. ${msg}`));
}
logAll('Step 1', 'Step 2', 'Step 3');

// Destructured parameters
function printConfig({ url, timeout = 30000 }: { url: string; timeout?: number }): void {
  console.log(`URL: ${url}, Timeout: ${timeout}`);
}
printConfig({ url: 'https://sqatools.in' });

📝 Function Types & Callbacks

// Type alias for a function
type Validator = (value: string) => boolean;

const isEmail: Validator = (value) => value.includes('@');
const isNotEmpty: Validator = (value) => value.trim().length > 0;

console.log(isEmail('test@test.com'));  // true
console.log(isNotEmpty(''));            // false

// Callback functions
function runValidation(input: string, validator: Validator, onResult: (passed: boolean) => void): void {
  const result = validator(input);
  onResult(result);
}

runValidation('hello@test.com', isEmail, (passed) => {
  console.log(passed ? '✅ Valid' : '❌ Invalid');
});

// Returning functions (higher-order)
function createMultiplier(factor: number): (n: number) => number {
  return (n) => n * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5));  // 10
console.log(triple(5));  // 15

🧬 Generic Functions

// Generic function — works with any type
function firstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

console.log(firstElement([1, 2, 3]));          // 1 (type: number)
console.log(firstElement(['a', 'b', 'c']));    // 'a' (type: string)

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

const user = { name: 'Deepesh', role: 'SDET' };
console.log(getProperty(user, 'name'));  // 'Deepesh'

// Generic async function
async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  return response.json() as Promise<T>;
}

💡 Generics let you write ONE function that works with strings, numbers, objects — anything. TypeScript figures out the type automatically when you call it.


⚡ Async Functions

// Async function returns a Promise
async function fetchTitle(url: string): Promise<string> {
  const response = await fetch(url);
  const html = await response.text();
  const match = html.match(/<title>(.*)<\/title>/);
  return match ? match[1] : 'No title found';
}

// Async arrow function
const delay = async (ms: number): Promise<void> => {
  return new Promise(resolve => setTimeout(resolve, ms));
};

// Try-catch in async functions
async function safeRequest(url: string): Promise<string | null> {
  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.text();
  } catch (error) {
    console.error('Request failed:', error);
    return null;
  }
}

🎭 Functions in Playwright — Real Examples

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

// Reusable login helper function
async function login(page: Page, email: string, password: string): Promise<void> {
  await page.goto('/login');
  await page.fill('#email', email);
  await page.fill('#password', password);
  await page.click('#login-btn');
  await expect(page.locator('.dashboard')).toBeVisible();
}

// Retry function for flaky operations
async function retry<T>(fn: () => Promise<T>, maxAttempts: number = 3): Promise<T> {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxAttempts) throw error;
      console.log(`Attempt ${attempt} failed, retrying...`);
      await new Promise(r => setTimeout(r, 1000));
    }
  }
  throw new Error('Should not reach here');
}

// Using helper functions in tests
test('login and verify dashboard', async ({ page }) => {
  await login(page, 'admin@sqatools.in', 'test123');
  await expect(page).toHaveTitle(/Dashboard/);
});

test('retry flaky element click', async ({ page }) => {
  await page.goto('https://sqatools.in');
  await retry(async () => {
    await page.click('.dynamic-button');
    await expect(page.locator('.result')).toBeVisible();
  });
});

❓ FAQ

🤔 When should I use arrow functions vs regular functions?

Use arrow functions for callbacks, short expressions, and class methods. Use regular functions when you need hoisting or a named function in stack traces. In Playwright, arrow functions are standard for test callbacks and helpers.


🤔 What is the difference between optional and default parameters?

Optional parameters (name?: string) can be omitted and will be undefined. Default parameters (name: string = 'Guest') have a fallback value when omitted. Default parameters are preferred when you have a sensible default.

🎯 Next Steps

🎬 Watch the full TypeScript tutorial series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

Leave a Comment