In TypeScript, does swapping the operands of an intersection type — writing `B & A` instead of `A & B` — ever change how the resulting type behaves?
answer
- properties behave one way, calls another
- intersecting member types is commutative
- signatures are concatenated, not intersected
- first matching overload wins
- specific signature goes on the left
basics
~20 sFor property members, no: each member's type is the intersection of both contributions, and that is commutative, so the two orderings are mutually assignable. Order does matter for call and construct signatures, which are concatenated in operand order and resolved first-match-wins.
solid answer
~50 sFor ordinary properties the order is irrelevant. `A & B` computes each shared member as the intersection of the two contributions, and intersection is commutative, so `A & B` and `B & A` are mutually assignable — that is why the composition is called order-independent. The exception is **call and construct signatures**. When both operands are callable, the resulting type carries the signatures of the left operand followed by those of the right, and overload resolution picks the *first* signature that accepts the arguments. So `((x: string) => "s") & ((x: unknown) => "u")` returns `"s"` for a string argument, while the reversed intersection returns `"u"`, because the broader signature now comes first. Order also affects the order members are printed in hovers and errors, which is cosmetic but matters for readability of a diagnostic.
code
typescript · 15 lines// Properties: order-independent.
type A = { id: string; a: number };
type B = { id: string | number; b: boolean };
declare let ab: A & B;
declare let ba: B & A;
ab = ba;
ba = ab;
// Call signatures: order decides which overload wins.
type StrFirst = ((x: string) => "s") & ((x: unknown) => "u");
type UnkFirst = ((x: unknown) => "u") & ((x: string) => "s");
declare const f: StrFirst;
declare const g: UnkFirst;
const r1: "s" = f("hi");
const r2: "u" = g("hi");go deeper
Know that A & B and B & A describe the same object shape — an intersection has no "last one wins" rule the way an object spread does.
Explain why: shared members get the intersection of both contributions, and that operation is commutative, so the two orderings are mutually assignable.
Demonstrate the exception. Callable operands concatenate their signatures in source order and resolution is first-match, so you can diagnose a call that infers a different return type depending on how the alias was assembled.
Set the convention for shared declaration files: order intersected signatures most-specific-first deliberately and comment why, since a later contributor reordering them silently changes inferred return types across the codebase.
## The property case: genuinely order-independent An intersection of two object types produces a type whose members are the union of both member sets, and where a name appears in both, the member's type is the intersection of the two contributions. Both of those operations are commutative, so for property-only types the ordering has no semantic effect: ```typescript type A = { id: string; a: number }; type B = { id: string | number; b: boolean }; type AB = A & B; // id: string & (string | number) => string type BA = B & A; // id: (string | number) & string => string declare let ab: AB; declare let ba: BA; ab = ba; // OK ba = ab; // OK ``` This is what people mean when they say intersections have no precedence: unlike a heritage clause, where the derived declaration overrides the parent's, `&` has no "winner". If two constituents disagree irreconcilably, the member becomes `never` regardless of which side you wrote first. ## The exception: call and construct signatures A type can carry call signatures, and intersecting two callable types does not intersect the signatures — it **concatenates** them into an overload set, in operand order. Overload resolution then walks that list top to bottom and commits to the first signature whose parameters accept the supplied arguments. The consequence is that the operand order decides which overload wins whenever more than one could match: ```typescript type StrFirst = ((x: string) => "s") & ((x: unknown) => "u"); type UnkFirst = ((x: unknown) => "u") & ((x: string) => "s"); declare const a: StrFirst; declare const b: UnkFirst; const r1 = a("hi"); // "s" — the string signature is tried first and matches const r2 = b("hi"); // "u" — the unknown signature comes first and also matches ``` Both types accept exactly the same arguments; they differ only in the *result* they infer. The same applies to methods declared on the two operands: intersecting `{ f(x: string): void }` with `{ f(x: number): void }` yields an `f` with two overloads rather than a `never` member, because the collision is between call signatures, not property types. This is not a curiosity. It is the mechanism behind hand-written overload ordering in declaration files, where a library intersects a specific signature with a fallback and relies on the specific one being written first. ## Where order shows up cosmetically Even in the property-only case, order leaks into the developer experience. Hover text and error messages generally print constituents in the order you wrote them, so `Props & { children: ReactNodeLike }` and the reverse produce differently-shaped diagnostics for the same underlying type. On a deeply nested alias chain that difference decides whether an error message is readable at all. ## What to do with this in practice - Do not rely on operand order to "override" a property. It does not override; it intersects, and a conflict silently produces `never`. If you want an override you want a heritage clause, not `&`. - Do rely on operand order when you are deliberately building an overload set, and write the most specific signature first, since resolution stops at the first match. - Be suspicious of a bug report of the form "the same call returns different types in two places" where both sites use an intersected callable. Check whether the two aliases were assembled in different orders. - Remember that all of this is compile-time only. The emitted JavaScript is identical whichever order you wrote; only the checker's answers change.
- If two intersected object types each declare a method with the same name but different parameter types, do you get `never`?No. Methods contribute call signatures, and intersecting callable types concatenates their signatures into an overload set rather than intersecting them. The resulting method accepts either parameter type, resolving to the first signature that matches. Collapse to `never` happens for property types that are disjoint, such as `string & number`.
- Given that order is irrelevant for properties, when should you still care about how you write an intersection?Two cases. When either operand is callable, order fixes overload precedence and should put the most specific signature first. And for readability, since hovers and error messages print constituents in source order — putting the caller's own type first usually produces a diagnostic that names the relevant shape early.
saying these in an interview costs you the question
- Says the left operand's property types take precedence
- Says the right operand overrides, like an object spread
- Claims overloads are tried most-specific-first regardless of order
- Thinks intersecting two function types intersects their parameters
- Believes operand order changes the emitted JavaScript