Skip to content

openContextModal modal name not type-checked despite MantineModalsOverride augmentation #8588

@RaphalRiad

Description

@RaphalRiad

Description

When using module augmentation to add type safety to context modals as documented, the modal property in openContextModal() accepts any string instead of being restricted to the defined modal keys. However, innerProps IS correctly type-checked.

Steps to reproduce

  1. Define modals and augment MantineModalsOverride as per docs:
// modals/index.ts
export const modals = {
  createDepartment: AddDepartmentModal,
  createUser: CreateUserModal,
} as const;

// registry.d.ts
import type { modals } from './modals';

declare module '@mantine/modals' {
  export interface MantineModalsOverride {
    modals: typeof modals;
  }
}

export {};
  1. Call openContextModal with an invalid modal name:
openContextModal({
  modal: 'invalidModalName', // ❌ No TypeScript error, but should error
  title: 'Test',
  innerProps: { orgId: 123 },
});
  1. Note that innerProps IS type-checked correctly:
openContextModal({
  modal: 'createDepartment',
  title: 'Test',
  innerProps: { wrongProp: 123 }, // ✅ TypeScript error as expected
});

Expected behavior

modal: 'invalidModalName' should produce a TypeScript error like:

Type '"invalidModalName"' is not assignable to type '"createDepartment" | "createUser"'

Actual behavior

No error. Any string is accepted for modal.

Root cause analysis

The issue is in context.d.ts:

export type MantineModalsOverwritten = MantineModalsOverride extends {
  modals: Record<string, React.FC<ContextModalProps<any>>>;
} ? MantineModalsOverride : {
  modals: Record<string, React.FC<ContextModalProps<any>>>;
};

export type MantineModals = MantineModalsOverwritten['modals'];
export type MantineModal = keyof MantineModals;

When TypeScript evaluates MantineModalsOverwritten['modals'], it intersects the type with the conditional's constraint (Record<string, ...>), causing keyof to return string instead of the literal union.

Proof:

// Direct access works correctly:
type ModalsDir = MantineModalsOverride['modals'];
type ModalDir = keyof ModalsDir;  // "createDepartment" | "createUser" ✓

// Through conditional type doesn't:
type Modals = MantineModalsOverwritten['modals'];
type Modal = keyof Modals;  // string ✗

Suggested fix

Replace the conditional type pattern with direct property access, or use a different conditional structure that doesn't cause type widening:

// Option 1: Direct access when modals property exists
export type MantineModals = MantineModalsOverride extends { modals: infer M }
  ? M
  : Record<string, React.FC<ContextModalProps<any>>>;

// Option 2: Use a mapped type or other pattern

Environment

  • @mantine/modals: 8.3.11
  • TypeScript: 5.9.3
  • tsconfig: moduleResolution: "bundler", strict: false

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedContributions from community are welcome

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions