从函数(挂钩)返回的对象的通用字段

时间:2020-05-12 17:54:22

标签: reactjs typescript typescript-generics

我正在处理React钩子,该钩子基于模式返回输入的道具(标签,值,onChange处理程序)。特定字段的模式具有“变量”字段,其值为“字符串”或“数字”。我想要实现一种特定的输出字段类型是基于架构“一般”地键入的。例如,如果架构中的属性“ variant”为“ string”,则在道具中会显示“ onStringChange”。如果“ variant”为“ number”,则为“ onNumberChange”。有什么办法可以做到这一点?

为清楚起见,以下是使用示例

const Form = () => {

  const fieldsProps = createFieldsPropsHook({   // <--- this is an example of using the function with "schema"
    firstname: {
        label: "Your firstname",
        variant: 'string'  // <--- variant 'string' so I want "onStringChange" handler
    },
    age: {
        label: "Your age",
        variant: 'number'    // <--- variant "number" so I want "onNumberChange"
    }
  })

  return (<div>
    <Input 
    value={ fieldsProps.firstname.value }
    label = { fieldsProps.firstname.label }
    onChange = {fieldsProps.firstname.onStringChange} // <--- string handler
    />

    <Input 
    value={ fieldsProps.age.value }
    label={ fieldsProps.age.label }
    onChange={fieldsProps.age.onNumberChange}   // <--- number handler
    />
  </div>)
}

对于上面的示例,我想这样输出

fieldsProps: {
    firstname: {
        onStringChange: (v: any) => void
        // label, etc...
    },
    age: {
        onNumberChange: (v: any) => void
        // label, etc...
    }
}

这是我的尝试

type Schema = Record<string, { variant: 'string', label: string } | { variant: 'number', label: string }>

type StringFieldProps = { onStringChange: (value: string) => void, label: string, value: string }
type NumberFieldProps = { onNumberChange: (value: number) => void, label: string, value: number }

function createFieldsPropsHook<T extends Schema>(schema: T) {

    // ... some implementation ...

    return output as Record<keyof T, StringFieldProps | NumberFieldProps>
}

但是我只知道所有字段都是相同的,并且没有处理程序

enter image description here enter image description here

那么,如何根据模式更改单个输出字段的类型?可能吗 在打字稿中?

1 个答案:

答案 0 :(得分:2)

您需要在每个键上进行映射(映射类型如{ [K in keyof T]: SomeType }),以便处理每个键的变体。

然后,您需要一种方法来根据该值查找字段道具返回类型。为此,我们创建一个中间类型,将变体与其输出类型进行匹配:

type VariantLookup = {
    string: StringFieldProps
    number: NumberFieldProps
}

这应该使添加更多变体("date""checkbox"等)非常容易

现在我们可以像这样键入返回值:

function createFieldsPropsHook<T extends Schema>(schema: T): {
    [K in keyof T]:
        VariantLookup[T[K]["variant"]]
} {
    // TODO: Implement...
    return {} as any
}

T[K]["variant"]从架构中获取"string""number",而VariantLookup[...]从查找助手类型中返回字段道具类型。

Playground


是否可以将“变量”设为可选?例如,如果未定义“变体”,则使用StringFieldProps

当然可以!

首先将您的架构类型更改为此:

type Schema = Record<string, { variant?: 'string' | 'number', label: string }>

这表示变量是字符串,数字或完全丢失。这样我们就可以检查'variant'属性,而不会出现类型错误。

现在,我们可以使用条件类型来检查variant是否作为查找类型中的键存在。如果是这样,则使用该变体返回类型,否则返回默认值StringFieldProps

function createFieldsPropsHook<T extends Schema>(schema: T): {
    [K in keyof T]:
        T[K]["variant"] extends keyof Schema
            ? VariantLookup[T[K]["variant"]]
            : StringFieldProps
} {
    // TODO: Implement...
    return {} as any
}