Irbisa · Cheatsheet

TypeScript Type System

type vs interface, any, unknown and never, narrowing and type guards, structural typing, enums and literal unions, generics and constraints, keyof and typeof, mapped and conditional types, infer, declaration files

Middle Developer · 18 questions

  1. 01

    What is the difference between type and interface in TypeScript, and which do you use by default?

    Both describe object shapes and are interchangeable for most models and props.

    interface User {  id: string;  name: string;} // declaration merging: a second interface adds fields…
  2. 02

    What is the difference between any, unknown and never?

    any switches type checking off: anything can be assigned to it and you can do anything with it.

    const loose: any = JSON.parse('{"name":"Ada"}');loose.name.toUpperCase(); // compilesloose.nmae.toUpperCase(); // compiles too, throws at runtime const data: unknown = JSON.parse('{"name":"Ada"}');// data.name; // error: 'data' is of type 'unknown'…
  3. 03

    What happens to TypeScript types when the code runs, and why can you not write value instanceof User when User is an interface?

    They disappear: the compiler erases every annotation, interface and type alias, and the JavaScript that runs has no trace of them.

    interface User {  name: string;} class Account {  id: string;…
  4. 04

    What is type narrowing, and which checks does TypeScript narrow on?

    Narrowing is TypeScript following your runtime checks through the code: after if (typeof x === 'string'), x is a string inside the branch and the rest of the union after an early return.

    function format(value: string | number | Date | null): string {  if (value === null) return '';                  // string | number | Date  if (typeof value === 'string') return value.trim();  if (value instanceof Date) return value.toISOString();  return value.toFixed(2);                        // only number is left}…
  5. 05

    What are union and intersection types, and what do you get from A & B when both declare the same property with different types?

    A union A | B is a value of either type, so until you narrow you can use only what both have.

    type Id = string | number; function show(id: Id): string {  // id.toUpperCase(); // error: does not exist on type 'number'  return typeof id === 'string' ? id.toUpperCase() : id.toFixed(0);}…
  6. 06

    TypeScript is structurally typed: why does it accept the object with an extra label in the first call and reject it in the second?

    interface Point {  x: number;  y: number;} function draw(p: Point) {  console.log(p.x, p.y);} const p = { x: 1, y: 2, label: 'A' };draw(p);                          // okdraw({ x: 1, y: 2, label: 'A' }); // error

    Structural typing means a value fits a type if it has the required properties, whatever else it carries, so p passes.

  7. 07

    What are a type guard (value is T) and an assertion function (asserts value is T), and what is the risk of writing one?

    A type guard returns value is T: when it returns true, TypeScript narrows the argument to T in that branch.

    interface User {  id: string;  name: string;} function isUser(value: unknown): value is User {…
  8. 08

    Why do many teams avoid enum in TypeScript, and what do they use instead?

    An enum is one of the few TypeScript features that emits runtime code, and it behaves unlike the rest of the type system: numeric enums map values back to names, string enums reject plain literals.

    enum Direction {  Up,  Down,}console.log(Direction[0]);          // 'Up': numeric enums map values back to namesconsole.log(Object.keys(Direction)); // ['0', '1', 'Up', 'Down']…
  9. 09

    What does each of these type aliases resolve to, and what do keyof, typeof and indexed access T[K] do here?

    const config = {  api: 'https://example.com',  retries: 3,  features: ['search', 'chat'],};const roles = ['admin', 'editor'] as const; type Config = typeof config;type ConfigKey = keyof Config;type Retries = Config['retries'];type Feature = Config['features'][number];type Role = (typeof roles)[number];type Dict = keyof { [key: string]: boolean };

    Config is { api: string; retries: number; features: string[] }, ConfigKey is 'api' | 'retries' | 'features', Retries is number, Feature is just string, Role is 'admin' | 'editor', and Dict is string | number.

  10. 10

    How do you write a generic getProperty(obj, key) whose return type follows the key, and what does K extends keyof T mean?

    Give the function two type parameters, T for the object and K extends keyof T for the key, and return T[K].

    function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {  return obj[key];} const user = { name: 'Ada', age: 36, admin: false };…
  11. 11

    What are function overloads in TypeScript, and when are they better than a single signature with a union parameter?

    Overloads are several call signatures written above one implementation: callers see only the overloads, and the implementation must handle all of them.

    interface User {  id: string;  name: string;}declare function fetchUser(id: string): Promise<User>;…
  12. 12

    What is a .d.ts file, and how do you add types for an untyped package or add your own property to Window?

    A .d.ts file holds only type declarations and no code: it describes JavaScript that exists elsewhere.

    // src/types/globals.d.tsexport {}; // a module, so global additions go inside declare global declare global {  interface Window {    analytics?: { track(event: string, props?: Record<string, unknown>): void };…
  13. 13

    What is a mapped type, and how would you implement Pick, Omit and a DeepPartial yourself?

    A mapped type builds an object type by looping over a union of keys: { [K in Keys]: T[K] }.

    type MyPick<T, K extends keyof T> = {  [P in K]: T[P];}; type MyOmit<T, K extends keyof T> = {  [P in keyof T as P extends K ? never : P]: T[P];…
  14. 14

    What are conditional types, and how does infer let you write your own ReturnType or unwrap a Promise?

    A conditional type `T extends U ?

    type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never; type UnwrapPromise<T> = T extends Promise<infer V> ? UnwrapPromise<V> : T; type ElementOf<T> = T extends readonly (infer E)[] ? E : never;…
  15. 15

    What are template literal types, and how would you derive handler names like onClick from an event name like click?

    Template literal types build string literal types with backtick syntax: `on${Capitalize<E>}` turns 'click' into 'onClick', and a union expands into every combination.

    type EventName = 'click' | 'focus' | 'change'; type HandlerName<E extends string> = `on${Capitalize<E>}`;type Handlers = HandlerName<EventName>; // 'onClick' | 'onFocus' | 'onChange' type HandlerProps<T> = {…
  16. 16

    What do these four aliases resolve to, and why does wrapping both sides of extends in square brackets change the result?

    type ToArray<T> = T extends unknown ? T[] : never;type ToArrayWhole<T> = [T] extends [unknown] ? T[] : never; type A = ToArray<string | number>;type B = ToArrayWhole<string | number>;type C = ToArray<never>;type D = ToArrayWhole<never>;

    A is string[] | number[], B is (string | number)[], C is never and D is never[].

  17. 17

    How do you make two string ids such as UserId and OrderId incompatible in TypeScript (branded types)?

    Intersect the base type with a marker property no real value has: type UserId = string & { readonly [brand]: 'UserId' }.

    declare const brand: unique symbol;type Brand<T, Name extends string> = T & { readonly [brand]: Name }; type UserId = Brand<string, 'UserId'>;type OrderId = Brand<string, 'OrderId'>;…
  18. 18

    What are covariance and contravariance, and why does TypeScript check method parameters more loosely than function-typed properties?

    Covariance keeps the direction of subtyping: a Dog[] works as an Animal[].

    interface Animal { name: string }interface Dog extends Animal { bark(): void }interface Cat extends Animal { meow(): void } type PropHandler = { handle: (animal: Animal) => void }; // property: checked strictlytype MethodHandler = { handle(animal: Animal): void };   // method: bivariant…