2

With this code:

else if (event.target.id == 'filter') {
            this.setState({ [event.target.id]: event.target.value });

I get this typescript error: TS2345 TypeScript (TS) Argument of type '{ [x: number]: any; }' is not assignable to parameter of type '{ id: number; redirect: boolean; filter: string; player: Player; } | ((prevState: Reado...'.' …

But if I instead do this:

else if (event.target.id == 'filter') {
            this.setState({ filter: event.target.value });

there's no error. Despite the error, the code runs fine. I think this started with TypeScript 2.9. I realize I can just use the second example, but I have other code like:

handleChange(event) {
        if (event.target.type == 'checkbox') {
            this.setState({ [event.target.id]: event.target.checked });
        } else {
            this.setState({ [event.target.id]: event.target.value });
        }
    }

which is useful. Is there a better way to do this in TypeScript 2.9?

Update: also relevant:

type PlayerListState = {
    id: number,
    redirect: boolean,
    filter: string,
    player: GamePlayer
};

class PlayerListComponent extends React.Component<PlayerListProps, PlayerListState> {

and the type definition of SetState from React:

    setState<K extends keyof S>(
        state: ((prevState: Readonly<S>, props: P) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
        callback?: () => void
    ): void;
4
  • ... wait, isn't id a number? How would it equal the string "filter"? Commented Jun 6, 2018 at 13:40
  • It's the DOM element ID. Commented Jun 6, 2018 at 15:13
  • Okay, makes sense Commented Jun 6, 2018 at 15:21
  • Still, if your error is Argument of type '{[x: number]: any;}..., it implies that TypeScript thinks event.target.id is a number. Are you sure about that error message? Commented Jun 6, 2018 at 15:22

1 Answer 1

1

I tend to belt-and-braces my element types when there is chance they won't be what I expect.

Here is an example adapted from this blog post on narrowing types.

function isInputElement(target: EventTarget | any): target is HTMLInputElement {
  if (!target) {
    return false;
  }

  return (target.tagName && target.tagName === 'INPUT')
}

// Note: Arbitrary element selection to make the demo complete :)
document.getElementById('filter').onload = (e) => {
    const target = e.target;

    if (!isInputElement(target)) {
        return;
    }

    if (target.id == 'filter') {
        this.setState({ [target.id]: target.value });
    }
}

Within the type guard, you can throw an exception, log a message, or take whatever action is needed when the event target isn't actually an INPUT element.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Fenton, that was really helpful, as well as the blogpost. It took me forward but I'm still having a problem. The state is strongly typed and it now gives me: Argument of type '{ [x: string]: any; }' is not assignable to parameter of type 'PlayerListState … Type '{ [x: string]: any; }' is not assignable to type 'Pick<PlayerListState, "id" | "filter" | "redirect" | "player">' It's accurate that I should be passing in a string with one of those four values to the React component state. Is there a way for me to typecheck this similar to what you already showed me?
I might need to see the types Pick and PlayerListState to answer that one.
I've updated the question with what I hope is helpful info @Fenton
Also, Pick is defined as part of TypeScript: medium.com/@curtistatewilkinson/…
Marking this as resolved since you got me over the original hump. I'm dropping back down to TypeScript 2.8 as there seem to be some rough edges as of now with React. Thanks again...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.