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 incompatible.
Type 'Event' is not assignable to type 'ChangeEvent<Body>'.
Than I tried the following. This compiles 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 compile 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?
Well, 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):
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.targetThe 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
};
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
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With