Reputation: 4212
I am using the CreatableSelect
component from React Select in my application but I am having trouble adding Typescript types.
All I want to do is to change any
from handleChange = (value: any)
with the right types. I used this const handleChange = (value: { label: any; value: any }[]) => {
but it does not work.
How can I change any
type to the right type?
demo: https://codesandbox.io/s/codesandboxer-example-forked-9tsfp?file=/example.js
export default class CreatableInputOnly extends Component<*, State> {
state = {
inputValue: "",
value: []
};
handleChange = (value: any) => {
console.log(value, "test");
this.setState({ value });
};
handleInputChange = (inputValue: string) => {
this.setState({ inputValue });
};
handleKeyDown = (event: SyntheticKeyboardEvent<HTMLElement>) => {
const { inputValue, value } = this.state;
if (!inputValue) return;
switch (event.key) {
case "Enter":
case "Tab":
console.group("Value Added");
console.log(value);
console.groupEnd();
this.setState({
inputValue: "",
value: [...value, createOption(inputValue)]
});
event.preventDefault();
}
};
render() {
const { inputValue, value } = this.state;
return (
<CreatableSelect
components={components}
inputValue={inputValue}
isClearable
isMulti
menuIsOpen={false}
onChange={this.handleChange}
onInputChange={this.handleInputChange}
onKeyDown={this.handleKeyDown}
placeholder="Type something and press enter..."
value={value}
/>
);
}
}
Upvotes: 0
Views: 8717
Reputation: 42298
The specific types depend on a generic type parameter OptionType
. The default type for OptionType
is the { label: string; value: string }
type which you are using.
I believe that the issue you are having is due to incorrect/outdated type packages in your environment.
In a new CodeSandbox, where I installed @types/react-select
, the definition for the onChange
function is:
onChange?: (value: ValueType<OptionType, IsMulti>, action: ActionMeta<OptionType>) => void;
This ValueType
utility type basically says that on a single select you have either an option or null
and on on multiple select you have an array
of options.
export type ValueType<OptionType extends OptionTypeBase, IsMulti extends boolean> = IsMulti extends true
? OptionsType<OptionType>
: OptionType | null;
export type OptionsType<OptionType extends OptionTypeBase> = ReadonlyArray<OptionType>;
I'm not sure how your linked sandbox is enqueing its types because the @types/react-select
package is not included in the package.json
. But if I "Go To Definition" I get a definition from a /sandbox/node_modules/@types/react-select/src
file. These are the types that I see:
onChange?: (value: ValueType<OptionType>, action: ActionMeta) => void;
export type ValueType<OptionType extends OptionTypeBase> = OptionType | OptionsType<OptionType> | null | undefined;
export type OptionsType<OptionType extends OptionTypeBase> = ReadonlyArray<OptionType>;
So what's the difference?
In the first case, we know that since you have a multi-select you will always receive an array of options.
In the second case there is no IsMulti
flag, so the types state that you could receive an array or a single option (or null
or undefined
). Your handleChange
function can only handle an array, so it can't be used as an onChange
handler since it can't handle all of the possible arguments that it might receive.
With the correct package types, what you tried before is almost correct. The only issue is that the array is readonly
, so we have to make sure that we can accept a ReadonlyArray
in both the handler and the state.
FYI these functions aren't being called where I would intuitively expect. handleChange
is called when removing a tag but not when adding a new one by pressing enter. So you'll want to check out the docs and/or play with the various callbacks. There are six props which are only on CreatableSelect
-- two settings and four callbacks.
This should fix all of your Typescript errors, using @types/react-select
4.0.13 and react-select
4.3.0:
import React, { Component } from "react";
import CreatableSelect from "react-select/creatable";
import { ActionMeta, InputActionMeta } from "react-select";
const components = {
DropdownIndicator: null
};
const createOption = (label: string) => ({
label,
value: label
});
type MyOption = ReturnType<typeof createOption>;
// or type MyOption = { label: string; value: string; }
interface MyState {
inputValue: string;
value: ReadonlyArray<MyOption>;
}
interface MyProps {
// are there any?
}
export default class CreatableInputOnly extends Component<MyProps, MyState> {
state: MyState = {
inputValue: "",
value: []
};
handleChange = (
value: ReadonlyArray<MyOption>,
meta: ActionMeta<MyOption>
) => {
console.log("value", value, "meta", meta);
this.setState({ value });
};
handleInputChange = (inputValue: string, meta: InputActionMeta) => {
this.setState({ inputValue });
};
handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
const { inputValue, value } = this.state;
if (!inputValue) return;
switch (event.key) {
case "Enter":
case "Tab":
console.group("Value Added");
console.log(value);
console.groupEnd();
this.setState({
inputValue: "",
value: [...value, createOption(inputValue)]
});
event.preventDefault();
}
};
render() {
const { inputValue, value } = this.state;
return (
<CreatableSelect
components={components}
inputValue={inputValue}
isClearable
isMulti
menuIsOpen={false}
onChange={this.handleChange}
onInputChange={this.handleInputChange}
onKeyDown={this.handleKeyDown}
placeholder="Type something and press enter..."
value={value}
/>
);
}
}
Upvotes: 3