13

I'm using vanilla JavaScript with TypeScript as pre-processor in combination with JSDoc.
That pretty much works flawlessly, especially in the backend (when using in NodeJS, for instance).

However, when I use it with DOM objects, things get a bit tricky.

For example: Say I have an HTML Input field, I catch the input event and want to access the input's value with e.target.value:

/**
 * Log data on input
 *
 * @param {Event} e
 */
let handleEvent = function(e){
    console.log(e.target.value);
};

document.getElementById("my-input").addEventListener("input", handleEvent);

This results in a TS warning:

Property 'value' does not exist on type 'EventTarget'

As seen here:

TypeScript Warning

Now my question is, what's the correct @param annotation?

So far I've tried Event, InputEvent and HTMLInputElement.

I don't want to use Type-Assertion. Instead I'd like to know how to specify it in the functions annotations directly, so I do not have to set @type for each and every occurrence of e.target.value individually as suggested here.

0

1 Answer 1

25

You can write your @param annotation like that:

/**
 * Log data on input
 *
 * @param {Event & { target: HTMLInputElement }} e
 */
let handleEvent = function(e){
    console.log(e.target.value);
};

Then you get full support for the correct types:

1

The reason why this works, is that you're basically "extending" the Event class by the new target property - which already exists, so it gets overridden with our new type.

The ampersand per se is not a jsdoc operator, but one from TypeScript. So this only works out if you keep using TypeScript as your preprocessor. See this for a more detailed explanation.

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

4 Comments

Sadly not a lot less typing¹ than the type assertion. This approach is reasonable, but it has the feeling of a workaround. Still, if Event/InputEvent doesn't have a generic type parameter (and it seems not to), I'm not sure what else you'd do. ¹ "typing" in the sense of pressing keys on a keyboard
@T.J.Crowder Imo it actually is less typing if you use e in the function body a lot. Doesn't feel like a workaround to me since I explicitly state the type of the parameter. The only "workaround" I could think of, would be accessing this instead of e. Nonetheless, I'm pretty happy with this solution :)
@NullDev - You'd grab target to a local, once. var target = /** @type {HTMLInputElement} */ (e.target); But this & solution is very nearly the same as using a generic type parameter (actually, now I think about it, I'm not sure what the type parameter would offer other than a bit more conciseness), so seems pretty good. :-)
As you mentioned, it just workd with TypeScript, not pure JS only with JSDoc. In this case the trick works on the function, but not when calling it in addEventListener( handleEvent ). Let's assume we want to use target and originalTarget. JSDoc outputs this error on the parameter: Argument of type 'MouseEvent' is not assignable to parameter of type 'MouseEvent & { originalTarget: HTMLInputElement; target: HTMLInputElement; }'. Property 'originalTarget' is missing in type 'MouseEvent' but required in type '{ originalTarget: HTMLInputElement; target: HTMLInputElement; }'.ts(2345)

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.