πŸ“š Learning Hub
Β· 4 min read
Last updated on

10 TypeScript Interview Questions That Separate Juniors From Seniors


Some links in this article are affiliate links. We earn a commission at no extra cost to you when you purchase through them. Full disclosure.

TypeScript interviews aren’t about syntax β€” any developer can look that up. They test whether you understand the type system deeply enough to design safe, maintainable code. These 10 questions are the ones that reveal that understanding. (New to TypeScript? Start with What is TypeScript first.)

1. What’s the difference between type and interface?

Both define object shapes. The practical differences:

  • interface supports declaration merging (defining the same interface twice merges them)
  • type supports unions, intersections, and mapped types
  • interface can be extended with extends, type uses &

Senior answer: β€œI use interface for public API contracts (they’re extendable) and type for everything else (unions, utility types, complex compositions).β€œ

2. Explain type narrowing

Type narrowing is how TypeScript refines a broad type to a more specific one within a code block:

function process(input: string | number) {
  if (typeof input === 'string') {
    // TypeScript knows input is string here
    return input.toUpperCase();
  }
  // TypeScript knows input is number here
  return input.toFixed(2);
}

Narrowing works with: typeof, instanceof, in, equality checks, and custom type guards.

3. What are generics and when do you use them?

Generics let you write functions/types that work with any type while preserving type safety:

// Without generics β€” loses type info
function first(arr: any[]): any { return arr[0]; }

// With generics β€” preserves type
function first<T>(arr: T[]): T { return arr[0]; }

const num = first([1, 2, 3]);    // type: number
const str = first(['a', 'b']);    // type: string

When to use: When a function/class needs to work with multiple types but the relationship between input and output types matters. Generics come up constantly in React interview questions too, especially when typing custom hooks and higher-order components.

4. What’s the difference between any, unknown, and never?

  • any β€” disables type checking. Anything goes. Avoid it.
  • unknown β€” type-safe any. You must narrow it before using it.
  • never β€” represents values that never occur (exhaustive checks, functions that throw).
function handleValue(val: unknown) {
  // val.toUpperCase(); // ❌ Error β€” must narrow first
  if (typeof val === 'string') {
    val.toUpperCase(); // βœ… Narrowed to string
  }
}

function assertNever(x: never): never {
  throw new Error(`Unexpected value: ${x}`);
}

5. Explain utility types: Pick, Omit, Partial, Required

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

type UserPreview = Pick<User, 'id' | 'name'>;        // { id, name }
type UserWithoutEmail = Omit<User, 'email'>;          // { id, name, role }
type PartialUser = Partial<User>;                      // all fields optional
type RequiredUser = Required<Partial<User>>;           // all fields required again

Senior follow-up: β€œCan you build a custom utility type?β€œ

type Readonly<T> = { readonly [K in keyof T]: T[K] };

6. What’s a discriminated union?

A pattern where each type in a union has a common literal field that TypeScript uses for narrowing:

type Success = { status: 'success'; data: string };
type Error = { status: 'error'; message: string };
type Result = Success | Error;

function handle(result: Result) {
  switch (result.status) {
    case 'success': return result.data;    // TypeScript knows it's Success
    case 'error': return result.message;   // TypeScript knows it's Error
  }
}

This is the most important TypeScript pattern for modeling domain logic safely. Libraries like Zod use discriminated unions extensively for schema validation β€” see Zod vs Yup for how they compare.

7. How does as const work?

as const makes TypeScript infer the narrowest possible type:

const config = { api: '/users', method: 'GET' };
// type: { api: string; method: string }

const config = { api: '/users', method: 'GET' } as const;
// type: { readonly api: '/users'; readonly method: 'GET' }

Useful for: configuration objects, action types, and anywhere you want literal types instead of broad string/number types.

8. What’s the difference between extends in generics vs interfaces?

In interfaces, extends means inheritance:

interface Animal { name: string }
interface Dog extends Animal { breed: string }

In generics, extends means constraint:

function getLength<T extends { length: number }>(item: T): number {
  return item.length; // T must have a length property
}

9. How do you type async functions and Promises?

// Function that returns a Promise
async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

// Extracting the resolved type
type ResolvedUser = Awaited<ReturnType<typeof fetchUser>>; // User

10. What TypeScript config settings do you always enable?

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "exactOptionalPropertyTypes": true
  }
}

Why each matters:

  • strict β€” enables all strict checks (nullable, implicit any, etc.)
  • noUncheckedIndexedAccess β€” array access returns T | undefined instead of T
  • noImplicitReturns β€” every code path must return a value
  • exactOptionalPropertyTypes β€” undefined and β€œmissing” are different

The candidate who can explain why they enable these settings understands TypeScript at a senior level.


Related: TypeScript Cheat Sheet Β· TypeScript Complete Guide Β· React Interview Questions

Go deeper: Pluralsight has advanced TypeScript courses covering generics, conditional types, and real-world patterns. Start a free 10-day trial.

πŸ“˜