2
votes

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;
1
... wait, isn't id a number? How would it equal the string "filter"?jcalz
It's the DOM element ID.BenjiFB
Okay, makes sensejcalz
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?jcalz

1 Answers

1
votes

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.