TypeScript lets it go through because the T in Superclass<T> does not affect its shape, therefore Superclass<A> and Superclass<B> have the same shape, and are therefore considered "compatible" by TypeScript.
Remember that TypeScript has a structural type system where types are considered equivalent by their members, even if their actual and original definitions are completely separate (think of convergent evolution). As for why it's surprising: Remember that TypeScript does not have a nominative type-system, where types with identical members, structure and layout, right down to cross-platform binary representations, are considered strictly different, as long as they have different type-names (this is how Java, C#, and many other languages' type systems work).
So while Superclass<T> is not using T and it doesn't affect its shape, so tsc considers T superfluous and ignores it:
So this...
class Superclass<T> {
combine(object: Superclass<T>): Superclass<T> { ... }
}
...is provably equivalent to:
class Superclass {
combine(object: Superclass): Superclass { ... }
}
However, as soon as Superclass<T> becomes dependent upon T, such as by adding a field typed as T, then it throws an error:
class Superclass<T>{
public value: T;
constructor(value: T) {
this.value = value;
}
combine(object: Superclass<T>) {
console.log("combined: " + object.value.toString());
return object;
}
}
class Generic1 {
x = Math.random();
}
class Generic2 {
y = Math.random();
}
var x = new Superclass<Generic1>( new Generic1() ).combine(new Superclass<Generic2>( new Generic2() ) );
Error:
Argument of type 'Superclass<Generic2>' is not assignable to parameter of type 'Superclass<Generic1>'.
Property 'x' is missing in type 'Generic2' but required in type 'Generic1'.
As for why this is the case - this is (I think) explained in TypeScript's documentation in the section about Type Compatibility:
The basic rule for TypeScript’s structural type system is that x is compatible with y if y has at least the same members as x
In this case, when Superclass<T> is closed as either Superclass<Generic1> or as Superclass<Generic2> both types have exactly the same members, and TypeScript considers them compatible wither each other.