Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element'. Type 'null' is not assignable to type 'Element'.ts(2345)

29,516

Solution 1

Since getElementById possibly returns null. So you just simply check before using like:

function Portal1({ children }) {
  return portalDiv ? ReactDOM.createPortal(<>{children}</>, portalDiv) : null;
}

Solution 2

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 3

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" keyword
const 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 4

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 5

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;
Share:
29,516
Kishore
Author by

Kishore

Hi there, I am an application developer, CSE graduate and I am more into practical programming. I like solving possible daily life problems using codes.

Updated on January 23, 2022

Comments

  • Kishore
    Kishore over 2 years

    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. Please help.

  • Kishore
    Kishore over 3 years
    Yeah, good one indeed. (for added info, some time, typescript, probably latest, suggest to use ? at same place instead of !. works fine.
  • Kishore
    Kishore about 3 years
    yeah, that might work, but we call that as inline typecasting, i.e., we're just changing type in place. still I feel, if there is null values, that is, if the div doesn't exist, it will still give null. and the typecasting null as HTMLElement might give error. did you try applying changes?
  • Oluwaseyitan Baderinwa
    Oluwaseyitan Baderinwa about 3 years
    I did apply it and it seemed to be the only way to fix the error. And the cheatsheet gives more details as to why this method is recommended. Btw, the HTMLElement type is Element|null type so I don't think it'll give an error. I already tested it
  • gcr
    gcr about 3 years
    ! back at ya! I don't know if the above if () {} solutions were possible in my case (maybeI just don't know how) as the jsx library (react-modal) expects a callback returning an html element, not the element directly, so I don't know at what point I could have done the check and got TS to recognize it. So glad you shared. I just wanna know if/when I'd be abusing some escape hatch like this. I wanna be a user not an abuser but maybe this is why they created it.
  • Titulum
    Titulum about 3 years
    Well, take a look at this example. The ! check on line 7 is required, because typescript is not 'smart' enough to see that you already checked whether the value is not null in another function.
  • DinoSaadeh
    DinoSaadeh about 2 years
    kind of a noob to this operator, can you link docs page for this?
  • Titulum
    Titulum about 2 years
  • Rohan Bhangui
    Rohan Bhangui about 2 years
    what does the ! do?