What is TypeScript?
TypeScript is JavaScript with a type system on top. You write types, the compiler checks them, and then the types are removed. The browser runs plain JavaScript. So the whole benefit happens before the code runs.
38 questions - Beginner to intermediate
TypeScript questions come up in almost every Angular interview and most React ones. Interviewers want to see that you use types to catch real bugs, not that you learned the syntax by heart. Keep your answers concrete.
Last updated:
TypeScript is JavaScript with a type system on top. You write types, the compiler checks them, and then the types are removed. The browser runs plain JavaScript. So the whole benefit happens before the code runs.
It catches typos and wrong data shapes while you build, not in production. It gives real autocomplete and safe renaming in the editor. It also works as documentation that cannot go out of date. On a big team that saves a lot of time.
TypeScript works out the type by itself from the value you give. So you do not have to write types everywhere. Write types for function parameters and public return values. Let inference handle simple local variables.
let count = 5; // inferred as number
count = "five"; // Error
any switches all checking off, so use it only as a last resort. unknown is the safe version: it can hold anything, but you must narrow the type before you use it. never means a value that can never exist, like the return type of a function that always throws.
let a: any = 1; a.foo(); // allowed, may crash
let u: unknown = 1;
// u.toFixed(); // Error
if (typeof u === "number") u.toFixed(); // fine
With strictNullChecks on, null and undefined are not allowed in a normal type. You must write string | null and then check before use. That one flag stops most "cannot read property of undefined" crashes.
A union with | means the value is one of these types. An intersection with & means the value has all of these types joined together.
type Status = "idle" | "loading" | "error";
type Admin = User & { permissions: string[] };
A literal type allows one exact value only, like "GET". Put a few in a union and you get a safe list of options. That is usually better than an enum.
An enum creates real JavaScript at runtime and adds to the bundle. A union of string literals disappears when you compile and is easier to work with. Use a plain union, or const enum, unless you really need the runtime object.
A tuple is an array with a fixed length where every position has its own type. React useState returns a tuple: a value and a setter.
const point: [number, number] = [10, 20];
const pair: [string, boolean] = ["ok", true];
A question mark makes a property optional, so it can be missing. readonly stops you changing it after the object is made. Both exist only at compile time.
interface User {
id: number;
readonly createdAt: string;
nickname?: string;
}
Both describe the shape of an object. An interface can be opened again later and added to, which is called declaration merging, so it fits public object shapes and classes. A type alias can also describe unions, tuples and mapped types, which an interface cannot. Pick one style and stay with it.
interface Props { title: string; }
interface Props { subtitle?: string; } // merged, both exist
type Id = string | number; // interface cannot do this
TypeScript compares the shape of types, not their names. If an object has all the required properties with the right types, it fits. It does not have to say that it implements anything.
When you assign an object literal straight away, TypeScript complains about extra properties that are not in the type. This catches typos. If you assign through a variable first, that check is skipped, because then it only checks that the types are compatible.
Generics let one function or type work with many types, while keeping the link between input and output. The caller decides the real type. You get safety without writing the same code again.
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([1, 2, 3]); // number | undefined
const s = first(["a", "b"]); // string | undefined
A constraint limits what the generic type can be, using extends. You can then safely use those properties inside the function.
function getLength<T extends { length: number }>(x: T) {
return x.length;
}
keyof gives a union of the property names of a type. typeof, used in a type position, takes the type of a value that already exists. Together they let you write code that stays correct when the object changes.
const config = { host: "localhost", port: 8080 };
type Config = typeof config; // { host: string; port: number }
type Key = keyof Config; // "host" | "port"
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
Partial makes every property optional. Required does the opposite. Pick keeps only the keys you choose. Omit removes keys. Record builds a key-value map. Readonly locks properties. ReturnType takes the return type of a function.
type Draft = Partial<User>;
type Preview = Pick<User, "id" | "name">;
type Safe = Omit<User, "password">;
type Lookup = Record<string, User>;
A mapped type builds a new type by looping over the keys of another one. Partial and Readonly are written this way inside TypeScript.
type Nullable<T> = { [K in keyof T]: T[K] | null };
A conditional type picks between two types based on a check, using the extends ? : form. Helpers like ReturnType are built this way.
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type A = Unwrap<Promise<string>>; // string
Inside a conditional type, infer catches a part of the type and gives it a name, so you can return it. It is how you pull the item type out of an array, or the value out of a promise.
Narrowing is TypeScript cutting a union down to a smaller type after a check. typeof, instanceof, the in operator, a truthy check, or comparing a literal field all narrow the type.
function print(value: string | number) {
if (typeof value === "string") {
value.toUpperCase(); // string here
} else {
value.toFixed(2); // number here
}
}
It is a union of object types that all share one literal field, usually called kind or type. Checking that field narrows the value to exactly one member. It is the cleanest way to model API responses and Redux actions.
type Result =
| { status: "success"; data: User }
| { status: "error"; message: string };
function show(r: Result) {
if (r.status === "success") return r.data.name;
return r.message;
}
It is a function whose return type is written as "value is Type". When it returns true, TypeScript narrows the variable in the code that called it. It is useful for checking data that came from an API.
function isUser(x: unknown): x is User {
return typeof x === "object" && x !== null && "id" in x;
}
Writing "as Type" tells the compiler to trust you. Nothing is checked while the code runs. So if the data does not match, the crash comes later and far from the real cause. Prefer narrowing or a validation library.
It tells the compiler that a value is definitely not null or undefined. Like as, it is not checked at runtime. Use it only when you can prove it, for example right after a guard.
public is the default and is visible everywhere. private is visible only inside the class, and it is checked only at compile time. protected is visible in the class and its subclasses. The JavaScript # private field is enforced at runtime as well.
An abstract class cannot be created directly. It can hold some working methods and leave others empty for subclasses to fill in. Use it when several classes share real code. Use an interface when you only need a contract.
A decorator is a function that adds behaviour or extra information to a class, method or property, using the @ syntax. Angular is built on decorators such as @Component, @Injectable and @Input.
@Component({ selector: "app-user" })
export class UserComponent {
@Input() userId!: string;
}
Two declarations with the same name can be joined into one. Two interfaces merge, and a namespace can add to a class or a function. Libraries use it to extend types like the Express Request object.
It is a declaration file. It describes the types of some JavaScript code but holds no working code. It is how you add types to a library that has none, either from DefinitelyTyped as @types/name, or written by you.
It is the settings file for the compiler. It says which files to include, which JavaScript version to output, which module system to use, and how strict to be. When a build acts strangely, check this file first.
It turns on a group of flags, including strictNullChecks, noImplicitAny, strictFunctionTypes and strictPropertyInitialization. Turn it on from day one in a new project. In an old project, turn the flags on one at a time.
It makes the compiler complain when it cannot work out a type and would quietly use any. This happens most often on function parameters. It is the single most useful strict flag.
No. All types are removed when you compile. So data from an API or a form still needs a real check while the code runs, written by hand or with a validation library such as Zod.
A compile-time error is TypeScript refusing to build because the types do not fit. Nothing has run yet. A runtime error happens in the browser while the user is on the page. TypeScript helps only with the first kind, which is why checking input still matters.
satisfies checks that a value matches a type without widening the value to that type. You keep the exact literal types that were inferred, and you still get the error checking.
const routes = {
home: "/",
blog: "/blog",
} satisfies Record<string, string>;
// routes.home is "/" not just string
The return type is a Promise of the value it resolves to. TypeScript works it out on its own, but writing it makes the contract clear.
async function getUser(id: string): Promise<User | null> {
const res = await fetch("/api/users/" + id);
return res.ok ? ((await res.json()) as User) : null;
}
Add TypeScript with allowJs so both file types build. Rename a few files at a time, starting with shared utilities. Add types where they help most. Turn strict flags on one by one. Do not do a big rewrite in one go.
CrackInterviewAI listens to the live interview and gives a structured answer on screen for coding, system design, HR and project questions. Download for Windows.