【问题标题】:How to narrow down union of generic types in Typescript?如何缩小 Typescript 中泛型类型的联合?
【发布时间】:2021-02-26 08:17:47
【问题描述】:

我正在尝试在 React 中创建一个使用泛型类型的表单组件。

表单的通用类型是传入的初始值。

我似乎一辈子都无法让 Typescript 缩小与特定字段对应的类型。

Here's a CodeSandbox 是我试图让它正常工作的众多尝试之一。

我尝试了很多其他的方法,比如为每个字段添加一个“类型”属性,然后使用 switch 语句尝试以这种方式缩小范围,但似乎都没有奏效!

不希望有人为我编写此代码,但非常感谢您朝正确方向轻推。

【问题讨论】:

    标签: reactjs typescript generics typescript-generics


    【解决方案1】:

    所以你现在可能已经猜到了,要做到这一点,你不能只定义一个像

    这样的可索引类型
    type FormValues<T> = {
      [key: string]: T
    }
    

    因为在这种情况下T 将是一个没有太大帮助的联合。

    所以你不能使用可索引类型,现在我能想到的唯一方法是停止尝试自己创建类型,让 typescript 为你推断它。

    首先让我稍微更改一下您的FormValue 声明,我将删除ValueOf,当我在玩您的沙盒时,我忘记了它的用途,但如果我认为这对我的方式不重要我建议。所以让我们这样输入:

    type FormValue<TName extends string, TValue> = {
      value: TValue
      name: TName
      pristine: boolean
      errors: string[] | null
    }
    

    现在这将只接受两个类型参数,一个是名称,另一个是值,没有不必要的对象。现在让我们输入InitialFormValues:它只是一个具有随机属性的对象,我们不会用它来输入任何东西,只用于约束类型:

    type InitialValues = {
      [key: string]: any
    }
    // or
    type InitialValues = Record<string, any>
    

    接下来是UseFormProps。这里我们避免创建可索引类型,这不会导致任何结果,但我们让 typescript 为我们推断类型。我们只需要对它进行一点限制,这样就不可能通过 p.e.一个数字为initialValues:

    type UseFormProps<T extends InitialValues> = {
      initialValues: T
    }
    

    现在打字稿会自动选择T,这正是我们所需要的:如果initialValues{ foo: 1, bar: 'str' },那么它将是{ foo: number, bar: string },仅此而已。

    最后去FormValues:

    type FormValues<T extends InitialValues> = {
      [x in keyof T]: FormValue<x, T[x]>
    }
    

    现在FormValues 将不仅是可索引的,而且将完全包含T 包含的属性,而且T[x] 不会像T[keyof T] 这样的联合,x 的每个值都会有所不同所以对于每个属性。基本上就是这样,现在它应该可以按预期工作了。您只需要更改transformInitialValues 中的某些内容,因为如果您将空对象分配给FormValues&lt;whatever&gt;,打字稿现在会报错

    【讨论】:

    • 这正是我想要的!也非常详细的解释。刚刚尝试了你提到的东西,它完全按照我一直在尝试的方式工作。非常感谢您花时间这样做,非常感谢!
    猜你喜欢
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-02
    • 2020-04-12
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    相关资源
    最近更新 更多