Reactjs 反应+;Typescript:使用具有可选泛型类型函数的道具正确键入通用组件

Reactjs 反应+;Typescript:使用具有可选泛型类型函数的道具正确键入通用组件,reactjs,typescript,Reactjs,Typescript,我曾经(现在仍然)在措辞上有困难,如果这个问题很难理解,我很抱歉 问题: 如何使用具有可选通用类型函数的道具正确键入通用组件, 这样我就可以创建使用和不使用可选泛型类型的组件了 例如(也可以在codesandbox中查看代码): 代码沙盒: 假设我想要一个包含1个基本搜索字段和可选搜索字段的搜索表单。 此外,可选的搜索字段根据使用方式返回不同但特定类型的数据。 我们事先知道具体的类型,所以我们将它们称为A和B 因此,我决定创建以下通用组件名称Search: // the specific typ

我曾经(现在仍然)在措辞上有困难,如果这个问题很难理解,我很抱歉

问题:
如何使用具有可选通用类型函数的道具正确键入通用组件,
这样我就可以创建使用和不使用可选泛型类型的组件了

例如(也可以在codesandbox中查看代码):
代码沙盒:

假设我想要一个包含1个基本搜索字段和可选搜索字段的搜索表单。 此外,可选的搜索字段根据使用方式返回不同但特定类型的数据。 我们事先知道具体的类型,所以我们将它们称为
A
B

因此,我决定创建以下通用组件名称
Search

// the specific types of data that the optional search field(s) return
export type OptionalSearchTypeA = {
  keyA1: string
  keyA2: string
}
export type OptionalSearchTypeB = {
  keyB1: string
}

// a union type of all types of optional search field(s) to restrict the type in generics
// allow undefined since it is optional
export type OptionalSearchType = OptionalSearchTypeA | OptionalSearchTypeB | undefined

// the search state used in the search component
export type SearchStateType<T extends OptionalSearchType = undefined> = {
  search: string
  optionalSearch?: T
}

// props for the actual search component 
export type SearchProps<T extends OptionalSearchType = undefined> = {
  label: string
  searchState: SearchStateType<T>
  onChange: (searchState: SearchStateType<T>) => void
  hasOptionalSearch: boolean
}

// actual Search component
export const Search = <T extends OptionalSearchType = undefined>({
  label,
  searchState,
  onChange,
  hasOptionalSearch,
}: SearchProps<T>) => {
  const handleChangeSearch = (e: ChangeEvent<HTMLInputElement>) => onChange({ ...searchState, search: e.target.value })

  return (
    <label id={label}>
      <div>{label}</div>
      <input id={label} value={searchState.search} onChange={handleChangeSearch} />
      {/* hasOptionalSearch && (generate optional input field based on searchState.optionalSearch) */}
    </label>
  )
}
现在当我在应用程序中使用它时…我得到一个错误

// searchState for components for both specific type and with optional and without optional
export const A_initSearchStateWithOptional: SearchStateType<OptionalSearchTypeA> = {
  search: '',
  optionalSearch: {
    keyA1: '',
    keyA2: '',
  },
}

export const A_initSearchStateWithoutOptional: SearchStateType = {
  search: '',
}

export const B_initSearchStateWithOptional: SearchStateType<OptionalSearchTypeB> = {
  search: '',
  optionalSearch: {
    keyB1: '',
  },
}

export const B_initSearchStateWithoutOptional: SearchStateType = {
  search: '',
}

export const App = () => {
  const [A_searchStateWithOptional, setA_searchStateWithOptional] = useState(A_initSearchStateWithOptional)
  const [A_searchStateWithoutOptional, setA_searchStateWithoutOptional] = useState(A_initSearchStateWithoutOptional)
  const [B_searchStateWithOptional, setB_searchStateWithOptional] = useState(B_initSearchStateWithOptional)
  const [B_searchStateWithoutOptional, setB_searchStateWithoutOptional] = useState(B_initSearchStateWithoutOptional)

  // typescript tells me that onChange has a type mismatch, but I know that it will return the type I expect
  return (
    <div className="App">
      <SearchA
        label="Search A with Optional"
        searchState={A_searchStateWithOptional}
        onChange={setA_searchStateWithOptional}
        hasOptionalSearch={true}
      />
      <SearchA
        label="Search A without Optional"
        searchState={A_searchStateWithoutOptional}
        onChange={setA_searchStateWithoutOptional}
        hasOptionalSearch={false}
      />
      <SearchB
        label="Search B with Optional"
        searchState={B_searchStateWithOptional}
        onChange={setB_searchStateWithOptional}
        hasOptionalSearch={true}
      />
      <SearchB
        label="Search B without Optional"
        searchState={B_searchStateWithoutOptional}
        onChange={setB_searchStateWithoutOptional}
        hasOptionalSearch={false}
      />
    </div>
  )
}

