skip to content

TypeScript

4 roadmaps378 questionsupdated

The static type layer that sits on top of JavaScript: annotations and inference, interfaces and generics, unions and narrowing, utility and mapped types, classes and decorators, declaration files, and the compiler configuration that ties it together. Interviews lean on TypeScript because it exposes how precisely a candidate reasons about the shape of their data.

on this pageshow

guide

overview

~2 min

TypeScript interviews test how precisely you can describe data, and whether you know where that description stops. The opening questions sort candidates quickly: `any` against `unknown`, `interface` against `type`, what `strictNullChecks` changes. From there the round moves to unions and narrowing, where static types meet the runtime checks that justify them, and to generics, where interviewers separate candidates who can use a library's signatures from those who can design one. Senior rounds go to the type-level layer — mapped and conditional types, and when that cleverness costs more than it saves — and to the compiler configuration behind a build that passes on one machine and fails on another. Under all of it runs one question: what survives compilation, and what the checker simply takes on trust. The hub follows those seams. [Basic Types & Annotations](/topics/lang-typescript-basic-types) is the vocabulary: primitives and literals, arrays and tuples, the special types, enums and function signatures. [Interfaces & Type Aliases](/topics/lang-typescript-interfaces-types) covers object shapes and the structural rules that decide assignability. [Unions & Narrowing](/topics/lang-typescript-unions-narrowing) and [Generics](/topics/lang-typescript-generics) are the two ways a type stays precise while code stays general. [Utility & Mapped Types](/topics/lang-typescript-utility-mapped-types) is computing new types from existing ones. [tsconfig & Compilation](/topics/lang-typescript-tsconfig-compilation) is the compiler, module resolution and publishing, and [Classes & Decorators](/topics/lang-typescript-classes-decorators) is the class-shaped layer that decorator-heavy frameworks depend on. Learn it roughly in that order. Basic types and object shapes come first, because most later answers assume you read a type as a set of allowed values and compare types by shape. Narrowing follows, since a union is close to useless without it, then generics. Leave mapped and conditional types until generics feel routine: they are generics applied to types rather than values. Pick up the strictness flags early, because they change what the earlier answers mean. Module and build questions weigh most when the role owns a build or publishes packages, and classes and decorators when the team works in Angular or NestJS.

primer

### The types disappear at runtime TypeScript is a checker layered over JavaScript, and the emitted code carries almost none of it: interfaces, annotations, `private`, `readonly`, `implements` and assertions are all removed. That is why a program that type-checks can still crash, and why validating external data is a runtime job. The handful of constructs that do emit code — enums, namespaces, parameter properties, decorators — deserve precise answers, because they break the "just delete the types" mental model. ### Compatibility is decided by shape Two types are compatible when their members line up, whatever they are called and wherever they were declared. Most surprises follow from that rule: an object with extra fields passes where a smaller shape is expected, two identically shaped IDs are interchangeable, and a class instance satisfies an interface it never mentions. The excess property check on fresh object literals is a narrow exception, and branded types are the usual way to get nominal behaviour back. ### A type is a set of values `string` is every string, a literal type is one value, a union is the combined set and an intersection is the overlap. `unknown` holds everything and permits almost nothing until you check; `never` is empty; `any` is less a set than an exit from checking. Thinking in sets explains widening, why `string & number` collapses to `never`, and why "assignable to" reads as "a subset of". ### Narrowing turns a broad type into a usable one The checker follows control flow and shrinks a variable's type after each test: `typeof`, `instanceof`, `in`, equality, truthiness, a tag comparison on a discriminated union. Much of intermediate TypeScript is knowing which tests it recognises and where it gives up — it does not look inside the functions you call, and it can drop a narrowing on a reassignable variable captured by a callback. User-defined predicates, `as` and `!` extend the checker's knowledge on your word alone; nothing verifies them. ### Generics carry information through A type parameter exists to relate one position to another — argument to return value, collection to callback — so the caller's exact type survives the call. Constraints say what a parameter must support; inference fills it in from the arguments. A parameter that appears only once relates nothing, and when that one place is the return type it is a cast in disguise. ### Types can be computed from other types `keyof`, indexed access, mapped types, conditional types and template literal types derive one type from another, so a single declaration stays the source of truth. The built-in utilities are short definitions written in this same language. The same power turns into compile-time programs that are slow to check and hard to read, and senior interviewers ask where you would stop. ### The configuration is part of the language `strict` and its member flags decide whether `null` is checked, whether an implicit `any` is an error and how function parameters compare, so the same source means different things in two projects. Module resolution, `target` and `lib` decide what the checker believes about the runtime without changing that runtime. Many builds strip types without checking them, so a separate type-check step is what actually enforces the rules.

