Enhancing with Utility Types
Don't be fooled by the nonexistent syntax of A &? B
; it doesn't exist. But fear not, for we have the power of utility types at our disposal.
You can craft a utility type for any scenario, although the brevity of using just A | (A & B)
might prove more succinct than inventing your own lengthy naming convention.
type WithOptional<Required, Optional> = Required | ( Required & Optional )
const x: WithOptional<A, B> = ...
Streamlining with &
If dealing with SomeLongName
and SomeOtherLongName
instead of simpler identifiers like A
and B
, repeating A
in A | (A & B)
becomes cumbersome. By extracting out A
, we ensure that we always possess A
alongside B
or another entity.
const x: A & (B | {});
const y: SomeLongName & (SomeOtherLongName | {});
Embracing Partiality
The comments sparked discussions about the practical applications of this type. Personally, I find myself utilizing such constructs for destructuring purposes, where A | (A & B)
fails to grant access to B
properties. Instead, opting for A & Partial<B>
asserts that all A
properties are mandatory while allowing B
properties to adorn as optional elements. These optional properties may be extracted through destructuring but could potentially hold a value of undefined
.
const x: A & Partial<B> = { p1: 0, p2: 0 }
// p1 and p2 are `number`, p3 and p4 are `number | undefined`
const { p1, p2, p3, p4 } = x;
This utility type definition also serves the purpose:
type WithOptional<Required, Optional> = Required & Partial<Optional>