Skip to content

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

Description

@jagguji

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

react-toastify@11.1.0

Evidence

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

// 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:

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

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions