How to get input value in TypeScript

Hi guys, I am new to Ember, try to make simple todo app by my own after YT tutorial. However I have a problem when I try to access input value.

Property ‘value’ does not exist on type ‘EventTarget’

My function:

updateNewItemValue(event: InputEvent) {
    this.newItem = event!.target.value;
 }

Using it in template:

<input
    type='text'
    {{on 'input' this.updateNewItemValue}}
    value={{this.newItem}}
/>

Maybe am I using wrong type for event argument?

:wave: welcome, @crazydev09!

This is an annoying subtlety about how the underlying runtime behavior works, which TypeScript is faithfully representing. An Event.target, including InputEvent.target, can be null. The reason is that you can construct those events yourself:

let myInputEvent = new InputEvent('input');
// no `target` here! 😱

In this case, you can see from inspecting the code that it is in fact wired up via a modifier in a way that means it will always have a target set, but that isn’t something TS can currently understand. Theoretically, it might be possible for TS to improve that in the future, but for today, that’s just a given.

In general, I deal with this by using the assert function from @ember/debug. These assertions will be stripped out of production builds, but allow you to check for this kind of safety in development and testing, and in a type-safe way using TypeScript assertion functions. (See below for a discussion of assertion functions.) Here’s how that might look:

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { assert } from '@ember/debug';

export default class Example extends Component {
  @tracked newItem: string | undefined;

  @action
  updateNewItemValue(event: InputEvent) {
    assert(
      'updateNewItemValue must be bound to an input element',
      event.target instanceof HTMLInputElement
    );

    this.newItem = event.target.value;
  }
}

You can see in this TS playground that this type checks!

If you’re unfamiliar, asserts functions use TypeScript’s assertion functions to tell the compiler that control flow stops at that point unless the condition is met. Or, to flip it around: it tells TypeScript it can know the condition is true if the assertion passes. You can implement your own—for example, if you want to leave the assertions present in production for cases where a good error message is really important for debugging:

export function assert(message: string, predicate: unknown): asserts predicate {
  if (!predicate) {
    throw new Error(message);
  }
}
1 Like

thank you very much, it works now