使用通用的扩展接口不再可分配给父级
Extending interface with generic no longer assignable to parent
我最近升级到 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;
}
}
在 typescript 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 也将满足打字稿,即进行此更改
type Foo<T> = (parent: Parent) => T
但这也不是解决方法,因为我既不控制 Foo 类型,也不控制 Parent。它们都来自供应商.d 文件,所以我不能修改它们。
但不管怎样,我不确定我是否理解为什么这是一个错误。 Foo 类型说它需要扩展 Parent 的东西,而 Child 就是这样一个对象,那么为什么打字稿会认为它不可分配?
编辑:我已将其标记为已回答,因为添加 --noStrictGenericChecks
标志会抑制错误 ()。但是,我仍然想知道 为什么 首先这是一个错误,因为我宁愿保持严格检查并在错误时重构我的代码,而不是仅仅将其短路.
所以要重申问题的核心,既然 Child extends Parent,为什么打字稿不再认为 Child 可以分配给 Parent,并且就 OOP 泛型而言,为什么比以前更正确?
2.4 版引入了更严格的泛型检查
阅读此 https://blogs.msdn.microsoft.com/typescript/2017/06/27/announcing-typescript-2-4/ 并查找标题 更严格地检查仿制药
来自文章:
As a temporary workaround for any breakage, you may be able to suppress some of these errors using the new --noStrictGenericChecks
flag.
它还为回调引入了严格的逆变。
我认为问题在于您正在 return 一个需要 Child
作为参数的函数,但是将它分配给一个必须能够接受任何派生自的函数Parent
,不只是 Child
.
假设您将 createFooFunction<T>
称为 get Foo<T>
。根据 Foo<T>
的定义,可以创建一个扩展 Parent
的 class Child2
并将其作为参数传递给您得到的 Foo<T>
。这是一个问题,因为您实际上 return 编辑了一个函数,该函数只能将 Child
作为参数,永远不会 Child2
.
这其实更正确。请记住,您不会使用 Child
并将其分配给 Parent
。您正在使用一个只需要 Child
的函数,并将其分配给可以接受任何类型 Parent
的函数。这是非常不同的。另一方面,如果我们处理的是 return 值而不是输入,那就没问题了。这就是协变和逆变之间的区别,一开始可能有点混乱。在 C# 中,您可以使用 in
和 out
关键字在您自己的通用 classes 中指定它。
例如,在 C# 中,如果您有一个 IEnumerable<Child>
(声明为 IEnumerable<out T>
),它自然也是一个 IEnumerable<Parent>
,因为您正在迭代 - 您会得到 Child
objects out - 你也可以得到 Parent
objects out,因为每个 Child
也是一个 Parent
。因此,您可以将 IEnumerable<Child>
分配给 IEnumerable<Parent>
但不能反过来!因为你不能保证得到一个 Child
对象。
另一方面,如果您有类似 IComparer<Parent>
(声明为 IComparer<in T>
)的东西可以比较两个 Parent
对象 - 因为每个 Child
也是aParent
,它也可以比较任意两个Child
对象。因此,您可以将 IComparer<Parent>
分配给 IComparer<Child>
,但不能反过来 - 可以比较 Child
的东西只知道如何比较 Child
!这是你的问题。
你可以将in
和out
理解为回调中的输入(参数)和输出(return)值。您只能使输入更具体(逆变)并输出更通用(协变)。
顺便说一下,我认为 <P extends Parent>
在这里完全没用(而且它增加了混乱),因为你可以在那个函数中传递任何扩展 Parent
的东西,即使它不是'不是通用的。这只有在您像这样 return 编辑类型时才有用:<P extends Parent>(parent: P) => P
以在 return 值中保留正确的类型 P
。如果没有泛型,您将不得不使用 Parent
和 return Parent
,因此即使您输入了更具体的内容,您也会得到 Parent
。至于为什么如果你摆脱这个错误就消失了,我真的不知道。
我最近升级到 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;
}
}
在 typescript 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 也将满足打字稿,即进行此更改
type Foo<T> = (parent: Parent) => T
但这也不是解决方法,因为我既不控制 Foo 类型,也不控制 Parent。它们都来自供应商.d 文件,所以我不能修改它们。
但不管怎样,我不确定我是否理解为什么这是一个错误。 Foo 类型说它需要扩展 Parent 的东西,而 Child 就是这样一个对象,那么为什么打字稿会认为它不可分配?
编辑:我已将其标记为已回答,因为添加 --noStrictGenericChecks
标志会抑制错误 (
所以要重申问题的核心,既然 Child extends Parent,为什么打字稿不再认为 Child 可以分配给 Parent,并且就 OOP 泛型而言,为什么比以前更正确?
2.4 版引入了更严格的泛型检查
阅读此 https://blogs.msdn.microsoft.com/typescript/2017/06/27/announcing-typescript-2-4/ 并查找标题 更严格地检查仿制药
来自文章:
As a temporary workaround for any breakage, you may be able to suppress some of these errors using the new
--noStrictGenericChecks
flag.
它还为回调引入了严格的逆变。
我认为问题在于您正在 return 一个需要 Child
作为参数的函数,但是将它分配给一个必须能够接受任何派生自的函数Parent
,不只是 Child
.
假设您将 createFooFunction<T>
称为 get Foo<T>
。根据 Foo<T>
的定义,可以创建一个扩展 Parent
的 class Child2
并将其作为参数传递给您得到的 Foo<T>
。这是一个问题,因为您实际上 return 编辑了一个函数,该函数只能将 Child
作为参数,永远不会 Child2
.
这其实更正确。请记住,您不会使用 Child
并将其分配给 Parent
。您正在使用一个只需要 Child
的函数,并将其分配给可以接受任何类型 Parent
的函数。这是非常不同的。另一方面,如果我们处理的是 return 值而不是输入,那就没问题了。这就是协变和逆变之间的区别,一开始可能有点混乱。在 C# 中,您可以使用 in
和 out
关键字在您自己的通用 classes 中指定它。
例如,在 C# 中,如果您有一个 IEnumerable<Child>
(声明为 IEnumerable<out T>
),它自然也是一个 IEnumerable<Parent>
,因为您正在迭代 - 您会得到 Child
objects out - 你也可以得到 Parent
objects out,因为每个 Child
也是一个 Parent
。因此,您可以将 IEnumerable<Child>
分配给 IEnumerable<Parent>
但不能反过来!因为你不能保证得到一个 Child
对象。
另一方面,如果您有类似 IComparer<Parent>
(声明为 IComparer<in T>
)的东西可以比较两个 Parent
对象 - 因为每个 Child
也是aParent
,它也可以比较任意两个Child
对象。因此,您可以将 IComparer<Parent>
分配给 IComparer<Child>
,但不能反过来 - 可以比较 Child
的东西只知道如何比较 Child
!这是你的问题。
你可以将in
和out
理解为回调中的输入(参数)和输出(return)值。您只能使输入更具体(逆变)并输出更通用(协变)。
顺便说一下,我认为 <P extends Parent>
在这里完全没用(而且它增加了混乱),因为你可以在那个函数中传递任何扩展 Parent
的东西,即使它不是'不是通用的。这只有在您像这样 return 编辑类型时才有用:<P extends Parent>(parent: P) => P
以在 return 值中保留正确的类型 P
。如果没有泛型,您将不得不使用 Parent
和 return Parent
,因此即使您输入了更具体的内容,您也会得到 Parent
。至于为什么如果你摆脱这个错误就消失了,我真的不知道。