通用接口,可以将对象属性映射到另一个接口



我正试图为我的FormBuilder组件制作类型,但在属性类型提取方面遇到了一些麻烦。

下面是简化后的代码:
interface NumericField {
type: 'numeric'
}
interface TextField {
type: 'text'
}
type Field = NumericField | TextField
type Fields = Record<string, Field>
export type PropertyTypeExtractor<T extends Fields> = {
[Property in keyof T]
: T[Property] extends NumericField ? number
: T[Property] extends TextField ? string 
: never;
};
const fields = {
numericField: {
type: 'numeric'
},
textField: {
type: 'text'
}
}
const data = {
numericField: 42,
textField: 'lorem ipsum'
}
function createForm<T extends Fields>(fields: T, data: PropertyTypeExtractor<T>) { }
// fields: Type 'string' is not assignable to type '"text"'.
createForm(fields, data)

正如我所看到的,主要问题来自type Fields = Record<string, Field>, typescript不知道Field的确切类型。即使type === 'text'意味着这可能是唯一的TextField

Typescript足够聪明:

const anotherFields: Fields = {
numericField: {
type: 'numeric'
},
textField: {
type: 'text'
}
}
const field = anotherFields.numericField // field: Field
if (field.type === 'numeric') {
console.log(field) // field: NumericField
}

那么这里有一些解决我的情况的方法吗?

游乐场

问题是typescript在将您的类型作为参数传递给函数时计算并扩展为类型Fields。您可以通过以下几种方式解决此问题:

  1. 将字段直接传递给inline函数:
createForm({
numericField: {
type: 'numeric'
},
textField: {
type: 'text'
}
}, data)
  1. 使用as const来防止字段变量的类型扩展:
const fields2 = {
numericField: {
type: 'numeric'
},
textField: {
type: 'text'
}
} as const;
createForm(fields, data)
  1. 加入到一个参数类型中,因此推理适用于整个对象。我并不是100%了解为什么这是有效的,但我相信这与同时发生的类型推断有关,而不是在比较两种不同类型时试图找到最合适的:
type FormParameters<T extends Fields> = { fields: T, data: PropertyTypeExtractor<T> };
function createForm<T extends FormParameters<any>>(parameters: T){ }
createForm({ fields, data })

相关内容

  • 没有找到相关文章

最新更新