用 Typescript 给 formily2.x Schema 提提速 —— 提示和校验篇

131 阅读1分钟

手写 Schema 会存在哪些挑战

手写 Schema 的同学应该会遇到一个痛点,x-component-props 没办法根据 x-component 来自行推断。我们先看看 formily 2.x 源代码中是如何定义 ISchema。简化以后如下:

type ISchema< Component = any,ComponentProps = any> = {
  ['x-component']?: Component
  ['x-component-props']?: ComponentProps
}

因为同一个 ISchema 中会使用多种 Component ,使用方式如下:

const FormSchema: ISchema<'Input' | 'Select', InputProps | SelectProps> = {
  type: 'object',
  'x-component': 'Input',
  'x-component-props': {
    ...
  },
};

问题就在于 'x-component-props' 的类型为 InputProps | SelectProps,ts 补全和校验无法根据 'x-component': 'Input' 收敛至 InputProps

如何通过 x-component 收敛 x-component-props 的类型

formily 2.x 导出的 ISchema 因为要考虑通用性,所以给出的泛型参数是比较宽泛的,我们如何基于现有的逻辑来扩展出更有效的收敛逻辑,先考虑最基础的类型收敛方式:

interface IInputProps {
    placeholder?: string
}

interface ISelectProps {
    options?: {
        label: string,
        value: string
    }[]
}


interface ComponentPropsMap {
  Input: IInputProps;
  Select: ISelectProps;
}

type ComponentNameKey = keyof ComponentPropsMap;

interface ISchema<T extends ComponentNameKey> {
  'x-component': T,
  'x-component-props': ComponentPropsMap[T]
}

// type MapISchema = ISchema<"Input"> | ISchema<"Select">
type MapISchema<T extends ComponentNameKey = ComponentNameKey> = T extends any ? ISchema<T> : never;


const schema: MapISchema = {
  'x-component': 'Select',
  'x-component-props': {
    options:[]
  }
}

const schema2: MapISchema = {
  'x-component': 'Input',
  'x-component-props': {
    placeholder: 'placeholder'
  }
}

使用 ComponentPropsMap 我们建立了类型间的关联关系,实现了通过 x-component 的值来推断出 x-component-props 应该有的类型。 在线示例可以参考 ts playground

以上使用的一些 API 如下:keypf映射类型

formily ISchema 的定义本质上是一个类型的迭代,仅需要在以上代码中简单扩展即可,暂不赘述。 注意我们将对应关系提取至最外层的 ComponentPropsMap。避免在迭代中传递。

下篇预告

以上我们有了常量 schema ,我们是否能够根据该常量推断出 form 值的类型格式呢?敬请期待。