You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React TypeScript传递Props给Hook时的类型错误求助

解决useInput Hook的TypeScript类型不匹配问题

看起来你遇到的是TypeScript对数组返回值的类型推断问题,以及组件props和Hook返回值之间的类型兼容性问题。咱们一步步来解决,完全不用妥协类型规范:

问题根源分析

  • useInput返回值的类型推断偏差:你的useInput返回的是[value, onChange, reset]数组,TypeScript默认会把它推断成(string | OnChangeType | (() => void))[]的联合数组类型,而非固定顺序的元组类型。当你把这个返回的onChange传给Input组件时,TS会认为它可能是string或其他类型,直接触发类型不兼容警告。
  • Input组件props的类型瑕疵:你给onChange设置了默认值null,但useInput返回的onChange是必填函数,这会导致类型冲突;另外onBlur函数的参数没有明确类型,也会触发类型检查警告。

具体解决方案

1. 给useInput定义明确的元组返回类型

修改useInput.tsx,通过as const强制TS将返回值推断为只读元组,固定每个位置的类型:

import { useState, ChangeEvent } from "react";

export type OnChangeType = (event: ChangeEvent<HTMLInputElement>) => void;

const useInput = (initialValue = "") => {
  const [value, setValue] = useState(initialValue);
  
  const reset = () => setValue("");
  
  const onChange: OnChangeType = e => {
    setValue(e.target.value);
  };

  // 明确指定返回值为固定顺序的元组类型
  return [value, onChange, reset] as const;
};

export default useInput;

2. 修正Input组件的props类型与逻辑

修改Input.tsx里的类型定义和props处理,解决类型冲突与细节问题:

import React, { useState, ChangeEvent } from "react";
import styled, { css } from "styled-components";
import type { OnChangeType } from "./hooks/useInput";

interface ILabelProps {
  hasContent: boolean;
}

const hasContentCSS = () => css`
  border: 5px solid royalblue;
`;

// 补全styled.label的语法结构
const Label = styled.label<ILabelProps>`
  display: block;
  ${props => props.hasContent && hasContentCSS()}
`;

interface IInputProps {
  readonly type?: string;
  readonly name: string;
  readonly label: string;
  value?: string | number | string[] | null;
  defaultValue?: string | number | string[] | null;
  // 调整onChange类型为可选函数,移除null的允许值
  readonly onChange?: OnChangeType;
}

export const Input = ({ 
  name = "email", 
  label, 
  value = null, 
  defaultValue = null, 
  onChange 
}: IInputProps) => {
  // 初始化hasContent时优先判断受控值value
  const [hasContent, setHasContent] = useState(() => {
    if (value !== null) return !!String(value);
    return !!defaultValue;
  });

  // 给onBlur的参数明确string类型
  const onBlur = (inputValue: string) => {
    setHasContent(inputValue.length > 0);
  };

  return (
    <Label hasContent={hasContent}>
      <input 
        type="text" 
        name={name}
        // 简化props传递逻辑,用空值合并运算符替代冗余判断
        {...(defaultValue !== null ? { defaultValue } : { value: value ?? "" })}
        {...(onChange && { onChange })}
        onBlur={e => onBlur(e.target.value)}
      />
      <span>{label}</span>
    </Label>
  );
};

3. 正确使用useInput与Input组件

现在你可以安全地解构useInput的返回值,传递给Input组件,不会再有类型错误:

import useInput from "./hooks/useInput";
import { Input } from "./Input";

const App = () => {
  const [emailValue, emailOnChange, resetEmail] = useInput("");
  
  return (
    <div>
      <Input 
        name="email" 
        label="Email" 
        value={emailValue} 
        onChange={emailOnChange} 
      />
      <button onClick={resetEmail}>Reset Email</button>
    </div>
  );
};

export default App;

这样所有TypeScript类型检查都会通过,同时保持了代码的类型安全性与规范。

内容的提问来源于stack exchange,提问作者Héctor León

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 16:22:34