TypeScript Data Types Practice Programs with Solutions (31 Programs)

Practise TypeScript data types with 31 programs, each with a full solution, the exact output and a short explanation. The programs start with the basics, move to intermediate problems and finish with real-world scenarios from test automation, such as test data, prices, form inputs and test results. They are written for testers learning TypeScript for Playwright.

Advertisement

How to practise: read the task, write your own solution first, then open Show solution to compare. Run any program with npx tsx program.ts (or compile it with npx tsc).

New to this topic? Read the TypeScript Data Types lesson first.

TypeScript Beginner Programs

1. The primitive typesBeginner

Declare one variable of each primitive type and print its runtime type.

Show solution
const title: string = 'Login test';
const duration: number = 2.5;
const passed: boolean = true;
const bigId: bigint = 9007199254740993n;
const key: symbol = Symbol('id');
console.log(typeof title, typeof duration, typeof passed, typeof bigId, typeof key);

Output

string number boolean bigint symbol

Explanation: TypeScript’s primitive types match JavaScript’s runtime types.

2. null and undefinedBeginner

Declare variables that can be null or undefined and print them.

Show solution
let selectedOption: string | null = null;
let middleName: string | undefined;
console.log(selectedOption, middleName);
selectedOption = 'Express';
console.log(selectedOption);

Output

null undefined
Express

Explanation: with strictNullChecks, null and undefined must be included in the type explicitly.

3. Arrays in two notationsBeginner

Declare number arrays with number[] and Array<number>.

Show solution
const scores: number[] = [90, 75, 88];
const timings: Array<number> = [120, 340];
console.log(scores.length + timings.length);
console.log(Math.max(...scores));

Output

5
90

Explanation: number[] and Array&lt;number&gt; are the same type. The shorter form is more common.

4. TupleBeginner

Store a username and password pair in a tuple.

Show solution
const credentials: [string, string] = ['qa_user', 'Secret123'];
const [user, pass] = credentials;
console.log(user, pass.length);

Output

qa_user 9

Explanation: a tuple has a fixed length and a type for each position.

5. Tuple with different typesBeginner

Store an HTTP status code and message as [number, string].

Show solution
const response: [number, string] = [404, 'Not Found'];
console.log(`${response[0]} ${response[1]}`);
// @ts-expect-error: Type 'string' is not assignable to type 'number'
const wrong: [number, string] = ['404', 'Not Found'];
console.log(wrong.length);

Output

404 Not Found
2

Explanation: each tuple position is checked separately, so swapped or wrong types are compile errors.

6. Numeric enumBeginner

Create a Priority enum and print a value and its name.

Show solution
enum Priority { Low, Medium, High }
const p: Priority = Priority.High;
console.log(p);
console.log(Priority[p]);

Output

2
High

Explanation: numeric enums start at 0. Reverse mapping turns a number back into its name.

7. String enumBeginner

Create a Browser string enum and use it in a message.

Show solution
enum Browser {
  Chromium = 'chromium',
  Firefox = 'firefox',
  WebKit = 'webkit'
}
const target: Browser = Browser.Firefox;
console.log(`Running on ${target}`);

Output

Running on firefox

Explanation: string enum members have readable values, which is useful in logs and reports.

8. void return typeBeginner

Write a logging function with a void return type.

Show solution
function logStep(step: string): void {
  console.log(`STEP: ${step}`);
}
const result = logStep('Open login page');
console.log(result);

Output

STEP: Open login page
undefined

Explanation: void means the function returns nothing useful. At runtime it returns undefined.

9. Object typeBeginner

Describe a product with an inline object type.

Show solution
const product: { sku: string; price: number; tags: string[] } = {
  sku: 'KB-01',
  price: 1499,
  tags: ['electronics', 'keyboard']
};
console.log(product.sku, product.price, product.tags.join('/'));

Output

KB-01 1499 electronics/keyboard

Explanation: object types list each property name with its type.

10. Date typeBeginner

