Skip to main content

Readonly | optional intersections

In short:

  • When properties with the same name intersect, the property type is taken as the intersection of the types (T1 & T2).
  • readonly "wins": if the property is readonly in at least one type, it will be readonly in the intersection.
  • Required-ness "wins": if the property is required in one type, it becomes required in the intersection; it stays optional only if it is optional in all parts.

Below, illustrated.


1) readonly × a regular field

javascript
type A = { readonly x: number }; type B = { x: number }; type C = A & B; // { readonly x: number }
  • It has to satisfy both types at once -> x cannot be changed => readonly.
  • If the types are incompatible, you get an unrepresentable result:
javascript
type D = { readonly x: number } & { x: string }; // x: number & string -> never -> the whole type becomes unrepresentable (effectively never)

Rule: for the same key, the readonly modifier in an intersection is always kept if it appears in at least one member.


2) How an intersection affects optional (?)

a) Optional × required -> required

javascript
type A = { x?: number }; type B = { x: number }; type C = A & B; // { x: number }
  • To satisfy B, x must exist; so the resulting property is required.

b) Optional × optional (with compatible types)

javascript
type A = { x?: number }; type B = { x?: number }; type C = A & B; // { x?: number }
  • The optionality is preserved, since the property is optional in both parts and the types are compatible.

c) Optional × optional (with incompatible types)

javascript
type A = { x?: string }; type B = { x?: number }; type C = A & B; // { x?: never }
  • Property type: string & number -> never.
  • Semantics of x?: never: the property cannot be specified (if you specify it, you cannot give it a valid value), but it can be omitted.
  • In effect, this behaves as if "property x doesn't exist".

d) Optional × required (with incompatible types)

javascript
type A = { x?: string }; type B = { x: number }; type C = A & B; // { x: never } // required, but never realizable
  • This produces a contradiction: the property is required, but its type is never => the intersection is unresolvable (effectively never).

e) Mixing with readonly

javascript
type A = { readonly x?: number }; type B = { x: number }; type C = A & B; // { readonly x: number }
  • Required-ness "wins", readonly is preserved.

Cheat sheet (intuitive rules)

  • Type: T = T1 & T2.
  • Required-ness: if the property is required in at least one type -> in the intersection it is required; it's optional only if it's optional everywhere.
  • readonly: if readonly appears anywhere -> in the intersection it's readonly.
  • If the resulting property type becomes never and the property is required -> the whole type is not realizable (effectively never). If the resulting type is never and the property is optional -> the property is "forbidden to specify", but can be omitted.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.