有人可以向我解释为什么要在Typescript中编译吗?
class Result<T> {
object: T | null = null;
}
function setOnlyA(res: Result<{ a: number }>) {
res.object = { a: 5 };
}
function setAB(res: Result<{ a: number; b: string }>) {
setOnlyA(res);
// everything compiles fine, but res object is invalid
// at this point according to type definitions
}
我希望setOnlyA
中不允许进行setAB
呼叫。我启用了strict
模式。我需要其他设置吗?
答案 0 :(得分:3)
不幸的是,这是打字稿类型系统的一个基本问题。即使可读写字段实际上应使类型不变,也假定字段是协变的。 (如果您想了解协方差和逆方差,请参阅此answer)。
Ryan Cavanaugh在this中进行了解释:
这是默认情况下的协变量类型系统的一个基本问题-隐式假设是通过超类型别名进行的写操作很少,这是正确的,除非不是这样。
严格遵守字段差异可能会给用户带来极大的痛苦,甚至对功能启用严格的差异也仅针对函数类型而不是针对方法(详细here:
):更严格的检查适用于所有函数类型,但源于方法或构造函数声明的函数类型除外。专门排除方法以确保通用类和接口(例如Array)
有人建议启用writeonly
修饰符(并严格要求readonly
)或具有显式的co / contra-contra-variant注释,因此稍后我们可能会得到一个严格的标志,但是目前这是TS团队做出的不明智/不实用的权衡。
答案 1 :(得分:0)
可以,因为{ a: number; b: string }
是{ a: number}
的子类型。这就是打字稿的工作方式:https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#14-structural-subtyping
答案 2 :(得分:0)
系统关心的是您实现类型,而不是实现完全相同的类型,像接口一样考虑它-
答案 3 :(得分:0)
我的固定代码如下:
class Result<T> {
private object: T | null = null;
// this solves the problem
setObject = (o: T) => {
this.object = o;
};
// this doesn't
//setObject(o: T) {
// this.object = o;
//};
}
function setOnlyA(res: Result<{ a: number }>) {
res.setObject({ a: 5 });
}
function setAB(res: Result<{ a: number; b: string }>) {
setOnlyA(res);
}
即解决方案是使用lambda作为设置器。使用常规成员函数不起作用-打字稿无法像原始代码中那样发现问题。