Structural typing
Deciding assignability by comparing members rather than declared names, so a value fits a type whenever it has the required properties, regardless of where its own type came from.
Literal type
A type with exactly one member value, such as one specific string or number; unions of them model a fixed set of allowed values.
Widening
Inference replacing a literal type with its general primitive, as it does for mutable bindings and object properties, unless an annotation or a const assertion keeps the literal.
Const assertion
The as const suffix on an expression, asking for the narrowest inference: literal values, readonly properties, and readonly tuples in place of arrays.
unknown
The top type: every value can be assigned to it, but a value of this type must be narrowed or asserted before use. The safe type for data from outside the program.
never
The empty bottom type; the type of code that cannot be reached and of a union once every member has been ruled out.
Narrowing
The checker's refinement of a variable to a more specific type within a branch, driven by control-flow analysis of type tests, comparisons and assignments.
Discriminated union
A union of object types that share a literal-typed tag property, so testing that single property selects one member of the union.
Type predicate
A return type of the form param is Type that lets a boolean function narrow its argument at call sites; the compiler takes the claim on trust.
Type parameter constraint
An extends clause on a type parameter that limits which types may be supplied and, in return, lets the body rely on what the constraint guarantees.
Contextual typing
Inference that flows from the expected type into an expression, such as an inline callback taking its parameter types from the function that receives it.
Mapped type
A type that iterates over a union of keys, typically keyof another type, producing one property per key with its modifiers and value type transformed.
Conditional type
A type that picks one of two branches by testing assignability, written like a ternary; combined with infer it can extract parts of the tested type.
Distributive conditional type
A conditional type whose checked type is a bare type parameter, so a union argument is split and each member is tested on its own.
Declaration file
A .d.ts file holding only type information about JavaScript that exists elsewhere; shipped with libraries and written for untyped packages and globals.
Declaration merging
The compiler combining several same-named declarations, such as two interfaces, into one definition; the mechanism behind augmenting library and global types.
Parameter property
A constructor parameter marked public, private, protected or readonly, which the compiler turns into a class field and assigns automatically; one of the few features that emits code.
Variance
How subtyping between type arguments carries over to the generic type built from them: covariant, contravariant, invariant or bivariant.

