Apologies for length, but it’s a big topic and I want to give a high-level view of the whole area. Here are the essential points:
Haskell’s type system is pretty flexible and mostly unobtrusive (unlike some static systems).
A good type system is a powerful design language, too.
We can also work with types in a “type-driven development” style.
Parametric polymorphism is about using the same code for everything.
Interfaces provide clean overloading and encourage and support approaches like DCI.
Often Haskell feels like a dynamically typed language, though with a good safety net in reserve.
Haskell has its limits, but the cost-benefit trade-off is probably in our favor.
There are several options beyond Haskell, such as dependent types.
Dependently typed languages are an elegant combination of computation and reasoning.
Many TDD specs can be encoded as (dependent) types, and vice versa.
I believe the distinction between tests and types is blurring, and each side can learn much from the other to capitalize on this.