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
What is the difference between
typeandinterfacein TypeScript, and which do you use by default?interface User { id: string; name: string;} // declaration merging: a second interface adds fields…What is the difference between
any,unknownandnever?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'…What happens to TypeScript types when the code runs, and why can you not write
value instanceof UserwhenUseris an interface?interface User { name: string;} class Account { id: string;…What is type narrowing, and which checks does TypeScript narrow on?
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}…What are union and intersection types, and what do you get from
A & Bwhen both declare the same property with different types?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);}…TypeScript is structurally typed: why does it accept the object with an extra
labelin 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' }); // errorWhat are a type guard (
value is T) and an assertion function (asserts value is T), and what is the risk of writing one?interface User { id: string; name: string;} function isUser(value: unknown): value is User {…Why do many teams avoid
enumin TypeScript, and what do they use instead?enum Direction { Up, Down,}console.log(Direction[0]); // 'Up': numeric enums map values back to namesconsole.log(Object.keys(Direction)); // ['0', '1', 'Up', 'Down']…What does each of these type aliases resolve to, and what do
keyof,typeofand indexed accessT[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 };How do you write a generic
getProperty(obj, key)whose return type follows the key, and what doesK extends keyof Tmean?function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key];} const user = { name: 'Ada', age: 36, admin: false };…What are function overloads in TypeScript, and when are they better than a single signature with a union parameter?
interface User { id: string; name: string;}declare function fetchUser(id: string): Promise<User>;…What is a
.d.tsfile, and how do you add types for an untyped package or add your own property toWindow?// 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 };…What is a mapped type, and how would you implement
Pick,Omitand aDeepPartialyourself?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];…What are conditional types, and how does
inferlet you write your ownReturnTypeor unwrap aPromise?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;…What are template literal types, and how would you derive handler names like
onClickfrom an event name likeclick?type EventName = 'click' | 'focus' | 'change'; type HandlerName<E extends string> = `on${Capitalize<E>}`;type Handlers = HandlerName<EventName>; // 'onClick' | 'onFocus' | 'onChange' type HandlerProps<T> = {…What do these four aliases resolve to, and why does wrapping both sides of
extendsin 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>;How do you make two string ids such as
UserIdandOrderIdincompatible in TypeScript (branded types)?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'>;…What are covariance and contravariance, and why does TypeScript check method parameters more loosely than function-typed properties?
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…