Create a Date from a fixed ISO string and print parts of it in UTC.

Show solution
const runDate: Date = new Date('2026-11-02T09:30:00Z');
console.log(runDate.toISOString());
console.log(runDate.getUTCFullYear(), runDate.getUTCMonth() + 1, runDate.getUTCDate());

Output

2026-11-02T09:30:00.000Z
2026 11 2

Explanation: built-in classes such as Date are types too. UTC methods avoid time-zone differences.

TypeScript Intermediate Programs

11. unknown vs anyIntermediate

Assign the same value to any and unknown and see which requires a check.

Show solution
const fromApi: unknown = JSON.parse('{"id": 7}');
const anything: any = JSON.parse('{"id": 7}');
console.log(anything.id);
if (typeof fromApi === 'object' && fromApi !== null && 'id' in fromApi) {
  console.log((fromApi as { id: number }).id);
}

Output

7
7

Explanation: unknown forces you to check the shape first, while any lets you access anything without checks.

12. never for exhaustive checksIntermediate

Use never to make sure every status in a union is handled.

Show solution
type Status = 'passed' | 'failed' | 'skipped';
function label(status: Status): string {
  switch (status) {
    case 'passed': return '✔';
    case 'failed': return '✘';
    case 'skipped': return '-';
    default: {
      const unreachable: never = status;
      return unreachable;
    }
  }
}
console.log(['passed', 'failed', 'skipped'].map(s => label(s as Status)).join(' '));

Output

✔ ✘ -

Explanation: if a new status is added to the union, the never assignment fails to compile until it is handled.

13. Union of object typesIntermediate

Model an API result that is either success or error, and print it.

Show solution
type ApiResult = { ok: true; data: string[] } | { ok: false; error: string };
const results: ApiResult[] = [{ ok: true, data: ['a', 'b'] }, { ok: false, error: 'Timeout' }];
for (const r of results) {
  console.log(r.ok ? `Got ${r.data.length} items` : `Failed: ${r.error}`);
}

Output

Got 2 items
Failed: Timeout

Explanation: checking the ok property narrows the union, so data and error are only accessible in the right branch.

14. Intersection typesIntermediate

Combine a User type and a Timestamps type into one.

Show solution
type User = { id: number; name: string };
type Timestamps = { createdAt: string };
type UserRecord = User & Timestamps;
const rec: UserRecord = { id: 1, name: 'Asha', createdAt: '2026-11-01' };
console.log(rec.name, rec.createdAt);

Output

Asha 2026-11-01

Explanation: A &amp; B requires all properties of both types.

15. Type assertion with asIntermediate

Parse JSON and tell TypeScript its shape with as.

Show solution
type Config = { retries: number; baseURL: string };
const config = JSON.parse('{"retries":2,"baseURL":"https://qa.app.test"}') as Config;
console.log(config.retries + 1, config.baseURL);

Output

3 https://qa.app.test

Explanation: as tells the compiler what type to assume. It does not check the data at runtime, so use it only when you trust the source.

16. Optional tuple elementsIntermediate

Use a tuple with an optional third element for a test step.

Show solution
type Step = [action: string, target: string, value?: string];
const steps: Step[] = [['click', 'Login'], ['fill', 'Email', 'qa@test.com']];
for (const [action, target, value] of steps) {
  console.log(value ? `${action} ${target} = ${value}` : `${action} ${target}`);
}

Output

click Login
fill Email = qa@test.com

Explanation: labelled tuple elements document each position, and ? makes the last one optional.

17. Readonly tupleIntermediate

Create a read-only coordinate pair and show the compile error when changing it.

Show solution
const viewport: readonly [number, number] = [1280, 720];
console.log(`${viewport[0]}x${viewport[1]}`);
// @ts-expect-error: Cannot assign to '0' because it is a read-only property
viewport[0] = 1920;

Output

1280x720

Explanation: readonly tuples protect fixed values such as default viewport sizes.

18. Index signaturesIntermediate

Type an object of HTTP headers with unknown keys.

