Prior to 4.9.0 (lab-alpha-40)
/**
true, value must be an array and the menu will support multiple selections.After 4.9.0"
/**
true, value must be an array and the menu will support multiple selections.I think it is a bug. Interface cannot have constants.
Steps:
1.
2.
3.
4.
| Tech | Version |
| ----------- | ------- |
| Material-UI | v4.?.? |
| React | |
| Browser | |
| TypeScript | |
| etc. | |
@lamqson This sounds expected. What's the issue?
(cc @testarossaaaaa)
@lamqson it's the way Discriminated Unions work in typescript. Maybe you can advise a better description for union props that depends on other prop. Also please indicate the level of the bug: it's related only to the documentation or you have a build time error.
Hello, Thanks for your quick reply.
We wrap Autocomplete inside of one of our components. and "multiple" is an option passed from props. I cannot assign it as TRUE or FALSE:
isMultiple: boolean
<Autocomplete
className={classesDefault.grow}
{...standardProps}
{...groupingProps}
multiple={isMultiple}
autoHighlight={props.autoHighlight ? props.autoHighlight : true}
loading={state.isOpen && state.options.length === 0}
open={state.isOpen && !itemsAtMaximum}
onOpen={e => {
setState({ ...state, isOpen: true })
}}
I am having a TypeScript compilation error:
```
Type '{ multiple: boolean; autoHighlight: true; loading: boolean; open: boolean; onOpen: (e: ChangeEvent<{}>) => void; onClose: (e: ChangeEvent<{}>) => void; getOptionLabel: (option: any) => any; ... 309 more ...; ref?: Ref<...>; }' is not assignable to type 'UseAutocompleteMultipleProps
Types of property 'multiple' are incompatible.
Type 'boolean' is not assignable to type 'true'. TS2322
338 |
339 | return (
340 |
| ^
341 | className={classesDefault.grow}
342 | {...standardProps}
343 | {...groupingProps}
```
Could you help to point to a fix?
Thanks.
Check the demo.tsx file at lines 6 and 13, I cannot reproduce the error without additional information about your component. A link to the reproduction in codesandbox will be helpful.
The issue has been closed because it does not conform to our issue requirements.
Please provide a full reproduction test case. This would help a lot 馃懛 .
A live example would be perfect. This codesandbox.io template _may_ be a good starting point. Thank you!
@testarossaaaaa @oliviertassinari
Download the codesandbox onto your computer, export as Zip, run yarn install, and run tsc:
yarn tsc
yarn run v1.21.1
warning package.json: No license field
$ ~/Downloads/material-demo/node_modules/.bin/tsc
src/demo.tsx:12:6 - error TS2322: Type '{ multiple: boolean; id: string; options: { title: string; year: number; }[]; getOptionLabel: (option: { title: string; year: number; }) => string; style: { width: number; }; renderInput: (params: RenderInputParams) => Element; }' is not assignable to type '(IntrinsicAttributes & AutocompleteProps<{ title: string; year: number; }> & UseAutocompleteSingleProps<{ title: string; year: number; }>) | (IntrinsicAttributes & AutocompleteProps<{ title: string; year: number; }> & UseAutocompleteMultipleProps<...>)'.
Type '{ multiple: boolean; id: string; options: { title: string; year: number; }[]; getOptionLabel: (option: { title: string; year: number; }) => string; style: { width: number; }; renderInput: (params: RenderInputParams) => Element; }' is not assignable to type 'UseAutocompleteMultipleProps<{ title: string; year: number; }>'.
Types of property 'multiple' are incompatible.
Type 'boolean' is not assignable to type 'true'.
12 <Autocomplete
~~~~~~~~~~~~
Found 1 error.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
@bugzpodder Ah, gotcha! To fulfil the requirements, you need to extend the full interface that uses union types under the hood, like in this example:
import Autocomplete, { AutocompleteProps } from "@material-ui/lab/Autocomplete";
import { UseAutocompleteProps } from "@material-ui/lab/useAutocomplete";
type Props<T> = {
customProp?: string;
} & Omit<AutocompleteProps<T>, "renderInput"> &
UseAutocompleteProps<T>;
type ComboBox = Props<typeof top100Films[0]>;
function ComboBox({ customProp, ...rest }: ComboBox) {
return (
<Autocomplete
{...rest}
id="combo-box-demo"
options={top100Films}
getOptionLabel={option => option.title}
style={{ width: 300 }}
renderInput={params => (
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
)}
/>
);
}
export default function() {
return <ComboBox customProp="test" />;
}
You cannot just use the custom prop isMultiple, because the whole type inferred from bunch of other properties on type level, such as multiple, so you have to use them to let typescript infer the right type.
You cannot just use the custom prop isMultiple
@testarossaaaaa What would be the "price" to pay to have this capability? What would we give up?
To solve the problem we can drop support of the multiple prop on typings level, so all handlers will receive a value as T | T[] regardless is multiple or not, so users will cast the type manually.
Another approach it simplify extending to extend Omit<AutocompleteProps<T>, OverwrittenProps> and document it in the cookbook or in some other place.
@testarossaaaaa Ok thanks, in this case, I doubt we should trade it. I think that we have more to gain by discriminating T to T[] even if it leads to this GitHub issue.
@oliviertassinari what if you do
interface AutocompleteProps<T, P extends HTMLInputElement | HTMLSelectElement = HTMLInputElement> {
multiple?: boolean
value?: P extends HTMLInputElement ? T : T[]
}
yes this is little bit verbose but you are not loosing anything
@TrejGun what problem did you face?
I want to wrap autocomplete to use with final form
here is a code sample https://trejgun.github.io/articles/bindings-for-using-final-form-with-material-ui-autocomplete
@oliviertassinari so will this work?
Issue from the first comment and the one that @lamqson brought up is addressed in https://github.com/mui-org/material-ui/pull/20949. This is actually one of the issues that was bothering me.
I have a custom type declared for the options:
interface OptionType {
label: string;
value: string;
}
I also have custom props:
type AutocompleteProps<T> = Omit<
MuiAutocompleteProps<T>, 'renderInput'
> & MuiUseAutocompleteProps<T> & {
fixedOptions?: T[];
label?: string;
name: string;
startAdornment?: React.ReactNode;
};
Then I compose the Autocomplete component into my own:
const Autocomplete: FC<AutocompleteProps<OptionType>> = (props): React.ReactElement => {
const classes = useStyles();
const {
disabled,
label,
name,
fixedOptions,
startAdornment,
...rest
} = props;
const [field, meta, helpers] = useField(name);
const form = useFormikContext();
return (
<Grid
className={classes.gridContainer}
container
item
>
{startAdornment ? (
<Grid className={classes.gridItemIcon} item>
{startAdornment}
</Grid>
) : null}
<Grid className={classes.gridItemField} item>
<MuiAutocomplete
classes={{
endAdornment: classes.autocompleteEndAdornment,
inputRoot: classes.autocompleteInputRoot,
input: classes.autocompleteInput,
}}
autoHighlight={true}
disableClearable
fullWidth
getOptionLabel={(option: OptionType): OptionType['label'] => option.label}
popupIcon={<ExpandMoreOutlinedIcon />}
renderInput={(params): React.ReactNode =>
<TextField
label={label}
FormHelperTextProps={{
classes: {
root: classes.helperText,
},
}}
InputLabelProps={{
classes: {
root: clsx({
[classes.inputLabelWithValue]: field.value,
}),
},
}}
{...params}
/>
}
renderTags={(tagValue, getTagProps): React.ReactNode =>
tagValue.map((option: OptionType, index: number) => {
const fixed = _.includes(fixedOptions, option);
return (
<Chip
classes={{
root: classes.tag,
colorPrimary: classes.tagColorPrimary,
deleteIcon: clsx(classes.tagDeleteIcon, {
[classes.tagDeleteIconHidden]: fixed,
}),
disabled: classes.tagDisabled,
}}
color={fixed ? 'default' : 'primary'}
label={option.label}
key={index}
{...getTagProps({ index })}
disabled={fixed}
/>
)
})
}
disabled={disabled ?? form.isSubmitting}
{...rest}
{...field}
onChange={(_event: React.ChangeEvent<{}>, value: AutocompleteProps<OptionType>['value']): void => {
helpers.setValue(value);
}}
/>
</Grid>
</Grid>
);
};
Yes, we have some really funky style requirements 馃槃
I get an error "Types of property 'defaultValue' are incompatible". If I omit the 'defaultValue' from inclusion in the prop type, since this is a controlled component using Formik, I get the same error just regarding 'multiple'.
I'm guessing I'm doing something really wrong since the type of discrimination seems to be causing this.. Can you give me a pointer to where I have misunderstood this?
i want costumize Autocomplate like this:
import React, { FC } from "react";
import { useStyles } from "./autocomplate.style";
import { useTranslator } from "localization";
import { Autocomplete, AutocompleteProps } from "@material-ui/lab/";
import { TextField } from "..";
export interface IAutoComplate {}
export const AutoComplate: FC<IAutoComplate & AutocompleteProps> = () => {
const lang = useTranslator();
const classes = useStyles();
return (
<Autocomplete
id="combo-box-demo"
options={[]}
getOptionLabel={(option) => option}
style={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Combo box" variant="outlined" />}
/>
);
};
but it gives me an error like this:
Generic type 'AutocompleteProps
what can i do in this situation ?
Most helpful comment
@lamqson This sounds expected. What's the issue?
(cc @testarossaaaaa)