The sections stack. Basic types and object shapes define what a single value may be; unions and narrowing let one variable hold several of those shapes safely; generics let one function or type work across all of them without losing precision. [Utility & Mapped Types](/topics/lang-typescript-utility-mapped-types) sits on top: a mapped type is a generic over keys, a conditional type is a generic that branches, and the utilities used every day are built from both. That is why [Conditional Types](/topics/lang-typescript-utility-mapped-types-conditional-types) questions keep circling back to unions, and why [Variance & Assignability](/topics/lang-typescript-generics-variance) is structural typing asked about type arguments. Two sections run across the others rather than above them. [tsconfig & Compilation](/topics/lang-typescript-tsconfig-compilation) sets the rules every other answer assumes — whether `null` belongs to `string`, whether an unannotated parameter is an error — and owns the boundary with JavaScript: declaration files, migration and the build tools that strip types. [Classes & Decorators](/topics/lang-typescript-classes-decorators) is where the erased type layer and the runtime meet most visibly: some modifiers vanish on emit, others generate fields or calls, and decorators exist in two incompatible generations. A small example where several core ideas meet: ```ts type UiEvent = | { kind: 'click'; x: number; y: number } | { kind: 'key'; code: string } | { kind: 'scroll'; delta: number }; type Handlers = { [E in UiEvent as E['kind']]: (e: E) => void }; function dispatch(e: UiEvent, on: Handlers): void { switch (e.kind) { case 'click': return on.click(e); case 'key': return on.key(e); case 'scroll': return on.scroll(e); } } ``` The union is a set of three shapes, and `kind` is its discriminant. `Handlers` is computed from it: the mapped type walks the union, renames each key to that member's tag and pairs it with a handler for exactly that member, so the union stays the single source of truth. Inside `dispatch`, each `case` narrows `e`, which is why handing it to the matching handler type-checks. Add a fourth member and every `Handlers` object stops compiling until it supplies the new handler — and of all this, only `dispatch` and its `switch` reach the emitted JavaScript.

  1. Basic Types & Annotations →

    Primitives, literals, tuples and the special types are the vocabulary every other section is written in; any versus unknown is where most screens begin.

  2. Interfaces & Type Aliases →

    Object shapes and structural assignability come next; many later answers compare two types by their members.

  3. Unions & Narrowing →

    Unions are how real data is modelled, and narrowing is what makes them usable; discriminated unions recur across the rest of the hub.

  4. Generics →

    With shapes and unions in place, learn how type parameters, constraints and inference keep a reusable function precise.

  5. Utility & Mapped Types →

    Type-level transformation is generics applied to types; start from the built-in utilities before writing mapped and conditional types yourself.

  6. tsconfig & Compilation →

    Strictness flags, module resolution and declaration files explain the build failures interviewers describe, and matter most once you own a project.

  • Reaching for any to quiet an error: it switches checking off for everything the value flows into, while unknown would have forced the check the code was missing.

  • Treating a type annotation on parsed JSON or an API response as validation: the checker believes it, nothing at runtime tests it, and bad data fails far from where it entered.

  • Using as or the non-null ! as a fix rather than a claim: both are assertions the compiler accepts on trust, so a wrong one becomes a runtime error.

  • Writing a user-defined guard whose body does not match its predicate: callers narrow on it anyway, and the mismatch is invisible to the checker.

  • Expecting private or readonly to protect data at runtime: both are checker rules only; reach for # fields or freezing when the runtime itself must enforce it.

  • Adding a type parameter that appears only in the return type: the caller picks it freely, so the signature promises a type that no code checks.

  • Believing paths or lib changes the output: both only inform the checker, so the build passes and the program fails on an unresolved import or a missing built-in.

  • Taking a bundler's successful build as proof of type safety: tools that strip types file by file never run the checker, so type-checking needs its own step in CI.

  • Building deep recursive or conditional types where a plain union would do: they slow the checker, produce unreadable errors and are hard for teammates to change.

This guide assumes a TypeScript 5.x compiler with `strict` turned on. The language grows through minor releases, and interviewers still ask about the ones that changed everyday code: - **2.0** — `strictNullChecks`, the `never` type and discriminated (tagged) unions. - **2.1** — `keyof`, indexed access types and mapped types, with `Partial`, `Readonly`, `Pick` and `Record`. - **2.8** — conditional types and `infer`, which made `ReturnType`, `Exclude` and their relatives possible. - **3.0** — the `unknown` type and project references. - **3.4** — `as const`. - **3.7** — optional chaining, nullish coalescing and assertion functions. - **3.8** — `import type` and support for ECMAScript `#private` fields. - **4.1** — template literal types and key remapping with `as` in mapped types. - **4.9** — the `satisfies` operator. - **5.0** — standard decorators alongside the legacy `experimentalDecorators` mode, `const` type parameters, `verbatimModuleSyntax` and the `bundler` resolution mode. - **5.4** — the `NoInfer` utility, and narrowing kept inside closures created after a variable's last assignment. - **5.5** — type predicates inferred from function bodies. Material written before 5.0 describes decorators with the legacy signature only, and material from before 2.0 assumes `null` fits every type. When an article disagrees with the compiler, check which era it was written for and which flags it assumed.