Show solution
const headers: { [name: string]: string } = { 'content-type': 'application/json' };
headers['x-request-id'] = 'abc-123';
console.log(Object.keys(headers).length, headers['content-type']);

Output

2 application/json

Explanation: an index signature allows any string key with values of the declared type.

19. Function typesIntermediate

Declare a variable whose type is a function from string to boolean.

Show solution
type Validator = (value: string) => boolean;
const isEmail: Validator = value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
const isNotEmpty: Validator = value => value.trim().length > 0;
console.log(isEmail('qa@test.com'), isEmail('qa'), isNotEmpty('  '));

Output

true false false

Explanation: function types describe parameters and return types, so any function assigned must match.

20. bigint arithmeticIntermediate

Add two large order IDs as bigint values.

Show solution
const base: bigint = 9007199254740991n;
const next: bigint = base + 2n;
console.log(next.toString());
try {
  // @ts-expect-error: Operator '+' cannot be applied to types 'bigint' and 'number'
  console.log(base + 1);
} catch (error) {
  console.log('Also fails at runtime:', error instanceof TypeError);
}

Output

9007199254740993
Also fails at runtime: true

Explanation: TypeScript rejects mixing bigint and number at compile time. The same mistake would throw a TypeError in plain JavaScript, so the compiler saves you from a runtime crash.

TypeScript Real-World QA Scenario Programs

21. Typed API responseReal-world

Describe a paginated API response and read its fields.

Show solution
type Product = { id: number; name: string; price: number };
type Page<T> = { items: T[]; page: number; total: number };
const response: Page<Product> = {
  items: [{ id: 1, name: 'Mouse', price: 599 }, { id: 2, name: 'Cable', price: 199 }],
  page: 1,
  total: 12
};
console.log(`Page ${response.page}: ${response.items.length} of ${response.total} items`);
console.log(response.items.map(p => p.name).join(', '));

Output

Page 1: 2 of 12 items
Mouse, Cable

Explanation: a typed response shape gives autocomplete and catches typos such as respose.itmes.

22. Status enum in test resultsReal-world

Use a string enum for test statuses and count them.

Show solution
enum TestStatus { Passed = 'passed', Failed = 'failed', Skipped = 'skipped' }
const runs: TestStatus[] = [TestStatus.Passed, TestStatus.Failed, TestStatus.Passed];
const failed = runs.filter(s => s === TestStatus.Failed).length;
console.log(`${runs.length} runs, ${failed} failed`);

Output

3 runs, 1 failed

Explanation: an enum gives one source of truth for allowed status values.

23. Tuple for table rowsReal-world

Type web-table rows as tuples and total the price column.

Show solution
type Row = [name: string, qty: number, price: number];
const rows: Row[] = [['Pen', 3, 20], ['Book', 1, 350], ['Bag', 2, 899]];
const total = rows.reduce((sum, [, qty, price]) => sum + qty * price, 0);
console.log('Order total:', total);

Output

Order total: 2208

Explanation: tuples fit fixed-column data such as table rows, and destructuring reads the columns by position.

24. Discriminated union for UI eventsReal-world

Handle click and fill events with a discriminated union.

Show solution
type Action =
  | { type: 'click'; selector: string }
  | { type: 'fill'; selector: string; value: string }
  | { type: 'goto'; url: string };
const actions: Action[] = [
  { type: 'goto', url: '/login' },
  { type: 'fill', selector: '#email', value: 'qa@test.com' },
  { type: 'click', selector: 'button[type=submit]' }
];
for (const a of actions) {
  if (a.type === 'goto') console.log('Navigate to', a.url);
  else if (a.type === 'fill') console.log(`Fill ${a.selector} with ${a.value}`);
  else console.log('Click', a.selector);
}

Output

Navigate to /login
Fill #email with qa@test.com
Click button[type=submit]

Explanation: the shared type field tells TypeScript which properties each action has.

25. Validate unknown JSONReal-world

