I’m a big believer in “the types should constrain the semantics of my program so hard that there is only one possible program I could write, and it is correct”. Of course we have to sacrifice some safety for speed of programming; for many domains, being 80% sure that a feature is correct in 95% of the possible use cases is good enough to ship it. But in fact I find that I code *faster* with a type system, because it forces most of the thinking to happen at the level of the problem domain (where it’s easy to think, because it’s close to real life); and there are a number of ways one can extremely cheaply use the type system to make invalid states unrepresentable in such a way that you no longer have to test certain things (because there’s no way even to phrase a program that could be incorrect in those ways).
For a super-cheap example, if you know that a list is going to be nonempty, use a non-empty list structure to hold it. (A non-empty list can be implemented as a pair of a head and a list.) Then you can save all the time you might otherwise have spent on coding defensively against people giving you empty list inputs, as well as any time you might have spent testing against that particular corner case.
For another super-cheap example that is so totally uncontroversial that it probably sounds vacuous (but it is in fact the same idea of “represent what you know in the type system so that the language can help you”), don’t store lists of (key, value); store a dictionary instead, if you know that keys are unique. This tells you via the type system that a) keys are definitely unique, and b) various algorithms like trees or hashmaps can be used for efficiency.