keyof 模型不可分配给泛型类型

时间:2021-04-01 20:40:12

标签: typescript typescript-generics union-types

我试图制作严格类型的列构建器,但遇到了联合类型问题。我认为解决方案是将构建器返回类型推断为 (ColumnDef<Model, "Key1"> | ColumnDef<Model, "Key2>)[],但不知道如何实现这一点

...

type EnhancedCellRenderer<
  Model extends AnyObject,
  Field extends keyof Model = keyof Model & string
> = (props: EnhancedCellRenderProps<Model, Field>) => ReactNode

type CellRenderer<
  Model extends AnyObject,
  Field extends keyof Model = keyof Model & string
> = BasicCellRenderer<Model[Field]> | EnhancedCellRenderer<Model, Field>

interface ColumnDef<
  Model extends AnyObject,
  Field extends keyof Model = keyof Model & string
> {
  width?: number | string
  field: Field
  renderer?: CellRenderer<Model, Field>
}

type ColumnBuilder<Model extends AnyObject> = <Field extends keyof Model>(
  field: Field,
  config?: Omit<ColumnDef<Model, Field>, 'field'>
) => ColumnDef<Model, Field>

declare function buildColumns<Model extends AnyObject>(
  cb: (builder: ColumnBuilder<Model>) => ColumnDef<Model>[]
): ColumnDef<Model>[]

type Model = {
  '01.20': number
  '02.05': number
  '02.20': number
  '03.05': number
  '03.20': number
  '04.05': number
  '04.20': number
}

buildColumns<Model>((builder) => [builder('01.20'), builder('02.05')])

我遇到了这个错误

Type 'ColumnDef<Model, "01.20">' is not assignable to type 'ColumnDef<Model, "01.20" | "02.05" | "02.20" | "03.05" | "03.20" | "04.05" | "04.20">'.
  Types of property 'renderer' are incompatible.
    Type 'CellRenderer<Model, "01.20"> | undefined' is not assignable to type 'CellRenderer<Model, "01.20" | "02.05" | "02.20" | "03.05" | "03.20" | "04.05" | "04.20"> | undefined'.
      Type 'EnhancedCellRenderer<Model, "01.20">' is not assignable to type 'CellRenderer<Model, "01.20" | "02.05" | "02.20" | "03.05" | "03.20" | "04.05" | "04.20"> | undefined'.
        Type 'EnhancedCellRenderer<Model, "01.20">' is not assignable to type 'EnhancedCellRenderer<Model, "01.20" | "02.05" | "02.20" | "03.05" | "03.20" | "04.05" | "04.20">'.
          Type '"01.20" | "02.05" | "02.20" | "03.05" | "03.20" | "04.05" | "04.20"' is not assignable to type '"01.20"'.

Typescript Playground

1 个答案:

答案 0 :(得分:1)

命名注意事项:泛型类型参数以conventionally的形式给出一两个大写字符的短名称,以便更容易地将它们与特定的类型名称区分开来。例如,看到 Model 既是特定类型又是泛型类型参数令人困惑。因此,在下文中,当用作类型参数时,我将使用 MF 而不是 ModelField


我不能声称已经仔细阅读了您的代码以了解它实际上试图做什么。尽管如此,我还是觉得您并不真正希望 ColumnDef<M> 成为适用于完整字段联合的单一事物,而是每个适用于单个领域的事物的联合。如果是这样,那么您可以在其 ColumnDef 参数中跨联合将 F 更改为 distribute

type ColumnDef<M extends AnyObject, F extends keyof M = keyof M> =
  F extends keyof M ? {
    width?: number | string
    field: F
    renderer?: CellRenderer<M, F>
  } : never;

如果您检查 ColumnDef<Model>,您会发现它现在是联合类型:

type ColumnDefModel = ColumnDef<Model>;
/* type ColumnDefModel = {
    width?: string | number | undefined;
    field: "01.20";
    renderer?: CellRenderer<Model, "01.20"> | undefined;
} | {
    width?: string | number | undefined;
    field: "02.05";
    renderer?: CellRenderer<...> | undefined;
} | ... 4 more ... | {
    ...;
} */

您的 buildColumns() 调用不再出错:

buildColumns<Model>((builder) => [builder('01.20'), builder('02.05')])

Playground link to code