| ▲ | IceDane 4 days ago | ||||||||||||||||||||||||||||||||||
Let's imagine we are working with some hypothetical language in which what you describe is possible. At some point, you will have to write a function where you validate/parse some arbitrary string, and it then returns some sort of `Email` type as a result. That function will probably return something like `Option<Email>` because you could feed it an invalid email. The implementation for that function can also be wrong, in exactly the same way the implementation for the typescript equivalent could be wrong. You would have to test it just the same. The guarantees provided by the typescript function are exactly equivalent, except for the fact that you do technically have an escape hatch where you can "force" the creation of a branded `Email` without using the provided safe constructor, where the other language might completely prevent this - but I've already addressed this. In practice, it doesn't matter. You only make the safe constructor available to the user, so they would have to explicitly go out of their way to construct an invalid branded `Email`, and if they do, well, that's not really your problem. | |||||||||||||||||||||||||||||||||||
| ▲ | 9rx 3 days ago | parent [-] | ||||||||||||||||||||||||||||||||||
> Let's imagine we are working with some hypothetical language in which what you describe is possible. Or even a real one. The hypothetical part is that you'd be able to understand it. The languages with complex type systems are complete bears to work with, as I am sure you can image. It isn't just some historical curiosity as to why we prefer to use testing over types to define the contract. It is way more practical. But, no matter how you feel about that, it remains that we do define the contract in tests in the languages people actually use. > The implementation for that function can also be wrong, in exactly the same way the implementation for the typescript equivalent could be wrong. The compiler would give you an error if you got the syntax wrong, and in isolation it's fair that you could, say, get the domain name wrong as long as it is syntactically valid. I suppose what I failed to convey, making some assumptions about your understanding of type systems, is that the types would not just specify RFC-compliance. You would also spec out other dependencies such that you also couldn't even provide the wrong domain name without a compiler error. So, no, the implementation of the function couldn't be wrong. You could mis-spec the contract, of course. Then the function could be "wrong". Maybe this is the intent behind what you are trying to say here. But the idea has always been here that I would also read and accept the contract, so it wouldn't actually be "wrong". It would be exactly what we both expect. My side will be written to what the contact specifies. If you try to deviate from the contract later, you'll face the wrath of the compiler. As you point out, that's not the case in Typescript. If you try to change the contract (even if by accident) the compiler/typechecker will never say a word. I won't know until my code starts breaking. Your tests are the only thing you have to keep you in line with what we agreed upon. Tests define the contract in its case. | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||