Microsoft makes the compiler and the language service that many editors use for TypeScript and JavaScript alike. Interviewers expect you to place it in three settings. On the **frontend**, it is the usual language for React, Angular and Vue codebases, and Angular is written in it and designed around it. On the **backend**, it runs on Node.js, Deno and Bun, and frameworks such as NestJS lean on its classes and decorators. In **libraries**, shipping declaration files is expected whether the source is TypeScript or not, and the community-maintained DefinitelyTyped repository supplies `@types` packages for libraries that ship none. The toolchain splits checking from transpiling. `tsc` does both, but many projects hand transpiling to faster tools such as esbuild, SWC or Babel, which remove types without checking them, and some runtimes execute TypeScript files directly in the same way. The comparisons that come up are with **JavaScript plus JSDoc**, which the same compiler can check without a build step at the cost of wordier annotations, and with **Flow**, an earlier static checker for JavaScript that TypeScript has largely displaced. Runtime validators such as Zod sit beside TypeScript rather than against it: they check data at the boundary and derive the static type from the same schema.

explore

report an issue with this guide →

questions

378 · 7 sections

In TypeScript, is `string[]` a different type from `Array<string>`, and which of the two forms can you write `readonly` in front of?

level: juniorimportance: must knowfreq 72%
basics
~20 s

string[] and Array<string> are the same type — the bracket form is shorthand for the generic one. They differ only in syntax: readonly attaches to the bracket form (readonly string[]); the generic spelling is ReadonlyArray<string>.

open as a page

In TypeScript, what does the annotation `[string, number]` guarantee about a value that the annotation `(string | number)[]` does not?

level: juniorimportance: must knowfreq 68%
basics
~20 s

A tuple type fixes both the length and the type of each position: [string, number] accepts exactly two elements, a string then a number. (string | number)[] accepts any number of elements, in any order.

open as a page

In TypeScript, given `enum Direction { Up = 1, Down, Left, Right }`, what value does each member hold — and why does `enum Level { Low = 'LOW', High }` fail to compile?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Numeric enum members auto-increment from the previous value, so Up = 1 makes Down 2, Left 3 and Right 4. String members never auto-increment, so any member following a string member must have its own explicit initializer.

open as a page

In TypeScript, what does the question mark do in `function log(msg: string, level?: string)`, and what rules govern where such a parameter may appear?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A question mark makes the parameter optional: callers may omit it, and inside the body its type includes undefined. Optional parameters must follow all required ones, and a parameter cannot have both a question mark and a default value.

open as a page

In TypeScript, the function `function add(a, b) { return a + b; }` is rejected under the `noImplicitAny` compiler option, yet the same function needs no return type annotation at all. Why does the compiler treat parameters and return types so differently?

level: juniorimportance: must knowfreq 82%
basics
~20 s

TypeScript infers a return type from the function body, but nothing tells it what callers will pass, so unannotated parameters fall back to the implicit any type, which noImplicitAny rejects. Annotate the inputs; let the body infer the output.

open as a page

TypeScript checks type compatibility structurally rather than nominally. What does that mean for a function whose parameter is typed `interface Point { x: number; y: number }` — which values will the compiler accept?

level: juniorimportance: must knowfreq 78%
basics
~20 s

TypeScript compares types by shape, not by declared name. Any value whose type has x and y of type number is accepted for a Point parameter, no matter how it was declared, and extra members do not disqualify it.

open as a page

In TypeScript, what does the type `(id: number) => Promise<User>` describe, and must the implementing function name its parameter `id`?

