learn
TypeScript

Type challenges

Nine interview-level typing tasks with tests and hidden solutions.

Nine tasks interviewers actually assign. Each starts broken — make the tests green with zero type errors, no any, no ignore comments, no !. Then peek at one possible solution. All checks run in the page; nothing leaves your browser.

1. first of any array

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
function first(items: number[]): number {
return items[0];
}

2. cart reducer

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
type CartAction = { kind: "add"; amount: number } | { kind: "reset" };
function reduceCart(total: number, action: CartAction): number {
if (action.kind === "add") {
  return total + action.amount;
}
return total + action.amount;
}

3. safe display name

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
function displayName(raw: unknown): string {
const record = raw as { name: string };
return record.name;
}

4. typed event bus

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
type Handler = (message: string) => void;
class Bus {
private handlers: unknown[] = [];
on(handler: Handler): void {
  this.handlers.push(handler);
}
emit(message: string): void {
  for (const handler of this.handlers) {
    handler(message);
  }
}
}

5. partial from scratch

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
type MyPartial<T> = T;

6. rescue legacy total

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
function total(prices) {
let sum = 0;
for (const price of prices) {
  sum = sum + price;
}
return sum;
}

7. typed fetch

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
interface ApiUser {
id: number;
name: string;
}
async function getUser(id: number): Promise<ApiUser> {
const response = await fakeFetch("https://api.example.com/users/" + id);
return response;
}

8. rename without mutating

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
interface CartItem {
readonly id: number;
name: string;
price: number;
}
function rename(items: CartItem[], id: number, name: string): CartItem[] {
for (const item of items) {
  if (item.id === id) {
    item.name = name;
  }
}
return items;
}

9. exhaustive status labels

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
type Status = "new" | "paid" | "shipped";
function label(status: Status): string {
if (status === "new") {
  return "fresh order";
}
return "money in";
}

References

On this page