'Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element'. Type 'null' is not assignable to type 'Element'.ts(2345)
I have index.html
<body>
<div id="portal"></div>
<div id="root"></div>
</body>
and want to use the component below in separate portal div
than root div
,
import React from 'react';
const portalDiv = document.getElementById('portal');
function Portal1(props) {
return ReactDOM.createPortal(
<div>
{props.children}
<div/>,
portalDiv); //here
}
export default Portal1;
But I am getting this error, Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element'. Type 'null' is not assignable to type 'Element'.ts(2345) in VScode.
I am using Typescript.
Solution 1:[1]
Other people have answered that you should add a null-check, but Typescript also has a non-null assertion that you can use when you are sure that the value is never null by adding the !
operator to the end of your statement:
const portalDiv = document.getElementById('#your-element')!;
Solution 2:[2]
So I dont know if anyone is still having this problem but there's an easier and straightforward solution to this.
simply declare the modal element with "as" keywordconst modalRoot = document.getElementById("modal-root") as HTMLElement;
This removes the error.
I suggest looking through this great react-typescript cheatsheet.
https://github.com/typescript-cheatsheets/react
Solution 3:[3]
Since getElementById
possibly returns null. So you just simply check before using like:
function Portal1({ children }) {
return portalDiv ? ReactDOM.createPortal(<>{children}</>, portalDiv) : null;
}
Solution 4:[4]
When we select an HTMLElement inside a DOM Tree using getElementById
it returns the Element within the document that matches the specified selector, if no match is found it basically returns null.
Let's get the portal element:
let portalDiv = document.getElementById("portal");
If the portal exists in the DOM tree it will return the HTMLElemnt
in case of not found it returns null
.
So the return type of getElementById
is HTMLElement | null
, since the 2nd argument of the ReactDom.createPrortal(children: ReactNode, element: HTMLElement, id: string)
is strictly HTMLElement
we have to caste our portalDiv
to HTMLElement
inorder to get rid of the warning.
Since we are sure that the portalDiv
do exists we can use this method to deal with the null
conditions.
let portalDiv = getElementById("portal") as HTMLElement;
now we can use portalDiv
without getting any errors:
function Portal1(props) {
return ReactDOM.createPortal(
<div>
{props.children}
<div/>,
portalDiv);
}
Solution 5:[5]
getElementById
can return null
, but createPortal
doesn't accept null
.
If you know the portal div will exist, make the code explicit about that:
const portalDiv = document.getElementById('portal');
if (!portalDiv) {
throw new Error("The element #portal wasn't found");
}
That will allow TypeScript to narrow the type of the constant, removing the | null
part of the type. It also gives you a nice proactive warning if someone changes things such that the div isn't there when this code runs anymore.
Solution 6:[6]
I think the best solution to this is not to make it either null or HTMLDIVElement but rather in the use-case try to let typescript know that the DivElement might be empty at the moment, but you will take responsibility of it by just using "!" symbol.
Code sample below:
import React, {useEffect} from 'react';
import ReactDOM from 'react-dom';
import './modal-portlet.style.scss';
const modalRoot = document.getElementById('modalRoot');
type Props = {
children: JSX.Element;
};
const ModalPortlet: React.FC<Props> = ({children}): JSX.Element => {
const divContainer = window.document.createElement('div');
divContainer.setAttribute('class', 'modal-container');
useEffect(() => {
/**********
* immediately the component mount append @divContainer as the childNode of @modalRoot in the DOM
**********/
modalRoot!.appendChild(divContainer);
return () => {
modalRoot!.removeChild(divContainer);
};
});
/************
* return the createPortal api that takes the children(JSX) and inject it into @divContainer which is already a childNode of @modalRoot
************/
return <>{ReactDOM.createPortal(children, divContainer)}</>;
};
export default ModalPortlet;
Solution 7:[7]
I was also facing same error so I tried few different ways but the one that worked for me is handling null condition
i.e. const portalDiv = document.getElementById('portal')!;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Titulum |
Solution 2 | Oluwaseyitan Baderinwa |
Solution 3 | tmhao2005 |
Solution 4 | Biswarup Bouri |
Solution 5 | T.J. Crowder |
Solution 6 | Emmanuel Onah |
Solution 7 | Priyanshu Jain |