Skip to content

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.

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 } = styles

Alias 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.

  • 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 const array, and a union type inferred from that array.
  • Add comments or JSDoc only where they explain intent, boundaries, or non-obvious behavior.