我最近升级到了typescript 2.4,我收到了一些错误,抱怨我的类型不再可分配。
以下是我遇到错误的情况:
interface Parent {
prop: any
}
interface Child extends Parent {
childProp: any
}
type Foo<T> = <P extends Parent>(parent: P) => T
function createFooFunction<T>(arg: T): Foo<T> {
// Error here!
return (child: Child): T => {
return arg;
}
}
在打字稿2.3中,这是可以接受的,但是typescript 2.4会产生这个错误
Type '(child: Child) => T' is not assignable to type 'Foo<T>'.
Types of parameters 'child' and 'parent' are incompatible.
Type 'P' is not assignable to type 'Child'.
Type 'Parent' is not assignable to type 'Child'.
Property 'childProp' is missing in type 'Parent'.
关于错误的最后一行,我注意到如果我将Child的属性设置为可选,那么就会满足typescript,即如果我做了这个改变
interface Child extends Parent {
childProp?: any
}
虽然这不是一个理想的修复,但在我的情况下,需要childProp。
我还注意到将Foo的参数类型直接更改为Parent也会满足typescript,即进行此更改
type Foo<T> = (parent: Parent) => T
这不是解决方法,因为我不控制Foo类型,也不控制父类。它们都来自供应商.d文件,所以我无法修改它们。
但无论如何,我不确定我理解为什么这是一个错误。 Foo类型说它需要扩展Parent的东西,而Child就是这样一个对象,那么为什么打字稿会认为它不可分配?
编辑:我已将此标记为已回答,因为添加--noStrictGenericChecks
标记会抑制错误(accepted answer here)。但是,我仍然想知道为什么这首先是一个错误,因为我宁愿保持严格的检查并重构我的代码,如果它错了,而不是简单地将其短路。
因此,为了重新讨论问题的核心,因为Child扩展了Parent,为什么打字稿不再认为Child可以赋予Parent,而且就OOP泛型而言,为什么它比以前更正确? / p>
答案 0 :(得分:4)
我认为问题在于您返回的函数需要Child
作为参数,但是将其分配给必须能够从Parent
派生的任何函数的函数,而不是只是Child
。
我们假设您致电createFooFunction<T>
获取Foo<T>
。根据{{1}}的定义,可以创建一个扩展Foo<T>
的类Child2
,并将其作为参数传递给您获得的Parent
。这是一个问题,因为您实际上返回的函数只能将Foo<T>
作为参数,而不是Child
。
这实际上更正确。请注意,您未Child2
并将其分配给Child
。您正在使用仅需要Parent
并将其分配给可以采用任何类型Child
的函数的函数。这是非常不同的。另一方面,如果我们处理的是返回值而不是输入,那就没问题了。这是协方差和逆变之间的差异,这在开始时可能有点混乱。在C#中,您可以使用Parent
和in
关键字在自己的通用类中指定此内容。
例如,在C#中,如果您有out
(声明为IEnumerable<Child>
),它自然也是IEnumerable<out T>
,因为您正在迭代 - 您和#39;重新获得IEnumerable<Parent>
个对象 - 您还可以获取Child
个对象,因为每个Parent
也是Child
。因此,您可以将Parent
分配给IEnumerable<Child>
,但不能反过来!由于您无法保证获得IEnumerable<Parent>
个对象。
另一方面,如果你有Child
(声明为IComparer<Parent>
),可以比较两个IComparer<in T>
个对象 - 因为每个Parent
也是{ {1}},它还可以比较任意两个Child
个对象。因此,您可以将Parent
分配给Child
,而不是相反 - 可以比较IComparer<Parent>
的内容只知道如何比较IComparer<Child>
!这是你的问题。
您可以将Child
和Child
理解为回调中的输入(参数)和输出(返回)值。您只能使输入更具体(逆变)并输出更通用(协方差)。
顺便说一句,我认为in
在这里完全没用(并且会增加混乱),因为即使它不是“无法”,你也可以传递任何扩展out
的函数。通用。只有在返回类似这样的类型时才会有用:<P extends Parent>
以在返回值中保留正确的类型Parent
。如果没有泛型,您必须<P extends Parent>(parent: P) => P
并返回P
,这样即使您输入更具体的内容,您也会获得Parent
。至于为什么如果你摆脱这个错误就会消失,我真的不知道。
答案 1 :(得分:2)
2.4版引入了更严格的泛型检查
阅读此https://blogs.msdn.microsoft.com/typescript/2017/06/27/announcing-typescript-2-4/并查找标题更严格检查泛型
来自文章:
作为任何破坏的临时解决方法,您可以使用新的
--noStrictGenericChecks
标记来抑制其中一些错误。
它还为回调引入了严格的逆转。