//搜索特定类型和带可选和不带可选组件的组件的状态
导出常量A_initSearchStateWithOptional:SearchStateType={
搜索:“”,
可选搜索:{
关键字A1:“”,
关键字A2:“”,
},
}
导出常量A_initSearchStateWithout可选:SearchStateType={
搜索:“”,
}
导出常量B_initSearchStateWith可选:SearchStateType={
搜索:“”,
可选搜索:{
键B1:“”,
},
}
导出常量B_initSearchStateWithout可选:SearchStateType={
搜索:“”,
}
导出常量应用=()=>{
const[A_searchStateWithOptional,setA_searchStateWithOptional]=useState(A_initSearchStateWithOptional)
const[A_searchStateWithoutOptional,setA_searchStateWithoutOptional]=useState(A_initSearchStateWithoutOptional)
const[B_searchStateWithOptional,setB_searchStateWithOptional]=useState(B_initSearchStateWithOptional)
const[B_searchstate without optional,setB_searchstate without optional]=使用状态(B_initsearchstate without optional)
//typescript告诉我onChange有一个类型不匹配,但我知道它将返回我期望的类型
返回(
)
}
错误如下:

Type 'Dispatch<SetStateAction<SearchStateType<OptionalSearchTypeA>>>' is not assignable to type '(searchState: SearchStateType<OptionalSearchTypeA | undefined>) => void'.
  Types of parameters 'value' and 'searchState' are incompatible.
    Type 'SearchStateType<OptionalSearchTypeA | undefined>' is not assignable to type 'SetStateAction<SearchStateType<OptionalSearchTypeA>>'.
      Type 'SearchStateType<OptionalSearchTypeA | undefined>' is not assignable to type 'SearchStateType<OptionalSearchTypeA>'.
        Type 'OptionalSearchTypeA | undefined' is not assignable to type 'OptionalSearchTypeA'.
          Type 'undefined' is not assignable to type 'OptionalSearchTypeA'.ts(2322)
index.ts(6, 3): The expected type comes from property 'onChange' which is declared here on type 'IntrinsicAttributes & SearchAProps'
Type'Dispatch'不可分配给Type'(searchState:SearchStateType)=>void'。
参数“value”和“searchState”的类型不兼容。
类型“SearchStateType”不可分配给类型“SetStateAction”。
类型“SearchStateType”不可分配给类型“SearchStateType”。
类型“OptionalSearchTypeA |未定义”不可分配给类型“OptionalSearchTypeA”。
类型“未定义”不可分配给类型“OptionalSearchTypeA”。ts(2322)
ts(6,3):预期的类型来自属性“onChange”,该属性在类型“IntrinsicAttributes&SearchAProps”上声明
它告诉我不能将
OptionalSearchType*| undefined
分配给
OptionalSearchType*


我知道对于带有
haoptionalsearch={true}
的搜索组件,我将收到
OptionalSeachType*
,而对于带有
haoptionalsearch={false}
的组件,我知道它将是
未定义的。我必须仍然使用
useState
键入它们吗?我只想简单地说它是
useState
useState
,因为我知道这就是将返回的内容。

通过将类型提供给useState,您应该能够使onChange函数比TS推断的更不具体

useState(A_initSearchStateWithOptional)

你要找的可能是有区别的工会类型

type SearchTypes = {
  hasOptional: true;
  optionalSearh: {
    type: string;
  };
} | {
  hasOptional: false;
};

使用此类型将告诉typescript,当hasOptional=true时,对象上必须存在optionalSearch字段。

感谢您的回复!而您的答案确实解决了类型错误。我不得不重新措辞我的问题,因为它没有提出我想问的问题。抱歉,我知道我将收到带有
hasOptionalSearch={true}
的组件的
OptionalSeachType*
,而对于带有
hasOptionalSearch={false}
的组件,它将是
未定义的。我必须仍然使用
useState
键入它们吗?我只想简单地说它是useState`或者
useState
,因为我知道它将被返回。我仍在努力理解这个任务。补充了另一个答案。
type SearchTypes = {
  hasOptional: true;
  optionalSearh: {
    type: string;
  };
} | {
  hasOptional: false;
};