This is done using what is called "sum types" - we can tell the compiler that say a user can be "Premium" or "Regular". This distinction will affect policies across the codebase - eg. Premium customers get a discount on their shipping costs. We'll have robust unit tests and integration test that cover all these policies.
But even with the tests, we run into trouble when the feature request to create a new type of User comes in - "Semi-Premium" who gets only their _domestic_ shipping free. We now have to hunt and peck across the codebase, and change all our policies so that it handles this possibility well. Our tests are of no help here - they are meant to verify existing facts about the system, and can't tell when this user fall through certain policies because we forgot to handle it there. The type system on the other hand knows exactly the places where we decide things based on the kind of user. It will then realize that a new type of user has come in, and our policies don't handle them. This turns what usually is a high-risk and difficult task into an almost mechanical one.
What's happening here is that we're telling the compiler more things about our domain. This means the compiler can then remind us as we go about our business writing code, to handle every case, to never pass a null, and to help model our data structures that prevents inconsistent states from ever happening. The more we can encode in types, the more powerful this becomes.