(null);
return (
@@ -62,7 +62,7 @@ export default function Playground() {
{...defaultProps}
id="controlled-demo"
value={value}
- onChange={(event, newValue) => {
+ onChange={(event: any, newValue: FilmOptionType | null) => {
setValue(newValue);
}}
renderInput={params => (
diff --git a/docs/src/pages/components/autocomplete/Sizes.tsx b/docs/src/pages/components/autocomplete/Sizes.tsx
index db58315fa3f47c..5ade96cf2438b5 100644
--- a/docs/src/pages/components/autocomplete/Sizes.tsx
+++ b/docs/src/pages/components/autocomplete/Sizes.tsx
@@ -25,7 +25,7 @@ export default function Sizes() {
id="size-small-standard"
size="small"
options={top100Films}
- getOptionLabel={(option: FilmOptionType) => option.title}
+ getOptionLabel={option => option.title}
defaultValue={top100Films[13]}
renderInput={params => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={[top100Films[13]]}
renderInput={params => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={top100Films[13]}
renderInput={params => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={[top100Films[13]]}
renderInput={params => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={top100Films[13]}
- renderTags={(value: FilmOptionType[], getTagProps) =>
- value.map((option: FilmOptionType, index: number) => (
+ renderTags={(value, getTagProps) =>
+ value.map((option, index) => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={[top100Films[13]]}
- renderTags={(value: FilmOptionType[], getTagProps) =>
- value.map((option: FilmOptionType, index: number) => (
+ renderTags={(value, getTagProps) =>
+ value.map((option, index) => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={[top100Films[13]]}
renderInput={params => (
option.title}
+ getOptionLabel={option => option.title}
defaultValue={[top100Films[13]]}
filterSelectedOptions
renderInput={params => (
@@ -79,11 +79,6 @@ export default function Tags() {
);
}
-interface FilmOptionType {
- title: string;
- year: number;
-}
-
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
diff --git a/docs/src/pages/components/autocomplete/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md
index 32eec630074a24..b3de3cc8f1ffba 100644
--- a/docs/src/pages/components/autocomplete/autocomplete.md
+++ b/docs/src/pages/components/autocomplete/autocomplete.md
@@ -172,6 +172,12 @@ Search within 10,000 randomly generated options. The list is virtualized thanks
VoiceOver on iOS Safari doesn't support the `aria-owns` attribute very well.
You can work around the issue with the `disablePortal` prop.
+### TypeScript
+
+To fully take advantage of type inference, you need to set the `multiple` prop to `undefined`, `false` or `true`.
+See [this discussion](https://github.com/mui-org/material-ui/pull/18854#discussion_r364215153) for more details.
+TypeScript might solve this bug in the future.
+
## Accessibility
(WAI-ARIA: https://www.w3.org/TR/wai-aria-practices/#combobox)
diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts b/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts
index 0b37afc815bf39..90e798807e3f08 100644
--- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts
+++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts
@@ -1,7 +1,11 @@
import * as React from 'react';
import { StandardProps } from '@material-ui/core';
import { PopperProps } from '@material-ui/core/Popper';
-import { UseAutocompleteProps, CreateFilterOptions, createFilterOptions } from '../useAutocomplete';
+import {
+ UseAutocompleteCommonProps,
+ createFilterOptions,
+ UseAutocompleteProps,
+} from '../useAutocomplete';
export { createFilterOptions };
@@ -30,8 +34,8 @@ export interface RenderInputParams {
inputProps: object;
}
-export interface AutocompleteProps
- extends UseAutocompleteProps,
+export interface AutocompleteProps
+ extends UseAutocompleteCommonProps,
StandardProps<
React.HTMLAttributes,
AutocompleteClassKey,
@@ -129,19 +133,19 @@ export interface AutocompleteProps
/**
* Render the option, use `getOptionLabel` by default.
*
- * @param {any} option The option to render.
+ * @param {T} option The option to render.
* @param {object} state The state of the component.
* @returns {ReactNode}
*/
- renderOption?: (option: any, state: RenderOptionState) => React.ReactNode;
+ renderOption?: (option: T, state: RenderOptionState) => React.ReactNode;
/**
* Render the selected value.
*
- * @param {any} value The `value` provided to the component.
+ * @param {T[]} value The `value` provided to the component.
* @param {function} getTagProps A tag props getter.
* @returns {ReactNode}
*/
- renderTags?: (value: any, getTagProps: GetTagProps) => React.ReactNode;
+ renderTags?: (value: T[], getTagProps: GetTagProps) => React.ReactNode;
/**
* The size of the autocomplete.
*/
@@ -171,4 +175,6 @@ export type AutocompleteClassKey =
| 'groupLabel'
| 'groupUl';
-export default function Autocomplete(props: AutocompleteProps): JSX.Element;
+export default function Autocomplete(
+ props: AutocompleteProps & UseAutocompleteProps,
+): JSX.Element;
diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js
index 2e3f53d09880bc..53685cc7c56430 100644
--- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js
+++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js
@@ -524,7 +524,7 @@ Autocomplete.propTypes = {
/**
* The default input value. Use when the component is not controlled.
*/
- defaultValue: PropTypes.any,
+ defaultValue: PropTypes.oneOfType([PropTypes.any, PropTypes.array]),
/**
* If `true`, the input can't be cleared.
*/
@@ -553,9 +553,9 @@ Autocomplete.propTypes = {
/**
* A filter function that determines the options that are eligible.
*
- * @param {any[]} options The options to render.
+ * @param {T[]} options The options to render.
* @param {object} state The state of the component.
- * @returns {any[]}
+ * @returns {T[]}
*/
filterOptions: PropTypes.func,
/**
@@ -588,7 +588,7 @@ Autocomplete.propTypes = {
* If provided, the options will be grouped under the returned string.
* The groupBy value is also used as the text for group headings when `renderGroup` is not provided.
*
- * @param {any} options The option to group.
+ * @param {T} options The option to group.
* @returns {string}
*/
groupBy: PropTypes.func,
@@ -637,7 +637,7 @@ Autocomplete.propTypes = {
* Callback fired when the value changes.
*
* @param {object} event The event source of the callback.
- * @param {any} value
+ * @param {T} value
*/
onChange: PropTypes.func,
/**
@@ -705,7 +705,7 @@ Autocomplete.propTypes = {
/**
* Render the option, use `getOptionLabel` by default.
*
- * @param {any} option The option to render.
+ * @param {T} option The option to render.
* @param {object} state The state of the component.
* @returns {ReactNode}
*/
@@ -713,7 +713,7 @@ Autocomplete.propTypes = {
/**
* Render the selected value.
*
- * @param {any} value The `value` provided to the component.
+ * @param {T[]} value The `value` provided to the component.
* @param {function} getTagProps A tag props getter.
* @returns {ReactNode}
*/
@@ -728,7 +728,7 @@ Autocomplete.propTypes = {
* The value must have reference equality with the option in order to be selected.
* You can customize the equality behavior with the `getOptionSelected` prop.
*/
- value: PropTypes.any,
+ value: PropTypes.oneOfType([PropTypes.any, PropTypes.array]),
};
export default withStyles(styles, { name: 'MuiAutocomplete' })(Autocomplete);
diff --git a/packages/material-ui-lab/src/TreeView/TreeView.d.ts b/packages/material-ui-lab/src/TreeView/TreeView.d.ts
index 8c1bf405624ace..5298492a583a77 100644
--- a/packages/material-ui-lab/src/TreeView/TreeView.d.ts
+++ b/packages/material-ui-lab/src/TreeView/TreeView.d.ts
@@ -32,7 +32,7 @@ export interface TreeViewProps
/**
* Callback fired when tree items are expanded/collapsed.
*
- * @param {object} event The event source of the callback
+ * @param {object} event The event source of the callback.
* @param {array} nodeIds The ids of the expanded nodes.
*/
onNodeToggle?: (event: React.ChangeEvent<{}>, nodeIds: string[]) => void;
diff --git a/packages/material-ui-lab/src/TreeView/TreeView.js b/packages/material-ui-lab/src/TreeView/TreeView.js
index b13f75561fb4e0..6f4eebe56d963a 100644
--- a/packages/material-ui-lab/src/TreeView/TreeView.js
+++ b/packages/material-ui-lab/src/TreeView/TreeView.js
@@ -394,7 +394,7 @@ TreeView.propTypes = {
/**
* Callback fired when tree items are expanded/collapsed.
*
- * @param {object} event The event source of the callback
+ * @param {object} event The event source of the callback.
* @param {array} nodeIds The ids of the expanded nodes.
*/
onNodeToggle: PropTypes.func,
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts
index 1a706f55746125..934233fe1995aa 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts
@@ -1,10 +1,10 @@
import * as React from 'react';
-export interface CreateFilterOptionsConfig {
+export interface CreateFilterOptionsConfig {
ignoreAccents?: boolean;
ignoreCase?: boolean;
matchFrom?: 'any' | 'start';
- stringify?: (option: any) => string;
+ stringify?: (option: T) => string;
trim?: boolean;
}
@@ -12,13 +12,11 @@ export interface FilterOptionsState {
inputValue: string;
}
-export type CreateFilterOptions = (
- config?: CreateFilterOptionsConfig,
-) => (option: any, state: FilterOptionsState) => any[];
+export function createFilterOptions(
+ config?: CreateFilterOptionsConfig,
+): (options: T[], state: FilterOptionsState) => T[];
-export const createFilterOptions: CreateFilterOptions;
-
-export interface UseAutocompleteProps {
+export interface UseAutocompleteCommonProps {
/**
* If `true`, the portion of the selected suggestion that has not been typed by the user,
* known as the completion string, appears inline after the input cursor in the textbox.
@@ -54,10 +52,6 @@ export interface UseAutocompleteProps {
* Consider this option when you need to customize the component.
*/
debug?: boolean;
- /**
- * The default input value. Use when the component is not controlled.
- */
- defaultValue?: any;
/**
* If `true`, the input can't be cleared.
*/
@@ -77,11 +71,11 @@ export interface UseAutocompleteProps {
/**
* A filter function that determines the options that are eligible.
*
- * @param {any[]} options The options to render.
+ * @param {T[]} options The options to render.
* @param {object} state The state of the component.
- * @returns {any[]}
+ * @returns {T[]}
*/
- filterOptions?: (options: any[], state: FilterOptionsState) => any[];
+ filterOptions?: (options: T[], state: FilterOptionsState) => T[];
/**
* If `true`, hide the selected options from the list box.
*/
@@ -93,25 +87,25 @@ export interface UseAutocompleteProps {
/**
* Used to determine the disabled state for a given option.
*/
- getOptionDisabled?: (option: any) => boolean;
+ getOptionDisabled?: (option: T) => boolean;
/**
* Used to determine the string value for a given option.
* It's used to fill the input (and the list box options if `renderOption` is not provided).
*/
- getOptionLabel?: (option: any) => string;
+ getOptionLabel?: (option: T) => string;
/**
* Used to determine if an option is selected.
* Uses strict equality by default.
*/
- getOptionSelected?: (option: any, value: any) => boolean;
+ getOptionSelected?: (option: T, value: T) => boolean;
/**
* If provided, the options will be grouped under the returned string.
* The groupBy value is also used as the text for group headings when `renderGroup` is not provided.
*
- * @param {any} options The option to group.
+ * @param {T} options The option to group.
* @returns {string}
*/
- groupBy?: (option: any) => string;
+ groupBy?: (option: T) => string;
/**
* This prop is used to help implement the accessibility logic.
* If you don't provide this prop. It falls back to a randomly generated id.
@@ -125,17 +119,6 @@ export interface UseAutocompleteProps {
* The input value.
*/
inputValue?: string;
- /**
- * If `true`, `value` must be an array and the menu will support multiple selections.
- */
- multiple?: boolean;
- /**
- * Callback fired when the value changes.
- *
- * @param {object} event The event source of the callback.
- * @param {any} value
- */
- onChange?: (event: React.ChangeEvent<{}>, value: any) => void;
/**
* Callback fired when the popup requests to be closed.
* Use in controlled mode (see open).
@@ -150,7 +133,7 @@ export interface UseAutocompleteProps {
* @param {string} value The new value of the text input.
* @param {string} reason Can be: "input" (user input), "reset" (programmatic change), `"clear"`.
*/
- onInputChange?: (event: React.ChangeEvent<{}>, value: any, reason: 'input' | 'reset') => void;
+ onInputChange?: (event: React.ChangeEvent<{}>, value: string, reason: 'input' | 'reset') => void;
/**
* Callback fired when the popup requests to be opened.
* Use in controlled mode (see open).
@@ -165,18 +148,65 @@ export interface UseAutocompleteProps {
/**
* Array of options.
*/
- options?: any[];
+ options?: T[];
+}
+
+export interface UseAutocompleteMultipleProps extends UseAutocompleteCommonProps {
+ /**
+ * If `true`, `value` must be an array and the menu will support multiple selections.
+ */
+ multiple: true;
/**
* The value of the autocomplete.
*
* The value must have reference equality with the option in order to be selected.
* You can customize the equality behavior with the `getOptionSelected` prop.
*/
- value?: any;
+ value?: T[];
+ /**
+ * The default input value. Use when the component is not controlled.
+ */
+ defaultValue?: T[];
+ /**
+ * Callback fired when the value changes.
+ *
+ * @param {object} event The event source of the callback.
+ * @param {T[]} value
+ */
+ onChange?: (event: React.ChangeEvent<{}>, value: T[]) => void;
+}
+
+export interface UseAutocompleteSingleProps extends UseAutocompleteCommonProps {
+ /**
+ * If `true`, `value` must be an array and the menu will support multiple selections.
+ */
+ multiple?: false;
+ /**
+ * The value of the autocomplete.
+ *
+ * The value must have reference equality with the option in order to be selected.
+ * You can customize the equality behavior with the `getOptionSelected` prop.
+ */
+ value?: T | null;
+ /**
+ * The default input value. Use when the component is not controlled.
+ */
+ defaultValue?: T;
+ /**
+ * Callback fired when the value changes.
+ *
+ * @param {object} event The event source of the callback.
+ * @param {T} value
+ */
+ onChange?: (event: React.ChangeEvent<{}>, value: T | null) => void;
}
-export default function useAutocomplete(
- props: UseAutocompleteProps,
+export type UseAutocompleteProps =
+ | UseAutocompleteSingleProps
+ | UseAutocompleteMultipleProps;
+
+export default function useAutocomplete(
+ props: UseAutocompleteProps,
): {
getRootProps: () => {};
getInputProps: () => {};
@@ -185,15 +215,17 @@ export default function useAutocomplete(
getPopupIndicatorProps: () => {};
getTagProps: ({ index }: { index: number }) => {};
getListboxProps: () => {};
- getOptionProps: ({ option, index }: { option: any; index: number }) => {};
+ getOptionProps: ({ option, index }: { option: T; index: number }) => {};
id: string;
inputValue: string;
- value: any;
+ // TODO: infer the right type when the issue is resolved
+ // https://github.com/microsoft/TypeScript/issues/13995
+ value: any; // or T | T[]
dirty: boolean;
popupOpen: boolean;
focused: boolean;
anchorEl: null | HTMLElement;
setAnchorEl: () => void;
focusedTag: number;
- groupedOptions: any[];
+ groupedOptions: T[];
};
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index 4037be9ee9ae2e..47443f48a59ada 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -1007,9 +1007,9 @@ useAutocomplete.propTypes = {
/**
* Callback fired when the text input value changes.
*
- * @param {object} event The event source of the callback
- * @param {string} value The new value of the text input
- * @param {string} reason One of "input" (user input) or "reset" (programmatic change)
+ * @param {object} event The event source of the callback.
+ * @param {string} value The new value of the text input.
+ * @param {string} reason One of "input" (user input) or "reset" (programmatic change).
*/
onInputChange: PropTypes.func,
/**
diff --git a/packages/material-ui/src/BottomNavigation/BottomNavigation.d.ts b/packages/material-ui/src/BottomNavigation/BottomNavigation.d.ts
index a1fdf776480a78..cd1a6dc921b40d 100644
--- a/packages/material-ui/src/BottomNavigation/BottomNavigation.d.ts
+++ b/packages/material-ui/src/BottomNavigation/BottomNavigation.d.ts
@@ -15,8 +15,8 @@ export interface BottomNavigationProps
/**
* Callback fired when the value changes.
*
- * @param {object} event The event source of the callback
- * @param {any} value We default to the index of the child
+ * @param {object} event The event source of the callback.
+ * @param {any} value We default to the index of the child.
*/
onChange?(event: React.ChangeEvent<{}>, value: any): void;
/**
diff --git a/packages/material-ui/src/BottomNavigation/BottomNavigation.js b/packages/material-ui/src/BottomNavigation/BottomNavigation.js
index b76a1ff0fc2f8f..b0d7c1c6dee8d1 100755
--- a/packages/material-ui/src/BottomNavigation/BottomNavigation.js
+++ b/packages/material-ui/src/BottomNavigation/BottomNavigation.js
@@ -83,8 +83,8 @@ BottomNavigation.propTypes = {
/**
* Callback fired when the value changes.
*
- * @param {object} event The event source of the callback
- * @param {any} value We default to the index of the child
+ * @param {object} event The event source of the callback.
+ * @param {any} value We default to the index of the child.
*/
onChange: PropTypes.func,
/**