base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[select] Broken `value` type inference on Select wrapper component

Open andremonteiro95 opened this issue 1 month ago • 2 comments

Bug report

Current behavior

After upgrading to version 1.0.0-beta.6, the Select component incorrectly infers its value type. With an approach that closely resembles the one in these docs:

function MySelect<Value, Multiple extends boolean | undefined = false>(
  props: SelectRootProps<Value, Multiple>
) {
  return <Select.Root {...props} />
}

I am getting the following error in Select.Root:

No overload matches this call.
  Overload 1 of 2, '(props: SelectRootControlledProps<Value, Multiple>): Element', gave the following error.
    Type '{ children?: ReactNode; inputRef?: Ref<HTMLInputElement> | undefined; name?: string | undefined; id?: string | undefined; required?: boolean | undefined; ... 15 more ...; onValueChange?: ((value: SelectValueType<...>, eventDetails: SelectRootChangeEventDetails) => void) | undefined; } | { ...; }' is not assignable to type 'IntrinsicAttributes & Omit<SelectRootCommonProps<Value>, "multiple" | "value" | "defaultValue" | "onValueChange"> & { ...; } & { ...; }'.
      Type '{ children?: ReactNode; inputRef?: Ref<HTMLInputElement> | undefined; name?: string | undefined; id?: string | undefined; required?: boolean | undefined; ... 15 more ...; onValueChange?: ((value: SelectValueType<...> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void) | und...' is not assignable to type '{ value: SelectValueType<Value, Multiple>; onValueChange?: ((value: SelectValueType<Value, Multiple>, eventDetails: SelectRootChangeEventDetails) => void) | undefined; }'.
        Property 'value' is optional in type '{ children?: ReactNode; inputRef?: Ref<HTMLInputElement> | undefined; name?: string | undefined; id?: string | undefined; required?: boolean | undefined; ... 15 more ...; onValueChange?: ((value: SelectValueType<...> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void) | und...' but required in type '{ value: SelectValueType<Value, Multiple>; onValueChange?: ((value: SelectValueType<Value, Multiple>, eventDetails: SelectRootChangeEventDetails) => void) | undefined; }'.
  Overload 2 of 2, '(props: SelectRootUncontrolledProps<Value, Multiple>): Element', gave the following error.
    Type '{ children?: ReactNode; inputRef?: Ref<HTMLInputElement> | undefined; name?: string | undefined; id?: string | undefined; required?: boolean | undefined; ... 15 more ...; onValueChange?: ((value: SelectValueType<...>, eventDetails: SelectRootChangeEventDetails) => void) | undefined; } | { ...; }' is not assignable to type 'IntrinsicAttributes & Omit<SelectRootCommonProps<Value>, "multiple" | "value" | "defaultValue" | "onValueChange"> & { ...; } & { ...; }'.
      Type '{ children?: ReactNode; inputRef?: Ref<HTMLInputElement> | undefined; name?: string | undefined; id?: string | undefined; required?: boolean | undefined; ... 15 more ...; onValueChange?: ((value: SelectValueType<...>, eventDetails: SelectRootChangeEventDetails) => void) | undefined; }' is not assignable to type '{ value?: any; onValueChange?: ((value: SelectValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void) | undefined; }'.
        Types of property 'onValueChange' are incompatible.
          Type '((value: SelectValueType<Value, Multiple>, eventDetails: SelectRootChangeEventDetails) => void) | undefined' is not assignable to type '((value: SelectValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void) | undefined'.
            Type '(value: SelectValueType<Value, Multiple>, eventDetails: SelectRootChangeEventDetails) => void' is not assignable to type '(value: SelectValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void'.
              Types of parameters 'value' and 'value' are incompatible.
                Type 'SelectValueType<Value, Multiple> | (Multiple extends true ? never : null)' is not assignable to type 'SelectValueType<Value, Multiple>'.
                  Type 'Multiple extends true ? never : null' is not assignable to type 'SelectValueType<Value, Multiple>'.
                    Type 'null' is not assignable to type 'SelectValueType<Value, Multiple>'.
                      Type 'null' is not assignable to type 'Value'.
                        'Value' could be instantiated with an arbitrary type which could be unrelated to 'null'.

Expected behavior

The type of the value property should be correctly inferred.

Reproducible example

Link to CodeSandbox: https://codesandbox.io/p/sandbox/rypm33

Base UI version

v1.0.0-beta.6

Which browser are you using?

Chrome

andremonteiro95 avatar Nov 21 '25 10:11 andremonteiro95

Those docs were added as part of the refactor in #3254 to simplify typed wrappers to ensure they're easy to make. It's not part of beta.6 yet, it'll be in the next version.

A workaround for the current version is to specify value after the spread:

export function MySelect<Value, Multiple extends boolean | undefined = false>(
  props: Select.Root.Props<Value, Multiple>
): React.JSX.Element {
  return (
    <Select.Root {...props} value={props.value}>
      {/* ... */}
    </Select.Root>
  );
}

Or install the preview release in the meantime until the next version is on npm, which has typesafety:

pnpm add https://pkg.pr.new/mui/base-ui/@base-ui-components/react@3254

atomiks avatar Nov 21 '25 18:11 atomiks

@atomiks Should we close this one, given that the next release will have the mentioned improvement? 🤔

LukasTy avatar Nov 24 '25 11:11 LukasTy

This fix will be available in the next npm release of Base UI.

In the meantime, you can try it out on our Canary release channel:

npm i https://pkg.pr.new/@base-ui-components/react@3331

github-actions[bot] avatar Nov 27 '25 14:11 github-actions[bot]