Home > Enterprise >  Set type for a key of an object from a set of strings in typescript
Set type for a key of an object from a set of strings in typescript

Time:12-12

I got such a piece of code in my React component:

  type TLang = 'tur' | 'eng';
  const [filterState, setFilterState] = useState<{
    [key in TLang]: string | null;
  }>(null);

  const inputChangeHadnler =
    (type: TLang): React.ChangeEventHandler<HTMLInputElement> =>
    (event) => {
      setFilterState({ [type]: event.target.value });
    };
 
  return <input onChange={inputChangeHadnler('tur')}></input>}

the line with the setFilterState states an error:

Argument of type '{ [x: string]: string; }' is not assignable to parameter of type 'SetStateAction<{ tur: string; eng: string; }>'. Type '{ [x: string]: string; }' is missing the following properties from type '{ tur: string; eng: string; }': tur, engts(2345)

As I understand, it shows the filrerstate object must have two properties: tur and eng, but I want it to have only one key, tur or eng, from the TLang type.

Are there any ways to say here: useState<{[key in TLang]: string | null;}>(null);, that that object should have only one value: 'tur' or 'eng'?

My only idea is just to say: useState<{ tur: string | null } | { eng: string | null} | null>(null)>, but I doubt it works if I have more languages.

CodePudding user response:

  • The event.target can, in general, be any element. With TypeScript, use .currentTarget so that TypeScript knows that the element being referred to is definitely the element the handler is attached to, rather than any of its children - that'll let it be inferred as an HTMLInputElement.
  • React generally works better with controlled components, and the same is true with TypeScript. Consider having a state for the input value that gets changed onChange of the input.
  • inputChangeHadnler should probably be inputChangeHandler (typos are a frequent source of bugs in programming - fix them as soon as you see them to reduce confusion)

Are there any ways to say here: useState<{[key in TLang]: string | null;}>(null);, that that object should have only one value: 'tur' or 'eng'?

You could - { tur: string | null } | { eng: string | null } - but instead to implement that sort of logic, you might consider creating another state for which language is being used.

const App = () => {
    const [inputValue, setInputValue] = useState('');
    const [lang, setLang] = useState<TLang>('tur');
    return <input value={inputValue} onChange={(e) => { setInputValue(e.currentTarget.value); }} />;
};

Now you have two very easy to manage string states - the input value, and the language. If you need to create a filterState object somewhere, you can now do so on demand from the existing states.

CodePudding user response:

Maybe defining state type as optional for every lang would help?

It should handle adding more languages.

Check demo Codesanbox example

  • Related