level: juniorimportance: must knowfreq 72%
basics
~20 s

That type describes any function taking one number argument and returning a Promise of User. Parameter names inside a function type are labels for readers only — matching is positional, so an implementation may name the parameter anything.

open as a page

In TypeScript, which kinds of type can a `type` alias express that an `interface` declaration cannot, and how should that shape your choice between the two?

level: juniorimportance: must knowfreq 85%
basics
~20 s

A type alias can name any type — unions, tuples, primitives and types derived from other types — while an interface declares an object type only. Reach for an alias whenever the type is not a plain object shape.

open as a page

In TypeScript, a helper is supposed to receive the `Person` class itself, but `function make(ctor: Person)` rejects the argument `Person`. What does the type `Person` actually refer to, and how should the parameter be typed?

level: juniorimportance: must knowfreq 55%
basics
~20 s

A class declaration creates two separate things: a type named Person that describes instances, and a value named Person that is the constructor. A parameter receiving the class itself must be typed typeof Person, or new (name: string) => Person.

open as a page

In TypeScript, what happens when the same interface name is declared twice in one scope, and what happens when the same `type` alias name is declared twice?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Two interface declarations sharing a name in the same scope merge into one type carrying both sets of members. Repeating a type alias name is a duplicate-identifier error instead: interfaces are open, aliases are closed.

open as a page

In TypeScript, `function longest<T>(a: T, b: T) { return a.length >= b.length ? a : b; }` does not compile. Why does the checker reject `a.length`, and what does changing the signature to `<T extends { length: number }>` change?

level: juniorimportance: must knowfreq 76%
basics
~20 s

An unconstrained T has no known members, so a.length is an error: "Property 'length' does not exist on type 'T'". Adding <T extends { length: number }> promises every T the caller picks has a numeric length, so the body may read it.

open as a page

In TypeScript, given `declare function get<T, K extends keyof T>(obj: T, key: K): T[K]`, what type does `get({ id: 1, name: 'ada' }, 'name')` produce, and what is the `K extends keyof T` constraint doing?

level: juniorimportance: must knowfreq 68%
basics
~20 s

It produces string. keyof T is the union of T's property names, so K is constrained to those names and infers the literal 'name' at this call; the return type T[K] then resolves to that one property's type.

open as a page

In TypeScript, what does the `= unknown` in a declaration like `interface ApiResult<T = unknown>` do, and when does the compiler actually apply that default?

level: juniorimportance: must knowfreq 55%
basics
~20 s

A type-parameter default supplies the type argument when the caller omits it, so writing ApiResult with no argument means ApiResult<unknown>. TypeScript also falls back to the default in a generic call when inference finds no candidate for that parameter.

open as a page

In TypeScript, what does declaring `function identity<T>(x: T): T` give you that `function identity(x: any): any` does not?

level: juniorimportance: must knowfreq 84%
basics
~20 s

A type parameter links the argument type to the return type, so the call site keeps the concrete type it passed in and stays type-checked. any turns checking off instead: the result is unchecked and any misuse of it silently compiles.

open as a page

Given the TypeScript declaration `interface ApiResponse<T> { data: T; status: number }`, what does the `<T>` declare, and what is the type of `res.data` when `res` is annotated `ApiResponse<User[]>`?

level: juniorimportance: must knowfreq 72%
basics
~10 s

The <T> declares a type parameter: a placeholder the user of the interface fills in. Writing ApiResponse<User[]> substitutes User[] for T everywhere in the declaration, so res.data has type User[] while status stays number.

open as a page

In TypeScript, given `type Dir = "up" | "down" | "left"` and `function move(d: Dir)`, what is d's type inside `if (d === "up") { ... } else { ... }`, and what does the compiler do if you write `d === "top"`?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Equality against a literal narrows both branches: d is "up" inside the if and "down" | "left" in the else. Comparing d to "top" is a compile error, because the two types have no overlap and the test could never be true.

open as a page

