Just chiming in to say you're correct -- that's exactly what I meant.
For example, if you write a function that only makes sense on numbers greater than zero, then make it take a `GreaterThanZero<Int>`. It's a contrived example but people often pride themselves on writing unit tests that check for "out of bounds" cases like that, and defensive coding in dynamic languages usually starts with "make sure they gave you the right things and nothing is null".
There are cracks to slip through with Typescript (it's not as good as using a language that enforces this stuff down to the runtime -- i.e. haskell), but in general you're going to avoid these kinds of errors in the first place if you write sufficiently useful types.
Another hot take:
The value of different types of testing is as follows (most valuable first):
- E2E tests that test customer flows
- E2E tests that prevent regressions (made after a bug was discovered)
- Smoke tests
- Integration tests
- Unit tests