类型错误:输入“布尔值|” (() => void)' 不可分配给类型 'MouseEventHandler<HTMLButtonElement> | 不明确的'

Jas*_*ngh 1 onclick typescript reactjs next.js react-hooks

我正在尝试在 NextJS 应用程序中播放音频,并且正在使用 TypeScript 编写代码。当我这样做时,我onClick()在开发环境中工作得非常好npm run dev

<button onClick ={toggle}> {playing ? "Pause" : "Play"</button>
Run Code Online (Sandbox Code Playgroud)

但是当我尝试这样做时,npm build我收到此错误:

类型错误:输入“布尔值|” (() => void)' 不可分配给类型 'MouseEventHandler | 不明确的'。类型“false”不可分配给类型“MouseEventHandler |” 不明确的'。

在阅读了 Stack Overflow 上的一系列问题后,我发现onClick()不需要一个 void,这就是toggle在这种情况下返回的函数,所以我这样做了:

<button onClick={() => toggle}> {playing ? "Pause" :"Play</button>
Run Code Online (Sandbox Code Playgroud)

经过上述更改后,分配错误消失了,但是当我运行应用程序时,播放按钮不再起作用。我愿意接受您对代码的任何其他建议。

这是完整的代码:

const useAudio = (url: string) => {
  const audio = useRef<HTMLAudioElement | undefined>(
    typeof Audio !== "undefined" ? new Audio(url) : undefined
  );
  
  const [playing, setPlaying] = useState(false);

  const toggle = () => setPlaying(!playing);
  //const toggle = setPlaying(!playing);

  useEffect(() => {
      playing ? audio.current?.play() : audio.current?.pause();
  }, [playing]);

  useEffect(() => {
    audio.current?.addEventListener('ended', () => setPlaying(false));
    return () => {
      audio.current?.removeEventListener('ended', () => setPlaying(false));
    };
  }, []);

  return [playing, toggle];
};

const NFT = ({ baseUri, metaId, url }: { baseUri: string; metaId: string; url: string }) => {
  const [metadata, setMetadata] = useState<{[key: string]: string} | null>(null)

  const fetchMetadata = async (url: string) => {
    const response = await fetch(url)
    const result = await response.json()

    if (!result) return

    setMetadata(result)
  }

  if(url) {
    var [playing, toggle] = useAudio(url);
  }

  useEffect(() => {
    fetchMetadata(`${baseUri}/${metaId}`)
  }, [])

  if (!metadata) return null

  return (
    <div className="w-full md:w-1/2 lg:w-1/3 p-3 mb-4">
      <div className="h-96">
        <div className="relative items-center min-h-full">
          <a href="#">
            <Image
              alt={metadata[MetadataField.Title]}
              src={metadata[MetadataField.Media]}
              layout="fill"
              objectFit="contain"
            />
          </a>
        </div>
         {url &&
          <button onClick ={toggle}> {playing ? "Pause" : "Play"} </button>
         }
      </div>
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

sli*_*wp2 5

来自反应打字稿备忘单

如果您在自定义 Hook 中返回一个数组,您将需要避免类型推断,因为 TypeScript 将推断联合类型(当您实际上希望数组的每个位置有不同的类型时)。

您需要使用const 断言

const useAudio = (url: string) => {
  const audio = useRef<HTMLAudioElement | undefined>(typeof Audio !== 'undefined' ? new Audio(url) : undefined);
  const [playing, setPlaying] = useState(false);
  const toggle = () => setPlaying(!playing);

  useEffect(() => {
    playing ? audio.current?.play() : audio.current?.pause();
  }, [playing]);

  useEffect(() => {
    audio.current?.addEventListener('ended', () => setPlaying(false));
    return () => {
      audio.current?.removeEventListener('ended', () => setPlaying(false));
    };
  }, []);

  return [playing, toggle] as const;
};
Run Code Online (Sandbox Code Playgroud)

请参见下面的示例:

function test() {
  return [true, () => {}];
}

const [isOk, func] = test(); 

func();  // error  
Run Code Online (Sandbox Code Playgroud)

函数的返回类型是TypeScript 推断的test联合类型。(boolean | (() => void))[]当你调用时func(),你会得到错误:

This expression is not callable.
  Not all constituents of type 'boolean | (() => void)' are callable.
    Type 'false' has no call signatures.ts(2349)
Run Code Online (Sandbox Code Playgroud)

解决方案1:const assertions

function test() {
  return [true, () => {}] as const
}
Run Code Online (Sandbox Code Playgroud)

这将推断出[boolean, () => void]而不是(boolean | (() => void))[]

test解决方案2:显式声明返回类型

function test(): [boolean, () => void] {
  return [true, () => {}];
}
Run Code Online (Sandbox Code Playgroud)

此外,你不能有条件地使用钩子,它违反了钩子规则