在 vanilla JS 中,我可以编写一些如下所示的代码:
function renderTextField(props) { }
function renderSelectField(props) { }
const fieldMapping = {
text: renderTextField,
select: renderSelectField,
};
function renderField(field) {
const renderFn = fieldMapping[field.type];
renderFn(field.data);
}
使用 2 种类型的字段只是为了让示例变小,但是这段代码的优点是泛型方法不需要知道字段类型,它将决定委托(delegate)给 fieldMapping
提供的映射.
我正在尝试用 TypeScript 编写类似的东西。但我无法弄清楚如何让这些类型工作并仍然使用一个对象来提供 type
和要委托(delegate)给的函数之间的映射。
我意识到我可以使用 switch 语句或条件而不是对象来映射事物,但如果可能的话我更愿意这样做。
type TextFieldData = { value: string }
type TextField = { type: 'text', data: TextFieldData }
type SelectFieldData = { options: string[], selectedValue: string }
type SelectField = { type: 'select', data: SelectFieldData }
type FormField = TextField | SelectField
function renderTextField(props: TextFieldData) {}
function renderSelectField(props: SelectFieldData) {}
const fieldMapping = {
text: renderTextField,
select: renderSelectField,
}
// This won't work!
function renderFieldDoesNotWork(field: FormField) {
const renderFn = fieldMapping[field.type]
// Type 'TextFieldData' is missing the following properties from type 'SelectFieldData': options, selectedValue
renderFn(field.data)
}
// This works
function renderFieldWorks(field: FormField) {
if (field.type === 'text') {
const renderFn = fieldMapping[field.type]
renderFn(field.data)
} else if (field.type === 'select') {
const renderFn = fieldMapping[field.type]
renderFn(field.data)
}
}
最佳答案
恐怕您将不得不使用 type assertion以避免此处的代码重复。 TypeScript 的类型系统对这些没有很好的支持 "correlated record types"或依赖于联合不独立的两个联合类型值的交互的任何操作。
您已经到达冗余代码-switch
-语句解决方法;这是不安全断言的解决方法:
function assertNarrowFunction<F extends (arg: any) => any>(f: F) {
return f as (arg: Parameters<F>[0]) => ReturnType<F>; // assert
}
接受联合类型的函数,如 ((a: string)=>number) | ((a: number)=>boolean)
并且不安全地缩小它为一个函数,该函数采用其参数类型的并集并返回其返回类型的并集,例如((a: string | number) => string | number)
.这是不安全的,因为前联合类型的函数可能类似于 const f = Math.random()<0.5 ? ((a: string)=>a.length) : ((a: number)=>number.toFixed())
。 ,这绝对不匹配 ((a: string | number) => string | number)
.我不能安全地调用 f(5)
因为也许f
是字符串长度函数。
无论如何,您可以在 renderFn
上使用这个不安全的缩小范围消除错误:
function renderFnAssertion(field: FormField) {
const renderFn = assertNarrowFunction(fieldMapping[field.type]);
renderFn(field.data); // okay
}
关于 renderFn
的类型,您对编译器撒了一些谎。 ...与其说它会接受任何旧参数(例如,renderFn(123)
将根据需要失败),但足以让它允许这样做:
function badRenderFn(field1: FormField, field2: FormField) {
const renderFn1 = assertNarrowFunction(fieldMapping[field1.type]);
renderFn1(field2.data); // no error!!! ooops
}
所以你要小心。
好的,希望对你有帮助;祝你好运!
关于typescript - 如何将可区分联合中的对象映射到可以调用它们的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56781010/