假设编译器对您实现IDog 的方式没有任何问题。那么以下就可以了:
class Dog implements IDog {
private name = "Buddy";
public likes<T extends Dog>(other: T) {
return other.name.toUpperCase() === "FRIEND";
}
}
const myDog: IDog = new Dog(); // should be okay if Dog implements IDog
但这可能会导致编译器无法捕获的运行时错误:
const eyeDog: IDog = {
likes(other) {
return true;
}
}
console.log(myDog.likes(eyeDog)) // okay for the compiler, but RUNTIME ERROR
所以编译器认为Dog 没有正确实现IDog 是正确的。允许这样做是"unsound"。如果你有一个想要扩展的函数类型(更具体),你不能让它的参数更具体和更健全;你需要让它们更通用。这意味着应该检查函数参数contravariantly(也就是说,它们与函数类型相反......它们反变化......逆变)。
当然,这会导致您对Cat 产生疑问。完全相同的论点在那里不起作用吗?
class Cat implements ICat {
private name = "Simba";
public likes(other: Cat) { // no error
return other.name.toUpperCase() === "FRIEND";
}
}
const myCat: ICat = new Cat(); // no error
const eyeCat: ICat = {
likes(other) { return true; }
}
console.log(myCat.likes(eyeCat)) // no compiler error, but RUNTIME ERROR
确实如此!编译器允许使用 Cat 对 ICat 进行不合理的扩展。什么给了?
这是明确的故意行为;方法parameters are checked bivariantly,这意味着编译器将接受更广泛的参数类型(安全)和更窄的参数类型(不安全)。这显然是因为,在实践中,人们很少使用myCat(或myDog)编写上面那种不安全的代码,而这种不安全正是允许存在许多有用的类型层次结构的原因(例如,TypeScript 允许Array<string> 是 Array<string | number> 的子类型。
那么,等等,为什么编译器关心泛型类型参数的可靠性而不关心方法参数?好问题;我不知道对此有任何“官方”答案(尽管我可能会查看 GitHub 问题以查看 TS 团队中是否有人对此发表过评论)。一般来说,TypeScript 中的健全性违规是根据启发式和实际代码仔细考虑的。
我的猜测是人们通常希望他们的泛型具有类型安全性(正如microsoft/TypeScript#16368 对它们的更严格检查的实现所证明的那样),特别是添加额外的代码以允许方法参数双变量会比它的价值更麻烦。
您可以通过启用the --noStrictGenericChecks compiler option 来禁用泛型的严格性检查,但我不建议故意降低编译器的类型安全性,因为它的影响远不止Dog 问题,而且很难找到资源当您依赖不寻常的编译器标志时寻求帮助。
请注意,您可能正在寻找每个子类或实现类只能 likes() 其自己类型的参数而不是所有可能的子类型的模式。如果是这样,那么您可以考虑改用the polymorphic this type。当您使用this 作为类型时,它就像泛型类型,表示“调用此方法的子类是什么类型”。但它是专门为允许您似乎正在做的事情而设计的:
interface IGoldfish {
likes(other: this): boolean;
}
class Goldfish implements IGoldfish {
private name = "Bubbles";
public likes(other: this) {
return other.name.toUpperCase() === "FRIEND";
}
}
const myFish: IGoldfish = new Goldfish();
这当然和其他两个例子有同样的问题:
const eyeFish: IGoldfish = { likes(other) { return true; } }
console.log(myFish.likes(eyeFish)) // RUNTIME ERROR
所以它不是治疗不健全的灵丹妙药。但它与没有泛型参数警告的泛型版本非常相似。
Playground link to code