In TypeScript, given `function fmt(x: string | number)`, what type does the checker give `x` inside `if (typeof x === 'string')` and in the `else` branch, and which strings is the compiler willing to accept on the right-hand side of a `typeof` comparison?

level: juniorimportance: must knowfreq 78%
basics
~20 s

TypeScript narrows x to string inside the if branch and to number in the else branch. Only the eight strings typeof can actually return are accepted; comparing against any other string is reported as a comparison with no overlap.

open as a page

In TypeScript, `const greeting = 'hi'` is inferred as the literal type `'hi'`, but `let greeting = 'hi'` is inferred as `string`. Why do the two differ, and how do you keep a literal type on a mutable variable?

level: juniorimportance: must knowfreq 72%
basics
~20 s

TypeScript widens a literal only for mutable bindings: const greeting = 'hi' keeps the literal type 'hi', while let greeting = 'hi' widens to string since it can be reassigned. Annotate the let to keep a literal type.

open as a page

In TypeScript, what does the postfix `!` in `const label = user.name!` tell the compiler, and what does that line compile to in the emitted JavaScript?

level: juniorimportance: must knowfreq 72%
basics
~20 s

TypeScript's postfix ! is the non-null assertion operator: it strips null and undefined from that expression's type and is then erased. The emitted JavaScript contains no check, so a wrong assertion still throws at runtime.

open as a page

In TypeScript, a dispatch over a discriminated union ends with `default: throw new Error('unhandled kind')`. What does replacing that throw with a call to a helper declared `function assertNever(value: never): never` buy you?

level: juniorimportance: must knowfreq 55%
basics
~20 s

A plain throw only fires at runtime. Passing the value to a parameter typed never makes the compiler check that branch too, so an unhandled union member becomes a build error rather than a production surprise.

open as a page

In TypeScript, what do the built-in utility types `Exclude<T, U>`, `Extract<T, U>` and `NonNullable<T>` produce when you apply them to a union type?

level: juniorimportance: must knowfreq 72%
basics
~10 s

They are set operations over unions. Exclude<T, U> drops every member of T assignable to U, Extract<T, U> keeps only those members, and NonNullable<T> removes null and undefined from T.

open as a page

In TypeScript, a function `createUser` returns an object literal whose shape you never declared. How do you get a named type for that return value without writing the shape twice, and why does `ReturnType<createUser>` fail to compile?

level: juniorimportance: must knowfreq 72%
basics
~10 s

ReturnType<typeof createUser> gives the type. In a type position, typeof turns the value createUser into its function type, which ReturnType then unwraps. ReturnType<createUser> fails because createUser is a value, not a type.

open as a page

In TypeScript, what does each of the built-in utility types `Partial<T>`, `Required<T>` and `Readonly<T>` do to the properties of T?

level: juniorimportance: must knowfreq 78%
basics
~20 s

Partial<T> makes every property of T optional, Required<T> makes every property mandatory, and Readonly<T> makes every property read-only. All three keep T's keys and property types, flip one modifier, and disappear when the code is compiled to JavaScript.

open as a page

In TypeScript, what do `Pick<T, K>` and `Omit<T, K>` produce, and why derive a type with them instead of hand-writing a second interface?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Pick<T, K> builds an object type containing only the properties of T named in K; Omit<T, K> contains every property of T except those. Deriving keeps one source of truth, so editing T updates both types automatically.

open as a page

In TypeScript, what object type does the built-in `Record<K, V>` utility produce, and how does its mapped-type definition explain the difference between `Record<string, number>` and `Record<'a' | 'b', number>`?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Record<K, V> builds an object type whose keys come from K and whose values are all V. It is defined as the mapped type { [P in K]: V }, so a string key produces an open index signature while a union of literals produces exactly those required properties.

open as a page

In a TypeScript library's tsconfig.json, what does the `declaration` compiler option emit, and why does a package that already ships compiled JavaScript still need that output?

level: juniorimportance: must knowfreq 70%
basics
~20 s

