SyntaxStudy
Sign Up
TypeScript Partial, Required, Readonly, and Record
TypeScript Beginner 1 min read

Partial, Required, Readonly, and Record

TypeScript ships with a library of built-in generic utility types that transform existing types. They are implemented using mapped types and conditional types internally, so studying them is also a good way to learn those advanced features. Partial makes every property of T optional, and Required does the opposite — it makes every property required, removing all optional markers. Readonly makes all properties of T non-writable after initialisation. It is useful for enforcing immutability at the type level, particularly for function parameters you want to guarantee are not mutated. The Record utility creates an object type with keys of type K (which must extend string, number, or symbol) and values of type V. It is the idiomatic way to type a dictionary or lookup table. These four utility types appear constantly in real-world TypeScript code. Partial is ubiquitous in update functions and patch operations, where you want to accept only the fields being modified. Record is used for lookup tables, caches, and configuration maps. Readonly appears in functional programming patterns and in component prop types to prevent accidental mutation.
Example
interface Product {
    id: number;
    name: string;
    price: number;
    description: string;
}

// Partial — all properties optional (useful for PATCH operations)
function updateProduct(id: number, patch: Partial<Product>): void {
    console.log(`Updating product ${id}`, patch);
}
updateProduct(1, { price: 19.99 }); // only updating price

// Required — all optional properties become required
interface Config {
    host?: string;
    port?: number;
    debug?: boolean;
}
type FullConfig = Required<Config>;
// { host: string; port: number; debug: boolean }

// Readonly — prevent mutation
function processConfig(config: Readonly<FullConfig>): void {
    console.log(config.host);
    // config.host = "new"; // Error: cannot assign to readonly property
}

// Record — typed dictionary
type Locale = "en" | "fr" | "de" | "ja";
const translations: Record<Locale, string> = {
    en: "Hello",
    fr: "Bonjour",
    de: "Hallo",
    ja: "こんにちは",
};

// Record for a cache
type Cache<T> = Record<string, { value: T; expires: number }>;

function createCache<T>(): Cache<T> { return {}; }
const userCache = createCache<Product>();
userCache["user_1"] = { value: { id: 1, name: "A", price: 10, description: "" }, expires: Date.now() + 60000 };