Write a type guard that checks unknown JSON before using it as a User.

Show solution
type User = { id: number; email: string };
function isUser(value: unknown): value is User {
  return typeof value === 'object' && value !== null &&
    typeof (value as User).id === 'number' && typeof (value as User).email === 'string';
}
for (const text of ['{"id":1,"email":"qa@test.com"}', '{"id":"1"}']) {
  const data: unknown = JSON.parse(text);
  console.log(isUser(data) ? `Valid user ${data.email}` : 'Invalid user data');
}

Output

Valid user qa@test.com
Invalid user data

Explanation: a type guard (value is User) checks at runtime and narrows the type at compile time.

26. Nullable fields from a formReal-world

Handle a form object where optional fields may be null.

Show solution
type Form = { name: string; company: string | null; phone?: string };
const forms: Form[] = [
  { name: 'Asha', company: 'Acme', phone: '98765' },
  { name: 'Ravi', company: null }
];
for (const f of forms) {
  console.log(`${f.name} | ${f.company ?? 'Individual'} | ${f.phone ?? '-'}`);
}

Output

Asha | Acme | 98765
Ravi | Individual | -

Explanation: null (an explicit empty value) and optional fields are both handled with ??.

27. Record of expected textsReal-world

Store expected page titles in a Record keyed by route.

Show solution
type Route = '/' | '/login' | '/cart';
const expectedTitles: Record<Route, string> = { '/': 'Home', '/login': 'Sign in', '/cart': 'Your cart' };
const visited: Route[] = ['/login', '/cart'];
for (const route of visited) console.log(`${route} -> ${expectedTitles[route]}`);

Output

/login -> Sign in
/cart -> Your cart

Explanation: Record&lt;Route, string&gt; requires an entry for every route, so none can be forgotten.

28. Symbol as a unique keyReal-world

Use a symbol to attach hidden metadata to a test object.

Show solution
const meta: unique symbol = Symbol('meta');
const test = { title: 'Login', [meta]: { retries: 2 } };
console.log(Object.keys(test));
console.log(test[meta].retries);

Output

[ 'title' ]
2

Explanation: symbol keys do not appear in Object.keys, so metadata does not interfere with normal properties.

29. Date arithmetic with typesReal-world

Calculate a token expiry 30 minutes after a fixed issue time.

Show solution
const issuedAt: Date = new Date('2026-11-02T10:00:00Z');
const expiresAt: Date = new Date(issuedAt.getTime() + 30 * 60 * 1000);
const remainingMin: number = (expiresAt.getTime() - issuedAt.getTime()) / 60000;
console.log(expiresAt.toISOString(), remainingMin);

Output

2026-11-02T10:30:00.000Z 30

Explanation: getTime() returns a number of milliseconds, which makes date arithmetic type-safe.

30. Map with typed keys and valuesReal-world

Use a Map&lt;string, number&gt; to track response times per endpoint.

Show solution
const timings = new Map<string, number>();
timings.set('/api/login', 220);
timings.set('/api/cart', 480);
timings.set('/api/login', 180);
console.log(timings.size, timings.get('/api/login'));
const slow = [...timings].filter(([, ms]) => ms > 300).map(([url]) => url);
console.log('Slow:', slow.join(', '));

Output

2 180
Slow: /api/cart

Explanation: Map&lt;K, V&gt; types both keys and values. Setting an existing key replaces its value.

31. Set of unique tagsReal-world

Collect unique tags from several tests into a Set&lt;string&gt;.

Show solution
const testTags: string[][] = [['smoke', 'login'], ['regression', 'login'], ['smoke', 'cart']];
const unique = new Set<string>();
for (const tags of testTags) tags.forEach(t => unique.add(t));
console.log(unique.size, [...unique].sort().join(', '));

Output

4 cart, login, regression, smoke

Explanation: a typed Set stores each value once and only accepts the declared type.

Keep Practising

1 thought on “TypeScript Data Types Practice Programs with Solutions (31 Programs)”

Leave a Comment