The declaration option makes tsc emit .d.ts files next to the JavaScript. Compilation erases types, so the .js carries none; the .d.ts is the type-only mirror that gives consumers of the published package autocomplete and type checking.

open as a page

In a tsconfig.json, what does the TypeScript compiler option `allowJs` do on its own, and what changes when you also turn on `checkJs`?

level: juniorimportance: must knowfreq 58%
basics
~20 s

allowJs admits .js and .jsx files into the TypeScript program so they are resolved, inferred from and emitted — but no errors are reported in them. checkJs additionally turns the type checker on for those JavaScript files.

open as a page

In TypeScript, when you write `import _ from 'lodash'`, where does the compiler look for that package's type declarations, and what does the error "Could not find a declaration file for module 'lodash'" mean?

level: juniorimportance: must knowfreq 62%
basics
~20 s

The compiler looks inside the package first — package.json's types or typings field, or a types condition in its exports map — then falls back to node_modules/@types/lodash. If neither exists, the import carries no type information.

open as a page

In a tsconfig.json, what does the `target` compiler option control and what does the `module` option control, and why can you not use one in place of the other?

level: juniorimportance: must knowfreq 66%
basics
~20 s

In tsconfig, target sets the language level of the emitted JavaScript syntax, deciding whether things like async/await or optional chaining get rewritten. module sets the output module format, such as CommonJS require and exports or untouched ESM. The two are independent.

open as a page

In TypeScript, what does `import type { User } from './models'` mean, and how does it differ from a plain `import { User } from './models'`?

level: juniorimportance: must knowfreq 68%
basics
~20 s

import type declares the binding as types only: TypeScript always erases that statement from the emitted JavaScript, and the name cannot be used as a value. A plain import is erased only when the compiler decides nothing in the file uses it at runtime.

open as a page

In TypeScript, what does the `abstract` modifier mean when it is written on a class and when it is written on one of that class's members, and which errors does the compiler raise around it?

level: juniorimportance: must knowfreq 72%
basics
~20 s

abstract on a class blocks creating instances of that class directly. abstract on a member declares a signature with no body that every concrete subclass must supply. TypeScript rejects instantiating the class and rejects a non-abstract subclass that leaves a member unimplemented.

open as a page

In a TypeScript class, what do the `public`, `private` and `protected` modifiers each control, and which code is allowed to touch a member marked with each?

level: juniorimportance: must knowfreq 70%
basics
~20 s

public, the default, allows access from anywhere. private limits access to the body of the declaring class. protected allows the declaring class and its subclasses. All three are checker-only rules that disappear when TypeScript emits JavaScript.

open as a page

In TypeScript, what is the difference between `class Duck implements Bird` and `class Duck extends Bird` — what does each clause change about type checking and about the emitted JavaScript?

level: juniorimportance: must knowfreq 78%
basics
~20 s

extends is real inheritance: the base class's members come along and the clause survives into the emitted JavaScript. implements is a compile-time-only conformance check that copies nothing and is erased. A class may implement many interfaces but extend only one class.

open as a page

TypeScript's types are erased before the code runs, so how can a validation or ORM library that uses decorators know at runtime that a class property is supposed to hold a string?

level: juniorimportance: must knowfreq 52%
basics
~20 s

The annotation itself is gone after compilation. Decorators are ordinary functions that run when the class is defined, so the library records what it needs in a runtime data store at that moment — either from an explicit argument or from compiler-emitted metadata.

open as a page

In TypeScript, what does `constructor(private readonly repo: UserRepo) {}` declare on the class, and how is that different from `constructor(repo: UserRepo) {}`?

level: juniorimportance: must knowfreq 68%
basics
~20 s

An accessibility or readonly modifier on a constructor parameter makes it a parameter property: TypeScript declares a matching class member and assigns the argument to it. Without a modifier the parameter stays an ordinary constructor-local.

open as a page
TypeScript interview questions & primer · KataJob