UNPKG

@spaced-out/ui-design-system

Version:
101 lines 3.61 kB
/** * ------------------------------------------------------------------------ * Utility types for Flow-style spread + override semantics * ------------------------------------------------------------------------ * * WHY THIS Type EXISTS * -------------------- * In Flow you can write: * * type B = { ...A, c: number }; * * which means "take all of A's properties, but if there is a conflict, * override with the new definition." In TypeScript, doing the naive * * type B = A & { c: number }; * * will give you `c: string & number` (=> `never`) instead of the * intended override. That leads to subtle bugs or unusable types. * * To avoid this pitfall and to set a clear team standard, we define * short utilities here (`Over`, `OverAll`, etc.) that encode the * intended semantics: **include everything from the left, right side * wins on conflicts**. * * * WHAT EACH UTILITY DOES * ---------------------- * * Over<T, R> * - Include all properties from T * - Add properties from R * - For overlapping keys, R overrides T * * OverAll<T, [R1, R2, ...]> * - Fold multiple overrides in sequence, left → right * - Equivalent to: Over(Over(Over(T, R1), R2), ...) * - Rightmost definition of a key always wins * * OverOnly<T, R> * - Same as Over, but disallows adding brand-new keys * - Useful when you want to enforce "true override only" * * OverAllOnly<T, [R1, R2, ...]> * - Same as OverAll, but strict: every key in every R must * already exist in T (or an earlier override) * * * EXAMPLES * -------- * * // Base shape * type A = { a: boolean; b: number; c: string }; * * // Simple override * type B = Over<A, { c: number }>; * // => { a: boolean; b: number; c: number } * * // Multiple overrides * type C = OverAll<A, [ * { c: number }, * { b: string }, * { d: Date } * ]>; * // => { a: boolean; c: number; b: string; d: Date } * * // Strict variant (no new keys allowed) * type D = OverOnly<A, { c: number }>; // OK * type E = OverOnly<A, { d: Date }>; // ERROR (d not in A) * * * DESIGN SYSTEM USAGE * ------------------- * - Use **Over** by default whenever you would otherwise write * `A & { ... }`. This ensures right-biased override and prevents * "never" bugs. * * - Use **OverAll** if you are combining 3+ prop bags or when you * have `A & B & C & { ... }`. * * - Use **OverOnly / OverAllOnly** when you want to *forbid* * introducing brand-new props, e.g. when extending a public API * contract that must not grow new keys. * * * TEAM STANDARD * ------------- * ✅ Always prefer Over / OverAll for prop composition in DS components. * ❌ Avoid raw `& { ... }` intersections for overrides — they will * produce `never` on conflicts. * * This makes our type contracts predictable, Flow-like, and safer * against accidental regressions when new keys are added upstream. * */ export type Over<T extends object, R extends object> = Omit<T, keyof R> & R; export type OverAll<T extends object, Rs extends readonly object[]> = Rs extends [infer H extends object, ...infer Tail extends readonly object[]] ? OverAll<Over<T, H>, Tail> : T; export type OverOnly<T extends object, R extends { [K in keyof R & keyof T]: unknown; } & object> = Omit<T, keyof R> & R; export type OverAllOnly<T extends object, Rs extends readonly object[]> = Rs extends [infer H extends object, ...infer Tail extends readonly object[]] ? OverAllOnly<OverOnly<T, H>, Tail> : T; //# sourceMappingURL=utils.d.ts.map