Subtypes can specialise their parent type or leave it as is. This specialisation can be done via either adding some members like fields or methods, or by overriding members and widen or narrow their types. However, manipulating types is very tricky and can only be done depending on how members are accessed. In general, the Liskov substitution principle applies.
Subtype Requirement: Let be a property provable about objects of type T. Then should be true for objects of type S where S is a subtype of T.
-- Barbara Liskov and Jeannette Wing, 1994
In other words: Whatever you can do with an object typed T you should also be able to do with an object typed S, where S is a subtype of T. However, let's have a look at what is possible in TypeScript but questionable from a type safety point of view (and hence not allowed in N4JS, Java, and other languages):
class C1 {fieldC1: boolean = true;}class C2 extends C1 {// Liskov substitution principle violated here since type of 'fieldC1' gets narrowedoverride fieldC1: true = true; // note: 'true' is a subtype of 'boolean'}const c2 = new C2();c2.fieldC1 = false; // TypeScript ERROR: Type 'false' is not assignable to type 'true'.const c1: C1 = c2; // up-castc1.fieldC1 = false; // assign 'false' via supertypeconsole.log(c2.fieldC1) // yields print out: "false"if (c2.fieldC1 == false) { // TypeScript ERROR: This comparison appears to be unintentional because the types 'true' and 'false' have no overlap.// actually this is reachable}
What we see in the example above is how the Liskov substitution principle was broken for fields in TypeScript: We cannot do with c2 what we are able to do with c1, despite the fact that the type of c2 is a subtype of the type of c1. In order to preserve the Liskov substitution principle we know from languages like Java, N4JS and others, that we are neither allowed to narrow nor to widen the types of fields along the type hierarchy. This is called type invariance. In TypeScript however, covariance is allowed, that means that the type of fields can be narrowed to a subtype, like shown in the example above.
by Marcus Mews