Matthias Falk
2 min readAug 3, 2021

--

Some unpleasant properties of TypeScript not mentioned

Type Parameter Constraints for Generics

If you have a `type T<U extends V> = ...` and `V` is an explicit or implicit union type (boolean is an implicit union type as it behaves exactly as `true | false`), what you normally mean is that `U` should be instantiated by exactly one component of `V` but neither does TypeScript complain in any form if `U` is instantiated by a union of subtypes of `V`, nor is there an easy method to restrict instantiations of `V` to "ground subtypes" (even though I have found a (dirty, as I think) workaround exploiting TypeScripts distribution behaviour, ask me if you are interested).

Example: `type T<U extends boolean> = ...`. If I use such a construction, I normally want `U` to be instantiated by either `true` or `false` but not by `true | false`. However, TypeScript does not complain about the latter. This might be a problem if you use an instantiation `T<U1>` while `U1 ` is a complicated generic type instantiation that you expect/intend to evaluate to either`true` or `false` but not to`true | false`. Here an - to my mind - avoidable source of bugs come up if you do some more intensive type programming.

Type inference

If it only worked reliably. I am quite often annoyed to get from TypeScript the error message "TS2589: Type instantiation is excessively deep and possibly infinite." (for a type that is perfectly OK, just a bit more complex). Sometimes a dirty and ugly trick helps out: If I have such a `type T<U extends V> = ...` for which TypeScript refuses to evaluate its instantiation `T<V1>` for a subtype `V1` of `V` with the said error message, you can do the inference work of TypeScript by yourself in the following way:

1. Define a fully expanded `type T_V1 = …` (without type parameters) that is equivalent to `T<V1>`
2. Define `type EQ<T, U> = [U] extends [V] ? ([V] extends [U] ? true : false) : false;` (that checks the types `T` and `U` for equivalence)
3. `let test: EQ<T_V1, T<V1>>;` (Verify that the type of `test` evaluates to `true`)
Then, some magic happens: The error message for T<V1> suddenly vanishes. I suspect some caching mechanism to be at work here. However, this is a very very ugly behavior to my mind. TypeScript has some arbitrary limitations built in that are documented nowhere that can drive you nuts, not to forget that I have to spoil my code with dead bodies such as those 3 lines listed above just to get the perfectly valid type instantiation T<V1> compiled.

--

--