1

I am trying to create a function using typescript that checks for the "Enter" key press and then if the length of the event.target.value is greater than 0, go to a page with the value. This is in a nextjs app hence the router.push line. Code roughly looks like this:

const goToSearch = (e: React.KeyboardEvent<HTMLInputElement> | React.ChangeEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      e.preventDefault();

      if (e.target.value.length > 0) {
        router.push(
          { pathname: "/Search", query: { searchVal: e.target.value } },
          "/Search"
        );
      }
    }
  };

To trigger this function, Im using onKeyDown prop in input element, like this:

  <input
    type="search"
    className="form-control border border-white"
    placeholder="Search"
    aria-label="Search"
    onKeyDown={goToSearch}
  />

The challenge here is since Im using typescript, the e type can be a Keyboard event or a Change Event. If I use the | to provide both types, I get errors since typescript is confused which event type to reference. Is there any way I can create an interface or some custom type that leverages both event types (a keyboard event vs a change event)? This interface or custom type will also need to take into account the preventDefault void function as well.

Thanks

Ray
  • 1,548
  • 2
  • 11
  • 18
  • Look at type guards https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types – ksav Feb 18 '21 at 05:14

1 Answers1

3

You can use a type guard to assert that the event is a specific one of the events in the union type before attempting to access properties that do not exist on both types in the union type.

const goToSearch = (e: React.KeyboardEvent<HTMLInputElement> | 
React.ChangeEvent<HTMLInputElement>) => {
  const {key} = (e as React.KeyboardEvent<HTMLInputElement>)
  const {target} = (e as React.ChangeEvent<HTMLInputElement>)
  if (key === "Enter") {
    e.preventDefault();
  }
  if (target.value.length > 0) {
    // change route
  }
};

TS Playground


The above could also be written as

const goToSearch = (e: React.KeyboardEvent<HTMLInputElement> | React.ChangeEvent<HTMLInputElement>) => {
  if ((e as React.KeyboardEvent<HTMLInputElement>) && (e as React.KeyboardEvent<HTMLInputElement>).key === "Enter") {
    e.preventDefault();
  }

  if ((e as React.ChangeEvent<HTMLInputElement>) && (e as React.ChangeEvent<HTMLInputElement>).target.value.length > 0) {
    // change route
  }
};

User-Defined Type Guards

ksav
  • 20,015
  • 6
  • 46
  • 66
  • This worked. Can you explain how you came to this solution just for my understanding. You created two deconstructed variables from the event object and set their types specifically to each type stated in the parameter? Now that they are seperate and associated with a specific variable, they avoid the type confusion? – Ray Feb 18 '21 at 13:59
  • I had a read of the discussions on this question before coming up with this answer. https://stackoverflow.com/questions/44321326/property-value-does-not-exist-on-type-eventtarget-in-typescript – ksav Feb 19 '21 at 00:17