在Typescript中,如何将可区分类型联合的成员赋给需要该联合的泛型类型

ozxc1zmp  于 2023-06-24  发布在  TypeScript
关注(0)|答案(1)|浏览(172)

我使用React-Typescript。我有一个组件,它需要像这样的 prop :

type ComponentPropsType = {
    filterSet: FilterSet<Union>
}

type FilterSet<T> = {
    targetField: keyof T;
}

type Union = FirstType | SecondType;

type FirstType = {
    name: string;
    id: number;
    specificToFirst: string;
}

type SecondType = {
    name: string;
    id: number;
    specificToSecond: string;
}

以下是组件的相关部分:

export const Component = ({ filterSet }: ComponentPropsType) => {
    ...
}

当我尝试像这样调用这个组件时:

export const InvokerComponent = () => {
    const getFilterSet: FilterSet<FirstType> = () => 
        {targetField: "specificToFirst"}

    const filterSet: FilterSet<FirstType> = getFilterSet();

    return (
        <Component filterSet={filterSet} />  // TS ERROR HERE
    )
}

Typescript告诉我:

Type FilterSet<FirstType> is not assignable to type FilterSet<Union> 
Type Union is not assignable to type FirstType
Type SecondType is missing the following properties from type FirstType: specificToFirst

我的目标是允许我的组件接受一个通用的filterSet,因为我希望将filterSet的字段限制为作为模板传入的类型的键。这在上面的例子中没有显示,但我认为这对手头的问题并不重要。
一旦我们把它变成Component,我就不在乎是传入FirstType还是SecondType来生成filterSet了。我想将filterSet限制为FilterSet<Union>,因为我希望Union类型定义良好。它对filterSet的定义很重要,但对组件的功能并不重要。
我希望我已经提供了足够的信息来帮助解决这个问题。重申问题:
在Typescript中,如何将区分类型联合的成员分配给期望该联合的泛型类型。

8zzbczxx

8zzbczxx1#

问题是你的Union没有在FilterSet中分发,这就是为什么FilterSet<Union被评估为:

{
    targetField: "name" | "id";
}

您实际需要的是以下格式的内容:

{
    targetField: "name";
} | {
    targetField: "id";
}

FirstTypeSecondType特定字段的原因是keyof操作符的已知问题(可能是GitHub问题,但我找不到)。
所需类型可以通过引用分布式条件类型docs来实现:

type FilterSet<T> = T extends T
  ? {
      targetField: keyof T;
    }
  : never;

测试:

// type Result = {
//   targetField: keyof FirstType;
// } | {
//   targetField: keyof SecondType;
// }
type Result = FilterSet<Union>

这正是我们需要的:

const getFilterSet = () => ({} as any);

export const InvokerComponent = () => {
  // The following funcntion simply returns a an object of type FilterSet<FirstType>
  const filterSet: FilterSet<FirstType> = getFilterSet();

  return (
    <Component filterSet={filterSet} /> // no error
  );
};

Playground

相关问题