skip to content

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%

answer

  1. one modifier flipped, keys unchanged
  2. optionality and mutability, nothing else
  3. Partial adds ?, Required strips it
  4. Readonly blocks assignment at compile time
  5. shallow, and nothing is emitted

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.

solid answer

~40 s

All three are mapped types over `keyof T`: they keep the same keys and the same property types and only flip a modifier. `Partial<T>` adds `?` to every property, which is why it is the usual type for a PATCH body or an options bag you merge over defaults. `Required<T>` does the opposite — it strips `?`, and where a property was optional it also drops `undefined` from that property's type. `Readonly<T>` marks every property `readonly`, so assigning to one is a compile error. Two things I would add without being asked: they are all one level deep, so nested objects are untouched, and none of them emits anything — `Readonly` is a rule the checker enforces, not a runtime freeze.

code

typescript · 15 lines
typescript
interface User {
  id: number;
  name?: string;
}

type P = Partial<User>;  // { id?: number; name?: string }
type R = Required<User>; // { id: number; name: string }
type O = Readonly<User>; // { readonly id: number; readonly name?: string }

const patch: P = {};
const full: R = { id: 1, name: "ada" };
const frozen: O = { id: 1 };

// @ts-expect-error id is read-only
frozen.id = 2;

go deeper

for a junior

Know what each of the three does in one sentence, and be able to name a use for Partial (an update payload or an options bag). Say plainly that they change types only and produce no JavaScript.

for a middle

Explain that they are mapped types over keyof T that copy each property type unchanged and flip one modifier, and volunteer the shallowness limit before you are asked about it.

for a senior

Show where these belong in a real pipeline: a partial options type at the public boundary, a required type after defaults are resolved, a read-only type for anything shared. Be honest about what the loose Partial contract lets through.

for a principal

Own the argument about how much of a data contract belongs in these generic transforms versus hand-written types. Blanket Partial and Required are convenient but say little about intent; be ready to justify where your codebase draws that line.

## The one-line definitions All three ship in TypeScript's own `lib.es5.d.ts`, and each is a mapped type over the keys of `T`: ```typescript type Partial<T> = { [P in keyof T]?: T[P] }; type Required<T> = { [P in keyof T]-?: T[P] }; type Readonly<T> = { readonly [P in keyof T]: T[P] }; ``` Read one left to right: for every key `P` in `keyof T`, produce a property whose type is `T[P]` — the original property type, copied verbatim — and change exactly one modifier. Nothing else about the type moves. That is the whole idea, and it is why interviewers treat these three as one question rather than three. Because the key list is literally `keyof T`, these are *homomorphic* mapped types, which is jargon for "same shape as the source". Two useful consequences follow. First, modifiers the utility does not touch are preserved: `Partial<T>` keeps a property that was already `readonly` read-only. Second, they distribute over a union, so `Partial<A | B>` is `Partial<A> | Partial<B>` rather than a flattened mess. ## Partial<T> `Partial<T>` says "any subset of these properties, or none of them". Its natural home is an update payload: a function that patches a stored record accepts `Partial<Record>` because the caller sends only the fields being changed. It is equally the type of an options object you merge over a set of defaults, where every option has a fallback. The cost is that `{}` is a valid `Partial<T>` for any `T`. The type expresses "everything is optional" and cannot express "at least one of these", so it is a loose contract by construction. ## Required<T> `Required<T>` removes the optional marker from every property. It is the type you reach for after a resolution step: a config object that arrives with everything optional, merged with defaults, comes out the other side as `Required<Config>` — a single value where every field is guaranteed present, so downstream code stops writing `?.` and `??` on every access. One subtlety worth knowing: stripping `?` from a property that *was* optional also removes `undefined` from that property's type, so `a?: number` becomes `a: number`. A property that was never optional keeps whatever its declared type was. ## Readonly<T> `Readonly<T>` marks each property `readonly`, so `value.prop = x` becomes the compile error "cannot assign to a read-only property". Use it on function parameters to document and enforce that a function does not mutate what it was handed, and on module-level constants and shared lookup tables. What `readonly` blocks is *assignment to the property*, exactly like `const` blocks reassignment of a binding. It does not make the value behind the property immutable, and it is not enforced anywhere but in the checker. ## They are shallow, and they are erased Each utility maps over the *top-level* keys of `T` and copies each property type unchanged, so a nested object type is passed through untouched by all three. That is the single most common follow-up. And because a type annotation carries no runtime weight in TypeScript, none of these three emits code. A value typed `Readonly<Config>` is an ordinary mutable JavaScript object at run time; any other reference to it that is typed without `readonly` can write to it freely, and code that never went through the checker at all — JSON parsed from the network, a JavaScript caller — is not constrained in any way. If you need immutability that survives into the running program, you need a runtime mechanism, and the type is only documentation of intent. ## Where the three meet in practice A typical layered use looks like this: the public options type is `Partial<Options>`, the internal resolved type is `Required<Options>`, and the resolved value is passed down as `Readonly<Required<Options>>` so no consumer can edit shared configuration. The composition reads well precisely because each utility does one narrow thing. ## What weak answers get wrong The two failure modes are believing `Readonly` does something at run time, and believing `Required<Partial<T>>` reconstructs `T`. It does not: the round trip loses the information about which properties were optional in the original, so every property comes back required.

  • Does `Readonly<T>` stop anything from happening at run time?
    No. Types are erased, so the emitted JavaScript contains no check at all — the object is an ordinary mutable object. Another reference to the same object typed without `readonly` can assign to it freely, and code that never passed through the checker is unaffected. `readonly` is a statement of intent the compiler enforces at the boundaries it can see, not a runtime guarantee.
  • Is `Required<Partial<T>>` the same type as `T`?
    No, and this is a good trap. `Partial<T>` makes every property optional, and `Required` then makes every property mandatory — including the ones that were optional in the original `T`. The round trip throws away which properties were optional, so given `{ id: number; name?: string }` you get back `{ id: number; name: string }`.
  • When is `Partial<T>` the wrong type for an options object?
    When some options genuinely have no sensible default. `Partial<T>` accepts `{}`, so it cannot say "these two fields are mandatory and the rest are optional". For that, keep the mandatory fields in a required type and intersect it with a partial version of the rest, so the compiler still rejects a call that omits the fields you actually need.

saying these in an interview costs you the question

  • Says Readonly<T> freezes the object at run time
  • Thinks Partial<T> recurses into nested object properties
  • Claims Required<Partial<T>> gives you the original type back
  • Describes Partial as a function you call on a value
  • Assumes a readonly-typed object cannot be mutated through another reference

context