Types: `ToastTransitionProps.position` is `ToastPosition | string` — should be `ToastPosition` (only the 6 positions have CSS)

Author: jaggujiCreated Jun 9, 2026Updated Jun 9, 2026

Describe the bug

ToastTransitionProps.position is typed ToastPosition | string, but every other place that describes the same value uses the pure union ToastPosition, and the runtime only supports the six known positions. The | string widens the union to plain string (TypeScript subsumes 'top-right' | … | string into string), so consumers of ToastTransitionProps — i.e. anyone writing a custom transition — lose all type-safety and autocomplete on position, and the compiler will happily accept an invalid value that produces no animation at runtime.

Version

[email protected]

Evidence

dist/index.d.ts (built types):

typescript
// line 99  — the public option
interface CommonOptions { position?: ToastPosition; ... }

// line 294 — internal toast props
interface ToastProps extends ToastOptions { position: ToastPosition; ... }

// line 278 — the outlier
interface ToastTransitionProps { position: ToastPosition | string; ... }   // ⟵ should be ToastPosition

ToastPosition is the closed union:

typescript
type ToastPosition = 'top-right' | 'top-center' | 'top-left'
                   | 'bottom-right' | 'bottom-center' | 'bottom-left';

Why | string is incorrect (not just imprecise):

  1. The transition builds its animation class by concatenation — const enterClassName = appendPosition ? \${enter}--${position}` : enter — and the shipped CSS only defines rules for the six known positions (.Toastify__bounce-enter--top-left, --top-right, --top-center, --bottom-left, --bottom-right, --bottom-center). Any other string yields a class like Toastify__bounce-enter--whatever` that matches no rule → no animation (silent breakage).
  2. The only value ever passed to a transition's position originates from the container/toast options, which are typed ToastPosition (CommonOptions.position?: ToastPosition). So the | string arm is never legitimately reachable through normal use.

Expected behavior

typescript
interface ToastTransitionProps {
  position: ToastPosition;   // closed union, consistent with CommonOptions / ToastProps
}

This restores typo-safety and autocomplete for custom-transition authors and matches what the runtime actually supports. (If arbitrary positions are intended to be supported in future, the fix would instead be to add the corresponding CSS — but as shipped, the types are looser than the implementation.)

How I found this

While generating ReScript type bindings from react-toastify's .d.ts, the binding for <Bounce> came out as position: string (correct per the types) while <ToastContainer> came out as a proper 6-value enum — the asymmetry traced back to this single | string. Filing since it looks like an unintended widening.