Home > front end >  Trying to use Image from "next/Image" throws a TypeScript error
Trying to use Image from "next/Image" throws a TypeScript error

Time:02-04

I have a component that looks like this:

import React, { FC, ImgHTMLAttributes, useEffect, useRef, useState } from "react";
import NextImage from "next/image";
import checkInViewIntersectionObserver from "@/utils/isInViewPortIntersectionObserver";
import PlaceIcon from "./PlaceIcon";

export interface NcImageProps extends ImgHTMLAttributes<HTMLImageElement> {
  containerClassName?: string;
}

const NcImage: FC<NcImageProps> = ({
  containerClassName = "",
  alt = "nc-imgs",
  src = "",
  className = "object-cover w-full h-full",
  ...args
}) => {
  const isMounted = useRef(false);
  const _containerRef = useRef(null);

  const [__src, set__src] = useState("");
  const [imageLoaded, setImageLoaded] = useState(false);

  useEffect(() => {
    let _imageEl: HTMLImageElement | null = null;

    isMounted.current = true;

    const _imageOnViewPort = () => {
      if (!src) {
        _handleImageLoaded();
        return true;
      }
      _imageEl = new Image();
      if (_imageEl) {
        _imageEl.src = src;
        _imageEl.addEventListener("load", _handleImageLoaded);
      }
      return true;
    };

    const _handleImageLoaded = () => {
      if (!isMounted.current) return;
      setImageLoaded(true);
      set__src(src);
    };

    const _checkInViewPort = () => {
      if (!_containerRef.current) return;
      checkInViewIntersectionObserver({
        target: _containerRef.current,
        options: {
          root: null,
          rootMargin: "0%",
          threshold: 0,
        },
        freezeOnceVisible: true,
        callback: _imageOnViewPort,
      });
    };

    const _initActions = async () => {
      _checkInViewPort();
    };

    _initActions();
    return () => {
      isMounted.current = false;
    };
  }, [src]);

  const renderLoadingPlaceholder = () => {
    return (
      <div
        className={`${className} flex items-center justify-center bg-neutral-200 dark:bg-neutral-6000 text-neutral-100 dark:text-neutral-500`}
      >
        <div className="h-2/4 max-w-[50%]">
          <PlaceIcon />
        </div>
      </div>
    );
  };

  return (
    <div className={`nc-NcImage ${containerClassName}`} data-nc-id="NcImage" ref={_containerRef}>
      {__src && imageLoaded ? (
        <NextImage src={__src} className={className} alt={alt} {...args} />
      ) : (
        renderLoadingPlaceholder()
      )}
    </div>
  );
};

export default NcImage;

It was originally using <img ... instead of NextImage and things were fine. I wanted to change it to Next.js' Image component. As soon as I did that, I got the following error:

Type '{ crossOrigin?: "" | "anonymous" | "use-credentials" | undefined; decoding?: "async" | "auto" | "sync" | undefined; height?: string | number | undefined; loading?: "eager" | "lazy" | undefined; ... 263 more ...; alt: string; }' is not assignable to type '{ src: string | StaticImport; alt: string; width?: SafeNumber | undefined; height?: SafeNumber | undefined; fill?: boolean | undefined; loader?: ImageLoader | undefined; ... 11 more ...; lazyRoot?: string | undefined; }'.
  Types of property 'width' are incompatible.
    Type 'string | number | undefined' is not assignable to type 'SafeNumber | undefined'.
      Type 'string' is not assignable to type 'SafeNumber | undefined'.ts(2322)

I'm not entirely sure how to fix it though since I am not using width anywhere.

I thought of just getting it from the inputs to NcImage and then changing its type:

  const newWidth = typeof width === 'number' ? SafeNumber(width) : width;

But I'm not sure what SafeNumber is or where to import it from. What is the proper way to fix this problem?

CodePudding user response:

Since you are rendering a Next.js Image tag, use the corresponding props type, which is ImageProps, like so:

import { ImageProps } from "next/image";
export interface NcImageProps extends ImageProps {
  containerClassName?: string;
}

With this, your error goes away.

  • Related