Typescript uses a structural type system. If you don't use type parameters they don't much matter. In your case you do use the type parameters, but in a recursive way. This will probably mean the compiler will check the parameters for a while but will not manage to find a definite incompatibility between A<string, number> and A<number, string> so it will asume they are compatibile.
Typescript uses a pragmatic approach to a type system. It should be useful most of the time while modeling the structural behavior of Javascript. Most of the time this works well, you just found one of the corner cases where this gives some surprising results. (I hesitate to say it gives a wrong result, because I can't think of a runtime error this admittedly surprising type compatibility will cause)
The good news is that this behavior goes away as soon as you add a member that uses any of the type parameters in a more direct way:
export class A<T, K> {
private k!:K
public foo(a: A<K, T>): A<K, T> {
return a;
}
}
const a1 = new A<string, number>();
const a2 = new A<string, number>();
a1.foo(a2); // error