[英]TypeScript incompatible function signatures
The following code is just an example to reproduce an error that I don't understand.以下代码只是重现我不明白的错误的示例。
In setFilter
I have narrowed the type of T
to Product
, so I would expect to be able to set filter
successfully, because T
and Product
have the same subtypes.在
setFilter
,我将T
的类型缩小到Product
,因此我希望能够成功设置filter
,因为T
和Product
具有相同的子类型。
How is it true that: 'T' could be instantiated with a different subtype of constraint 'Product'.
事实如何:
'T' could be instantiated with a different subtype of constraint 'Product'.
? ?
EDIT: I realised that you can pass a subtype into setFilter
that would make these signatures incompatible... so how could I define filter
to allow this pattern?编辑:我意识到你可以将一个子类型传递给
setFilter
,这会使这些签名不兼容......那么我怎么能定义filter
来允许这种模式呢?
type Product = { type: string };
let filter: (product: Product) => boolean = () => true;
function setFilter<T extends Product>(productType: string) {
filter = (product: T) => product.type === productType;
/* Type '(product: T) => boolean' is not assignable to type '(product: Product) => boolean'.
Types of parameters 'product' and 'product' are incompatible.
Type 'Product' is not assignable to type 'T'.
'Product' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Product'.
*/
}
const res = [{ type: 'subscription' }, { type: 'hardware' }, { type: 'hardware' }];
setFilter('hardware');
console.log(res.filter(filter));
This works for me, just remove (:T):这对我有用,只需删除(:T):
type Product = { type: string };
let filter: (product: Product) => boolean = () => true;
function setFilter<T extends Product>(productType: string) {
filter = (product) => product.type === productType;
}
const res = [{ type: 'subscription' }, { type: 'hardware' }, { type:
'hardware' }];
setFilter('hardware');
console.log(res.filter(filter));
The problem is that when you define T extends Product
then TypeScript can't guarantee that T
would be correct.问题在于,当您定义
T extends Product
TypeScript 无法保证T
是正确的。 Here is a minimal example, let's say we have:这是一个最小的例子,假设我们有:
type Product = { type: string };
interface SubProductA extends Product {
name: string
}
interface SubProductB extends Product {
rating: number
}
declare let filter: (product: Product) => boolean
const product1: Product = {
type: "foo"
}
const product2: SubProductA = {
type: "bar",
name: "Fred"
}
const product3: SubProductB = {
type: "baz",
rating: 5
}
filter(product1); //valid! it's a Product. But not SubProductA
filter(product2); //valid! it's a Product *and* SubProductA
filter(product2); //valid! it's a Product. But not SubProductA
See on TypeScript Playground 请参阅 TypeScript Playground
The filter
expects a function that only expects Product
, so calling it with the parent type is fine - that's guaranteed to work. filter
需要一个只需要Product
的函数,所以用父类型调用它是可以的- 这保证可以工作。 You can also call it with a subtype - that's also fine since it's assignable to its parent.您也可以使用子类型调用它 - 这也很好,因为它可以分配给它的父类型。 However, you cannot guarantee that
filter
will be called with a specific subtype.但是,您不能保证将使用特定子类型调用
filter
。 Just to illustrate this as clearly as possible:只是为了尽可能清楚地说明这一点:
type Animal = { };
interface Cat extends Animal {
cute: number
}
interface Dog extends Animal {
friendly: number
}
function getCatsAndDogs(): Array<Cat | Dog> {
return []; //dummy implementation
}
const dogFilter: (x: Dog) => boolean = x => x.friendly > 9;
const catFilter: (x: Cat) => boolean = x => x.cute > 9;
let arr: Array<Animal> = getCatsAndDogs(); //valid (Cat | Dog) is assignable to Animal
arr.filter(dogFilter); //not valid - we cannot guarantee Animal would be Dog
arr.filter(catFilter); //not valid - we cannot guarantee Animal would be Cat
See on TypeScript Playground 请参阅 TypeScript Playground
So, if you intend to only use the common parent, so you don't depend on anything defined in a subtype, then you don't need a generic, just use the parent type:因此,如果您打算只使用公共父类型,因此不依赖于子类型中定义的任何内容,那么您不需要泛型,只需使用父类型:
function setFilter(productType: string) {
filter = (product: Product) => product.type === productType;
}
This works because every subtype of Product
is a product but not every Product
is a specific subtype.这是有效的,因为
Product
每个子类型都是一个产品,但不是每个Product
都是一个特定的子类型。 The same way as every Cat
is an Animal
but not every Animal
is a Cat
.就像每
Cat
都是Animal
但不是每只Animal
都是Cat
。
If you do need to use a subtype for filtering, you can get around it using a type assertion which will look something like this:如果您确实需要使用子类型进行过滤,则可以使用类型断言来解决它,该类型断言如下所示:
filter = (p: Product) => (g as SubTypeA).name === "Fred";
Or if using a generic argument T
then:或者,如果使用通用参数
T
则:
filter = (p: Product) => (p as T)/* include something T specific not applicable for Product*/;
This is known as downcasting because you convert from the parent/super type to the child/sub type.这称为向下转换,因为您从父/超类型转换为子/子类型。
However, as the compiler already said, type safety cannot be guaranteed at compile time.但是,正如编译器已经说过的,在编译时不能保证类型安全。 Unless you have information the compiler doesn't and you're sure that
filter
will actually be called with a specific subtype, then you shouldn't just use a straight type assertion there.除非你有编译器没有的信息,并且你确定
filter
实际上会用特定的子类型调用,否则你不应该只在那里使用直接类型断言。 To guarantee type safety, you can use a type guard :为了保证类型安全,您可以使用类型保护:
type Product = { type: string };
interface SubProductA extends Product {
name: string
}
interface SubProductB extends Product {
rating: number
}
function isSubProductA(product: Product): product is SubProductA {
return "name" in product;
}
declare let filter: (product: Product) => boolean
filter = (product: Product) => {
if (isSubProductA(product)) {
//no need to do type assertion we're sure of the type
return product.name === "Fred";
}
return false;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.