Return 使用条件类型的通用函数中的类型问题

Return type issue in generic function using conditional type

我正在尝试创建一个通用函数,它接受一个 FieldType 和 returns 一个对象,该对象包含来自公共基类型的属性以及该特定类型的默认值 FormField。出于某种原因,如果没有编译器的抱怨,我无法让它工作。我不确定问题出在哪里但是

我怀疑这可能是 FormField 类型被定义为条件类型的方式。我尝试过以各种方式重新定义我的类型但没有成功,所以我怀疑我错过了一些明显的东西或者如果没有类型断言这是不可能的。

这是我的类型:

type FieldType = 'text' | 'number' | 'custom'

interface FormFieldBase {
  type: FieldType
  label: string
  error: string
  disabled: boolean
}

interface CustomField extends FormFieldBase {
  value: string
}

interface TextField extends FormFieldBase {
  value: string
  placeholder: string
}

interface NumberField extends FormFieldBase {
  value: number
}

type FormField<T extends FieldType> = T extends 'text'
  ? TextField
  : T extends 'number'
  ? NumberField
  : CustomField

这是我的通用函数:

const createBaseField = <T extends FieldType>(
  type: T
): FormFieldBase => ({
  type,
  label: '',
  error: '',
  disabled: false
})

const defaultValues: {
  [K in FieldType]: Omit<FormField<K>, keyof FormFieldBase>
} = {
  text: {
    value: '',
    placeholder: ''
  },
  checkbox: {
    value: false
  },
  custom: {
    value: ''
  },
  number: {
    value: 0
  }
}

const fieldCreator = <T extends FieldType>(type: T): FormField<T> => ({
  ...createBaseField(type),
  ...defaultValues[type]
})


fieldCreator 产生以下错误:

Type '{ type: FieldType; label: string; error: string; disabled: boolean; } & { number: Pick<NumberField, "value">; text: Pick<TextField, "value" | "placeholder">; checkbox: Pick<...>; custom: Pick<...>; }[T]' is not assignable to type 'FormField'.ts(2322)

在我看来,FormFieldBase(来自createBaseField)和来自 FormField<T> 的字段(不包括来自 FormFieldBase 的字段(来自 defaultValues)的交集应该是 FormField<T>.

我做错了什么?

TLDR;

FormField<T> 作为 fieldCreator 函数声明的 return 类型对 Defaults<FieldType>.

一无所知

更长的版本

请注意,您的 FormField<T> 类型仅保证解析的类型是 CustomFieldTextFieldNumberField。三个extends都是FormFieldBase类型,所以也保证了类型符合FormFieldBase的形状,但是没有别的.

现在,您的 fieldCreator 函数签名声明它 return 是 FormField<T>。让我们删除这个显式注释,看看是否是这种情况:

const a = fieldCreator("text"); //& Pick<TextField<"text">, "value" | "placeholder">

为简洁起见,省略了FormFieldBase类型。您可以清楚地看到,return 类型在 FormField<T> 中有关于其形状 不存在 的附加信息。正如您猜对的那样,由于 spread properties 操作,此信息取自 Omit<FormField<K>, keyof FormFieldBase>

因此,当您明确地说 fieldCreator 函数 return 是 FormField<T> 时,编译器会合理地抱怨事实并非如此。只需删除注释并让 TypeScript 完成它的工作并推断正确的类型:

const a = fieldCreator("text");
a.value; //string
a.placeholder; //string

const b = fieldCreator("number");
b.placeholder; //error
b.value; //number

旁白:如果您使用泛型,请一直使用 - FormFieldBase 应该接受 FieldType 作为类型参数,以及扩展接口和默认常量的类型。见 playground link.