[英]How do I define a class in Typescript that accepts an interface that has two fields for the same generic and guarantee they are the same type?
I apologize for the confusing wording in the title, I'm having a hard time coming up with a one line summary for the question. 对于标题中的措辞令人困惑,我深表歉意,我很难为这个问题写一个单行摘要。 Here is an example to explain what I'm trying to do: 这是一个示例,解释我要执行的操作:
interface Fish {
type: 'fish';
}
interface Dog {
type: 'dog';
}
type Animal = Fish | Dog;
interface MatingPair<T> {
animal: T;
otherAnimal: T;
}
class AnimalCollection<T> {
private pairs: T[] = [];
addPair<V extends T>(subject: V) {
this.pairs.push(subject);
}
}
let td = new AnimalCollection<MatingPair<Animal>>();
// Correctly generates error, dog is not a fish
td.addPair<MatingPair<Fish>>({
animal: { type: 'dog' },
otherAnimal: { type: 'fish' }
});
// Incorrectly lets dogs and fish be a mating pair
td.addPair({
animal: { type: 'dog' },
otherAnimal: { type: 'fish' }
});
I would like to be able to declare that addPair
should receive a version of MatingPair
that not just contains two "animals", but that the two animals are the same type. 我希望能够声明addPair
应该收到一个MatingPair
版本,该版本不仅包含两个“动物”,而且两个动物是同一类型。
Right now this code properly verifies that the call to td.addPair
on the last line is receiving two animals of the same type (fish, in this case), but only because I'm explicitly setting T
to be Fish
. 现在,此代码正确地验证了对最后一行td.addPair
的调用是否正在接收两个相同类型的动物(在这种情况下为fish),但这只是因为我将T
明确设置为Fish
。 Is there a way to define this such that I can say both that MatingPair.animal
& MatingPair.otherAnimal
both have values that are types within the union Animal, but also that the type is the same for both of them? 有没有一种方法可以这样定义,我可以说MatingPair.animal
和MatingPair.otherAnimal
都具有在联合动物中属于类型的值,而且两者的类型都相同?
You can reach your goal by restructuring the definition of the Animal collection class a bit like so: 您可以通过如下方式重构Animal集合类的定义来实现您的目标:
interface Fish {
type: 'fish';
}
interface Dog {
type: 'dog';
}
type Animal = Fish | Dog;
interface MatingPair<T> {
animal: T;
otherAnimal: T;
}
class AnimalCollection<T>
{
private pairs: MatingPair<T>[] = [];
addPair<V extends T>(subject: MatingPair<V>)
{
this.pairs.push(subject);
}
}
let td = new AnimalCollection<Animal>();
// Correctly generates error, dog is not a fish
td.addPair<Fish>({
animal: { type: 'dog' },
otherAnimal: { type: 'fish' }
});
// Correctly generates error, dog is not a fish
td.addPair({
animal: { type: 'dog' },
otherAnimal: { type: 'fish' }
});
Its not 100% what you ask for - but will solve the problem. 它不是您要求的100%,但可以解决问题。
A little bit of explanation. 一点解释。 Basically in your sample you are trying to do this: 基本上在您的示例中,您正在尝试执行以下操作:
let a: MatingPair<Animal> = {
animal: { type: 'dog' },
otherAnimal: { type: 'fish' }
}
And this is perfectly legal because both animal
and otherAnimal
are of type Animal
, just different animals. 这是完全合法的,因为animal
和otherAnimal
都是Animal
类型,只是不同的动物。
Whereas my subtle change makes code to look more like this: 而我的细微更改使代码看起来更像这样:
let a: MatingPair<Dog> = {
animal: { type: 'dog' },
otherAnimal: { type: 'fish' }
}
And this is wrong as fish is not a dog. 这是错误的,因为鱼不是狗。
In the real sample idea is the same, its just instead of Dog
I have put there requirement that V
is descendant of Animal
. 在实际示例中想法是相同的,只是我代替了Dog
要求V
是Animal
后代。 So either Dog
or Fish
but not both as they are incompatible. 因此,无论是Dog
还是Fish
但都不是两者,因为它们不兼容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.