TypeScript 接口函数字段逆变类型错误
TypeScript Interface Function Field Contravariance Type Error
我有以下类型,它确定所有属性都将是函数,它不接受参数,或者接受类型为 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
报ts错误2411?看来应该允许这种缩小。
如有任何帮助,我们将不胜感激。谢谢!
这是 function types being contravariant in their parameters. The word "contravariant" means "varies in the opposite way". If you make a function's parameter more specific (narrow), you are making the function type itself more general (wide). That means instead of making HasFn
a subtype of FnTrait
(which is what extends
means), you are sort of making it a supertype. This is not allowed, and violates the principle of substitutability 的实例。
特别是,
anotherFn()
是错误的,因为它要求其参数具有 string
值 id
属性,而 FnTrait["anotherFn"]
则不需要。预计您可以调用 FnTrait
的任何 属性,或者不带参数,或者使用几乎任何类型的单个参数。但是如果你在没有参数的情况下调用它的 anotherFn()
方法,或者使用缺少正确排序的 id
属性 的参数,HasFn
可能会爆炸。因此,根据定义,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()
意味着您不能安全地替换 FnTrait
值,而 HasFn
值被请求,FnTrait
无法分配给 HasFn
,你会得到一个错误。
之所以someFn()
是而不是是因为a function of fewer parameters is assignable to a function that takes more parameters。这是因为 someFn()
将不可避免地忽略传递给它的任何参数,因此将它视为可能接收参数的函数是安全的:
fnTrait.someFn({ a: 123 }); // okay at compile time and runtime
这与 anotherFn()
失败的原因相同:可替代性。
我有以下类型,它确定所有属性都将是函数,它不接受参数,或者接受类型为 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
报ts错误2411?看来应该允许这种缩小。
如有任何帮助,我们将不胜感激。谢谢!
这是 function types being contravariant in their parameters. The word "contravariant" means "varies in the opposite way". If you make a function's parameter more specific (narrow), you are making the function type itself more general (wide). That means instead of making HasFn
a subtype of FnTrait
(which is what extends
means), you are sort of making it a supertype. This is not allowed, and violates the principle of substitutability 的实例。
特别是,
anotherFn()
是错误的,因为它要求其参数具有 string
值 id
属性,而 FnTrait["anotherFn"]
则不需要。预计您可以调用 FnTrait
的任何 属性,或者不带参数,或者使用几乎任何类型的单个参数。但是如果你在没有参数的情况下调用它的 anotherFn()
方法,或者使用缺少正确排序的 id
属性 的参数,HasFn
可能会爆炸。因此,根据定义,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()
意味着您不能安全地替换 FnTrait
值,而 HasFn
值被请求,FnTrait
无法分配给 HasFn
,你会得到一个错误。
之所以someFn()
是而不是是因为a function of fewer parameters is assignable to a function that takes more parameters。这是因为 someFn()
将不可避免地忽略传递给它的任何参数,因此将它视为可能接收参数的函数是安全的:
fnTrait.someFn({ a: 123 }); // okay at compile time and runtime
这与 anotherFn()
失败的原因相同:可替代性。