All of these are just strings. But if you have a function that requires that string to be of type VerifiedMail, your type checker or compiler will either warn you about the misuse or not even let you compile the code (like in Rust).
This is good, because you as the programmer can rely on the fact that wherever you see a string of type VerifiedMail, that it is indeed a string containing a verified email adress, you don't even need to check, because you know the conversion between the different types had be done explicitly.
You can of course extend the whole thing and have a OnboardUser with a ValidatedMail and only convert the OnboardUser into an actual User once there is a VerifiedMail etc.
You get the idea. Whenever you find yourself wondering if a variable is actually holding the expected information, it is a good idea to leverage the type system to replace wondering with knowing.
But you are right: unchecked email could as well just be a string.
In context, there's presumably a form in a web page containing a claimed email address and other personal data that "evolves" a bunch of strings into valid Email, Address, Name etc. objects or into a collection of form fields with a list of error messages attached, depending on how validation goes.
This is good, because you as the programmer can rely on the fact that wherever you see a string of type VerifiedMail, that it is indeed a string containing a verified email adress, you don't even need to check, because you know the conversion between the different types had be done explicitly.
You can of course extend the whole thing and have a OnboardUser with a ValidatedMail and only convert the OnboardUser into an actual User once there is a VerifiedMail etc.
You get the idea. Whenever you find yourself wondering if a variable is actually holding the expected information, it is a good idea to leverage the type system to replace wondering with knowing.
But you are right: unchecked email could as well just be a string.