问题描述
我具有以下类型,该类型确定所有属性都将是函数,不接受任何参数,也不能接受类型为Record<string,any>
的单个参数:
type FnTrait = Record<
string,(input?: Record<string,any>) => any
>;
我尝试将此类型扩展到另一个接口(我希望具有相同的约束)。
interface HasFn extends FnTrait {
someFn(): string; // no problem
anotherFn(o: {id: string}): number; // ts error 2411
}
这会在anotherFn
上产生错误:Property 'anotherFn' of type '(o: { id: string; }) => number' is not assignable to string index type '(input?: Record<string | number | symbol,any> | undefined) => any'.
为什么someFn
没有产生错误,而anotherFn
却产生了错误2411?似乎应该允许缩小范围。
任何帮助将不胜感激。谢谢!
解决方法
这是function types being contravariant in their parameters的实例。单词“相反”的意思是“以相反的方式变化”。如果使函数的参数更特定于 (窄),则使函数类型本身更加 general (宽)。这意味着您不是将options: {
animation: {
duration: 2000
},...
设为HasFn
的子类型(这就是FnTrait
的意思),而是使它成为超类型。这是不允许的,并且违反了substitutability的原则。
尤其是,
extends
是错误的,因为它要求其参数具有anotherFn()
值的string
属性,而id
没有。期望您可以不带参数,也可以具有几乎任何类型的单个参数来调用FnTrait["anotherFn"]
的任何属性。但是,如果调用FnTrait
的{{1}}方法不带参数,或者参数缺少正确的HasFn
属性,则anotherFn()
可能会爆炸。因此,根据定义,id
不可分配给HasFn
,尽管已声明对其进行了扩展:
FnTrait
由于const hasFn: HasFn = {
someFn: () => "",anotherFn: o => o.id.length
}
const fnTrait: FnTrait = hasFn;
fnTrait.anotherFn({ a: 123 }); // okay at compile time,explodes at runtime
意味着您无法安全地替换请求值anotherFn()
的{{1}}值,因此FnTrait
无法分配给HasFn
,你会得到一个错误。
FnTrait
不是 错误的原因是因为a function of fewer parameters is assignable to a function that takes more parameters。这是因为HasFn
将根据需要忽略传递给它的任何参数,因此将其视为可能会接收参数的函数是安全的:
someFn()
这与someFn()
失败的原因相同:可替代性。