TypeScript Conventions
Use these conventions when adding or revising TypeScript in Wavemap. They complement the formatter and lint rules with review guidance for choices that are valid TypeScript but materially affect readability.
Destructure At Function Boundaries
Section titled “Destructure At Function Boundaries”Prefer destructuring object parameters when a function consumes their fields locally. This makes the function’s inputs
visible at the signature and avoids repeating a generic wrapper such as args throughout the implementation.
const resolveWindow = ({ startDate, endDate, timeZoneId }: TResolveWindowArgs) => { validateDateRange(startDate, endDate)
return projectWindow({ startDate, endDate, timeZoneId })}Avoid retaining an args object only to repeatedly read args.startDate, args.endDate, and args.timeZoneId.
Apply the same preference to returned objects when the caller consumes specific fields:
const { publicId, slug } = buildPublicEventIdentifierFields(eventName)This is clearer than naming a generic result or response and repeatedly reading properties from it. Rename fields
during destructuring when the local role differs from the returned name, such as
{ disambiguation: resolvedDisambiguation }.
Destructure Repeated CSS Module Members At Module Scope
Section titled “Destructure Repeated CSS Module Members At Module Scope”When a UI module uses several statically named classes from an imported CSS module, destructure those classes once at
module scope, immediately below the imports. Components and helpers should then use the local bindings instead of
repeatedly reading styles.className or styles["class-name"] throughout the implementation.
import styles from "./AvatarStyles.module.css"
const { avatar, ["avatar--round"]: avatarRound, avatar__fallback, avatar__image } = stylesAlias class keys that are not valid TypeScript identifiers with a clear local name, as shown by avatarRound above.
Choose aliases that cannot be confused with props, parameters, or other local values.
Use judgment for small modules. A component or helper that reads only one or two classes may keep direct access when a module-scope destructure would add more ceremony than clarity. This is a readability convention rather than a numeric lint rule. Keep the imported style object intact for genuinely dynamic class-name lookups as well.
Keep Meaningful Objects When They Carry Meaning
Section titled “Keep Meaningful Objects When They Carry Meaning”Destructuring is a preference, not a requirement to flatten every value. Keep the object intact when:
- The function forwards it unchanged to another owner.
- A discriminated union must remain intact while its discriminator narrows the available fields.
- Several operations act on the object as one domain concept.
- Destructuring a large surface would hide structure or create ambiguous local names.
Use domain-specific names such as analysis, schedule, or authenticatedUser in those cases. Avoid generic names such
as args, data, result, or response when a more specific name communicates ownership or state.
Related Conventions
Section titled “Related Conventions”- Prefer verbose, self-documenting names and arrow functions.
- Keep reusable type declarations in the owning type module instead of mixing a public type surface into utility code.
- Model string vocabularies with named constants, a canonical
as constarray, and a union type inferred from that array. - Add comments or JSDoc only where they explain intent, boundaries, or non-obvious behavior.
Read Next
Section titled “Read Next”- Feature Slice Workflow for ownership across apps and packages.
- API Contracts for shared schema and domain-code conventions.
- Testing for test ownership and authoring guidance.