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

11

117

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.

Subtilize answered 21/8, 2020 at 10:0 Comment(4)
Hi, you should change the accepted answer?Bourse
@Bourse Accepted answer needn't always be the one with highest score, right? The one I've accepted was the first one to answer and get me out of stuck. so thanks for suggestion. I've also upvoted all the answers on this, since all are useful.Subtilize
Yes the highest one is the most relevant and should be accepted nowBourse
No. Accepted answer is the one which helped you resolve issue first. 5 years later if someone answers newly and gets highest upvote, doesnt mean it helped the Asker at time it was asked. okay? peace.Subtilize
S
274

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')!;
Streptococcus answered 21/8, 2020 at 10:9 Comment(9)
Yeah, good one indeed. (for added info, some time, typescript, probably latest, suggest to use ? at same place instead of !. works fine.Subtilize
! 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.Smithy
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.Streptococcus
kind of a noob to this operator, can you link docs page for this?Widdershins
@Widdershins typescriptlang.org/docs/handbook/release-notes/…Streptococcus
You should delete # from your selector.Bregenz
Wow I can't believe nobody mentioned this already. Thank you @Hooman, updating the response.Streptococcus
This is where I have added the '!'. Thanks for who raised this issue. But why TS can't tell this? <code> const root = ReactDOM.createRoot(document.getElementById('root')! as HTMLElement ); </code>Knell
This is one of the few times using the non-null assertion operator is correct.Cacoepy
J
78

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

Jarodjarosite answered 24/2, 2021 at 20:53 Comment(3)
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?Subtilize
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 itJarodjarosite
I like this method because anyone coming across the code would clearly see what the type is. If you wanted, you could always combine the ? & the as keyword. So you could do something like const modalRoot = document.getElementById("modal-root") as HTMLElement; & then when you need to access it and its properties do: modalRoot?.foobar.Afflux
C
30

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);
}
Carliecarlile answered 28/4, 2022 at 6:14 Comment(0)
B
12

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

function Portal1({ children }) {
  return portalDiv ? ReactDOM.createPortal(<>{children}</>, portalDiv) : null;
}
Box answered 21/8, 2020 at 10:4 Comment(0)
T
8

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.

Teenateenage answered 21/8, 2020 at 10:6 Comment(0)
F
2

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;
Finsen answered 11/1, 2021 at 0:22 Comment(0)
A
2

There is simple solution just add as HTMLAudioElemnt at the end of get Element by ID const element = document.getElementById(auduiId) as HTMLAudioElement;

Apivorous answered 17/8, 2022 at 13:4 Comment(0)
E
2

If you are switching React.js files from Javascript to Typescript using node.js, this should be the fix code for you in index.ts, line 8:

const root = ReactDOM.createRoot(document.getElementById('root')!);
Epiphany answered 8/12, 2022 at 13:22 Comment(0)
A
1

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')!;

Abuse answered 23/1, 2022 at 8:19 Comment(1)
what does the ! do?Arbitration
I
1

Since the return type of the function is HTMLElement | null you have to declare the variable as those types or use the as keyword.

const portalDiv: HTMLElement|null = document.getElementById('portal');

Or using the as keyword we can allow the return type to be something other than HTMLElement

const portalDiv = document.getElementById('portal') as HTMLElement;

As mentioned in another answer you can turn off this typescript feature by setting the compiler argument, "strictNullChecks" to false.

Involucrum answered 11/10, 2023 at 19:44 Comment(0)
S
-3

For a quick fix just add "strictNullChecks": false to your tsconfig.json file.

Stroganoff answered 16/9, 2022 at 12:0 Comment(2)
This will impoverish the developer experience.Epiphany
It could affect the entire appFenestrated

© 2022 - 2024 — McMap. All rights reserved.