考虑这个小小的Typescript例子:
interface Parent {
name: string;
}
interface Child extends Parent {
name: "Joe";
}
这有效,但我不太明白,为什么。
我对继承的理解是,理想情况下你应该能够说出这样的话:“孩子的行为与父母的行为相同”,因此“延伸”这个词。但在这种情况下,Parent允许任何字符串作为名称,而Child仅允许特定字符串。所以Child缩小了Parent的可能性,你不能说,Child的行为与Parent的行为方式相同。我在这里考虑Liskovs替代原则。
那么它在Typescript中的运作方式背后的基本原理是什么?
答案 0 :(得分:4)
对于类型,它与可分配性有关。类型的工作方式是子类型应该可以在没有断言的情况下分配给父类型。
在这种情况下,孩子比父母更具体,所以允许 - "Joe"
是特定的,string
是一般的。它是可分配的,因为string
包含所有字符串的可能性 - 包括"Joe"
。
常规包含具体内容,但具体不包括一般
因此,您可以将孩子分配给父母:
const child: Child = { name: "Joe" };
const parent: Parent = child; // ok, because name being "Joe" is assignable to string
但你不能将父母分配给孩子:
const parent: Parent = { name: "Alex" };
const child: Child = parent; // not ok, because name being string might not be "Joe"
其他示例
这就是为什么你可以拥有一个具有可选属性的父级和一个具有必需属性的子级的原因:
interface Parent {
name?: string;
}
interface Child extends Parent {
name: string;
}
可变性危险
如果使用得当,以这种方式添加限制可能非常有用。我偶尔会这样做。
虽然属性是可变的但是很危险。如果使用不当,可能会发生以下情况:
const child: Child = { name: "Joe" };
const parent: Parent = child;
parent.name = "Alex";
console.log(child.name); // Alex
这可能是你考虑LSP的原因。为了避免这种危险,可以将对象的属性标记为readonly
并以不可变的方式使用。