Can it be that TypeScript DOM types for events and React Event types don't go well with another?
Look at this code:
useEffect(() => {
const onScroll = (event: React.ChangeEvent<Body>) => {
console.log("event", event.target);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
It throws
No overload matches this call.
Overload 1 of 2, '(type: "scroll", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions | undefined): void', gave the following error.
Argument of type '(event: ChangeEvent<Body>) => void' is not assignable to parameter of type '(this: Window, ev: Event) => any'.
Types of parameters 'event' and 'ev' are inpatible.
Type 'Event' is not assignable to type 'ChangeEvent<Body>'.
Than I tried the following. This piles correctly.
useEffect(() => {
const onScroll = (event: Event) => { // <--- changed event type here
console.log("event", event.target);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
event.target
can not be access without pile errors, though. I checked the following function call (it is working in the browser if I ts-ignore the error):
useEffect(() => {
const onScroll = (event: Event) => {
console.log(
"event",
event?.target?.activeElement.getBoundingClientRect() // <--- this function call is actually working (ts-ignored it), even if TypeScript throws an error
);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
throws
TypeScript error in /home/sirhennihau/Workspace/giphy-search/src/Components/grid.tsx(27,24):
Property 'activeElement' does not exist on type 'EventTarget'. TS2339
25 | console.log(
26 | "event",
> 27 | event?.target?.activeElement.getBoundingClientRect()
| ^
28 | );
29 | };
30 |
My dependencies:
"@types/node": "^12.0.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^17.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"typescript": "^4.0.3"
Are maybe just the typings bad or am I doing something wrong?
Can it be that TypeScript DOM types for events and React Event types don't go well with another?
Look at this code:
useEffect(() => {
const onScroll = (event: React.ChangeEvent<Body>) => {
console.log("event", event.target);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
It throws
No overload matches this call.
Overload 1 of 2, '(type: "scroll", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions | undefined): void', gave the following error.
Argument of type '(event: ChangeEvent<Body>) => void' is not assignable to parameter of type '(this: Window, ev: Event) => any'.
Types of parameters 'event' and 'ev' are inpatible.
Type 'Event' is not assignable to type 'ChangeEvent<Body>'.
Than I tried the following. This piles correctly.
useEffect(() => {
const onScroll = (event: Event) => { // <--- changed event type here
console.log("event", event.target);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
event.target
can not be access without pile errors, though. I checked the following function call (it is working in the browser if I ts-ignore the error):
useEffect(() => {
const onScroll = (event: Event) => {
console.log(
"event",
event?.target?.activeElement.getBoundingClientRect() // <--- this function call is actually working (ts-ignored it), even if TypeScript throws an error
);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
throws
TypeScript error in /home/sirhennihau/Workspace/giphy-search/src/Components/grid.tsx(27,24):
Property 'activeElement' does not exist on type 'EventTarget'. TS2339
25 | console.log(
26 | "event",
> 27 | event?.target?.activeElement.getBoundingClientRect()
| ^
28 | );
29 | };
30 |
My dependencies:
"@types/node": "^12.0.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^17.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"typescript": "^4.0.3"
Are maybe just the typings bad or am I doing something wrong?
Share Improve this question asked Jan 1, 2021 at 15:10 Sir hennihauSir hennihau 1,7846 gold badges23 silver badges38 bronze badges1 Answer
Reset to default 10Well, yes, React-Events and DOM-Events sometimes don't "go well" with one another, but there are clean ways to handle your use case (and most of all use cases in React, in my experience):
DOM events
The DOM methond window.addEventListener
expects a DOM-EventListener
(
(evt: Event): void;
), so you need to use the DOM-Event
interface (as you did).
Here I added some types to your code to illustrate this:
useEffect(() => {
const onScroll: EventListener = (event: Event) => { // <-- DOM-EventListener
console.log("event", event.target);
};
const win: Window = window; // <-- DOM-Window, extends DOM-EventTarget
win.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
event.target
The event.target
should be accessible.
I assume you mean that you get errors if you try to acces event.target.activeElement
.
event.target.activeElement
throws an error, because Event
could be any event,
event.target
could be any element that extends EventTarget | null
,
which is no specific element, and maybe has no activeElement
.
But it works despite of the TS error, because in reality it is an element that has the .activeElement
.
const onScroll: EventListener = (event: Event) => {
const targetAny: EventTarget | null = event.target; // <-- maybe has no .activeElement
console.log( targetAny.activeElement );
};
If you are sure what the element is, you might type assert the specific element:
const onScroll: EventListener = (event: Event) => {
const targetDiv: HTMLDocument = event.target as HTMLDocument; // <-- assert DOM-HTMLDocument
console.log("DOM event", targetDiv.activeElement); // <-- activeElement works
};
React events
The React-Types should be used with React-Elements.
You still would have to assert the type of event.target
to access the properties of a specific element (here I use .style
), because the .target
of the React.SyntheticEvent
(which extends React.BaseSyntheticEvent
) is exactly the same DOM-EventTarget
as the DOM-Event
uses.
To be clear: yes, React also uses some DOM-Types, so here React "goes well" with DOM-Events.
const onScrollReact: React.EventHandler<React.UIEvent<ReactNode>> = (event: React.UIEvent<React.ReactNode> ) => {
const target: EventTarget = event.target; // <-- React uses the DOM EventTarget
const targetDiv: HTMLDivElement = target as HTMLDivElement; // <-- type assert DOM-HTMLDivElement
console.log("React event", target.style); // <-- throws TS error
console.log("React event", targetDiv.style); // <-- no TS error
};
return (<div
onScroll={ onScrollReact }
style={{ height: '400px', overflow: 'scroll' }}
>
<div style={{ height: '2000px', backgroundColor: '#eee' }}>
try scroll event
</div>
</div>);
You could alternatively use the DOM-HTMLDivElement
in this example (the behavior is exactly the same, but it is more 'declarative', I guess):
const onScrollReact2: React.EventHandler<React.UIEvent<HTMLDivElement>> = (event: React.UIEvent<HTMLDivElement>) => {
console.log("React event", event.target.style); // <-- still throws TS error
};