First of all, from a type-theory point of view, both Email and WebForm have the same type. (same signature).
Also from a theoretical point of view, i think a type should not have an instance if its not valid and "EmailAddress" of value "aasda" should not exists. However it is allowed in his types.
Also, I don't see how defining lots of very specific types such as FromAddress and ToAddress are really making live easier.
Something like this does the job, and is typesafe (in golang):
type EmailAddress string
type Email struct {
to EmailAddress
from EmailAddress
subject string
body string
}
func sendEmail(webForm string) err = {
email, err := validateEmailForm(form) # performs validation
if err {
return err
}
err := email_service.sendEmail(email)
if err {
return err
}
}
Tests will simply use the returnValue of sendMail to validate its inner working.