TypeScript Interview Questions for Frontend Development
25 TypeScript questions with answers for frontend development roles — 5 easy, 14 medium, 6 hard.
About TypeScript
TypeScript is now standard in most frontend teams. You will revise type versus interface, any versus unknown, narrowing, generics, utility types, discriminated unions, strict compiler options, declaration files and the satisfies operator, plus real scenarios like migrating a JavaScript codebase and validating API data at runtime.
Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
TypeScript is a superset of JavaScript that adds static types and compiles to plain JavaScript. It catches errors such as wrong property names, missing arguments and null access while you type, before the code runs. It also improves editor autocomplete and makes refactoring large codebases safer. Types are erased at runtime, so TypeScript adds no performance cost but needs a build or type-stripping step.
02
What is the difference between type and interface in TypeScript?
Both describe object shapes. An interface can be extended with extends and merged by declaring it twice, which libraries use to let users augment types. A type alias can describe anything, including unions, tuples, primitives and mapped types, and combines with intersections. For plain object shapes either works; many teams use interface for public object contracts and type for unions and computed types.
A union type A | B means the value can be either type, such as string | number or 'success' | 'error'. You must narrow it before using type-specific members. An intersection type A & B combines all properties of both, for example User & { permissions: string[] } adds a field to an existing type. Unions model alternatives, while intersections model composition of features.
04
Explain the utility types Partial, Pick, Omit and Record.
Partial<T> makes all properties optional, useful for update payloads. Pick<T, 'id' | 'name'> creates a type with only selected keys, and Omit<T, 'password'> removes keys, handy for public user objects. Record<K, V> builds an object type with keys K and values V, such as Record<'en' | 'hi', string> for translations. Others include Required, Readonly, ReturnType and Awaited.
05
How would you learn TypeScript quickly if your new project required it?
Situation: I joined an internship where the codebase was fully TypeScript, which I had never used. Task: I needed to contribute within the first week. Action: I read the official handbook basics, studied existing types in the project, and converted small JavaScript exercises into TypeScript each evening. Result: I submitted my first typed component on day five and became comfortable with generics within a month.
any turns off type checking completely, so errors slip through silently. unknown also accepts any value, but you must narrow it with typeof, instanceof or a type guard before using it, which makes it the safe choice for API responses and catch errors. never represents values that never occur, such as a function that always throws, or the leftover case in an exhaustive switch over a union.
Narrowing is how TypeScript refines a broad type to a specific one inside a code branch. It understands typeof checks, instanceof, the in operator, equality checks, truthiness and discriminant properties. For example, inside if (typeof value === 'string') you can call value.toUpperCase(). Custom type guard functions returning value is User extend this to your own checks, and assertion functions narrow after they run.
Generics let you write reusable code that keeps type information, using type parameters like <T>. For example, function first<T>(items: T[]): T | undefined returns a number for a number array and a User for a User array without losing types. Constraints such as <T extends { id: string }> restrict what T can be. Generics power typed API clients, form libraries and reusable UI components.
TypeScript enums generate runtime JavaScript objects, and numeric enums accept any number, which weakens safety. String literal unions like type Status = 'draft' | 'published' have zero runtime cost, work naturally with JSON and give the same autocomplete. When you need a runtime list of values, an as const object plus a derived type works well. Many teams avoid enums, especially since type-stripping tools do not support them.
10
Which tsconfig options are important for a strict codebase?
strict: true enables a family of checks, including strictNullChecks and noImplicitAny, and should be on for new projects. noUncheckedIndexedAccess adds undefined to array and record lookups, catching many runtime errors. exactOptionalPropertyTypes, noImplicitOverride and noFallthroughCasesInSwitch add more safety. For bundler-based apps, moduleResolution: bundler, isolatedModules and verbatimModuleSyntax keep TypeScript aligned with how tools like Vite compile code.
11
What are declaration files (.d.ts) and when do you write them?
Declaration files describe the types of JavaScript code without containing implementation. Libraries ship them so TypeScript users get type checking, and many come from DefinitelyTyped as @types packages. You write your own when using an untyped library, declaring global variables injected by scripts, or typing non-code imports like declare module '*.svg'. Generating them with declaration: true is standard when publishing a TypeScript library.
12
Write a type-safe getProperty function using generics and keyof.
Constrain the key to the object's own keys:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const course = { title: 'Frontend', weeks: 14 };
const weeks = getProperty(course, 'weeks'); // number
// getProperty(course, 'price'); // compile error
T[K] is an indexed access type, so the return type matches the chosen property. Typos in key names are caught at compile time.
13
Model API request state with a discriminated union.
A shared status field lets TypeScript narrow each case:
type ApiState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; message: string };
function render(state: ApiState<string[]>) {
switch (state.status) {
case 'success': return state.data.join(', ');
case 'error': return 'Error: ' + state.message;
default: return 'Loading...';
}
}
Impossible states, like having both data and an error, cannot be represented.
Let callers specify the expected response type:
async function getJson<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
return res.json() as Promise<T>;
}
interface Course { id: number; title: string; }
const courses = await getJson<Course[]>('/api/courses');
courses[0].title.toUpperCase();
The cast trusts the server, so for untrusted data validate with a schema library like Zod before returning.
15
Write a custom type guard to check if a value is a User.
A function returning value is User narrows the type:
interface User { id: number; email: string; }
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null &&
'id' in value && typeof (value as User).id === 'number' &&
'email' in value && typeof (value as User).email === 'string';
}
const data: unknown = JSON.parse(raw);
if (isUser(data)) console.log(data.email);
Inside the if block, data is treated as User.
16
Create a type where all fields are optional except id.
First I check if types exist on DefinitelyTyped by installing @types/library-name. If not, I write a minimal declaration file, for example declare module 'old-chart-lib' with only the functions I use, in a types folder included by tsconfig. This is better than a blanket any import. If the library is important to the community, contributing those types upstream helps everyone.
18
Your team uses 'as any' everywhere to silence errors. How do you improve this?
I would first understand why: often the real types are unclear or the API response is untyped. I would add shared API types, introduce unknown with type guards for external data, and enable the ESLint rule no-explicit-any as a warning. Then I would fix the most risky areas first and block new any in CI. Showing a bug that types would have caught helps get team buy-in.
19
Tell me about a time you convinced your team to adopt a new tool like TypeScript.
Situation: our JavaScript app kept breaking after API changes. Task: I believed TypeScript would help but the team worried about slowing down. Action: I converted one small module as a pilot, showed three bugs it caught, and proposed gradual adoption with no deadline pressure. Result: the team agreed, we migrated over two quarters, and production errors related to wrong data shapes dropped significantly.
TypeScript compares types by their shape, not by their declared names. If an object has all the properties a type requires, it is compatible, even if it was never declared as that type. This is flexible but can let a UserId string be passed where an OrderId string is expected. Teams use branded types, such as string & { __brand: 'UserId' }, to simulate nominal typing for important identifiers.
satisfies checks that a value matches a type without changing the value's inferred type. With const routes = { home: '/', about: '/about' } satisfies Record<string, string>, TypeScript validates the shape but still knows the exact keys, so routes.contact is an error. A plain type annotation would widen the type and lose those specific keys. It is ideal for configuration objects and theme tokens.
22
Write a DeepReadonly type using mapped and conditional types.
I would migrate gradually rather than in one big rewrite. First add tsconfig with allowJs and checkJs off, rename entry files to .ts, and keep the build working. Then convert shared utilities and API types first, because many files depend on them, and enable strict for new files. I would track the number of any usages, add type checking to CI, and convert feature folders as they are touched.
24
TypeScript types say a field exists, but the app crashes because the API did not send it. How do you prevent this?
TypeScript only checks at compile time; it cannot guarantee what a server returns at runtime. I would validate responses at the boundary with a schema library like Zod or Valibot, deriving TypeScript types from the schema so they never drift. Invalid data then fails clearly with a logged error and a fallback UI. Generating types from OpenAPI specs also keeps frontend and backend contracts aligned.
25
Type checking takes several minutes in a large monorepo. How do you speed it up?
I would enable project references with composite builds so only changed packages are rechecked, and turn on incremental with build info files cached in CI. skipLibCheck avoids rechecking node_modules declarations. Very complex conditional types and huge unions slow the checker, so I would profile with --generateTrace and simplify hotspots. The bundler can use fast transpilation while type checking runs separately in parallel.
Frequently asked questions
How important is TypeScript in Frontend Development interviews?
TypeScript is now standard in most frontend teams. You will revise type versus interface, any versus unknown, narrowing, generics, utility types, discriminated unions, strict compiler options, declaration files and the satisfies operator, plus real scenarios like migrating a JavaScript codebase and validating API data at runtime.
How should I use the TypeScript sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.