Articles I've gathered that discuss improved leveraging of the type system in order to produce less errors. All summaries are built _from my own understanding_, and are _for my own notes_, and as such _may be inaccurate_. I suggest you read the articles yourself :-) --- Designing with types: Making illegal states unrepresentable F# for Fun and Profit (2013) [https://fsharpforfunandprofit.com/posts/designing-with-types-making-illegal-states-unrepresentable/] An article on making invalid/illegal states irrepresentable by using the type system. A tangential example to what the article writes about: A user can have either a email, or a phone number, or both, but not not neither. Naïvely, you might implement this as such: ```ts type User = { email: string | null, phone_number: string | null, }; ``` But now you can have invalid/illegal state! `{ email: null, phone_number: null }` If you instead use a tagged union (also referred to in our circle as "a rust enum"; more generally, a part of the umbrella of algebraic data types): ```ts type ContactInfo = | { tag: "only_email", email: string } | { tag: "only_phone_number", phone_number: string } | { tag: "both", email: string, phone_number: string } type User = { contact_info: ContactInfo, } ``` It's now impossible to create a user that does not uphold the invariant of "A user can have either a email, or a phone number, or both, but not not neither." As a bonus, you can also switch on the `tag` of enums written in this manner to handle the different situations, all with the support of the type checker! ```ts switch (contact.tag) { case "only_email": console.log("User email is", contact.email); break; case "only_phone_number": console.log("User phone # is", contact.phone_number); break; case "some_invalid_tag": // ERR: "some_invalid_tag" is not "only_email" | "only_phone_number" | "both" break; case "both": console.log("User email is", contact.email); console.log("User phone # is", contact.phone_number); break; } ``` And if you return a value, it'll force you to handle the values exhaustively too (most languages with ADTs handle exhaustiveness checking a little bit better, TypeScript is a little behind on this front) ```ts function consume_contact(contact: ContactInfo): true { // ERR: `void` is not `true` switch (contact.tag) { case "only_email": console.log("User email is", contact.email); return true; // case "only_phone_number": // console.log("User phone # is", contact.phone_number); // break; case "both": console.log("User email is", contact.email); console.log("User phone # is", contact.phone_number); return true; } } ``` --- Parse, don't validate Alexis King (2019) [https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/] Article explaining the concept of "parse, don't validate" - i.e., instead of validating an email to be an email, instead attempt to parse it as an email, and return either an Email data type, or an error. This forces the user to handle the error, and ensures that whenever the user has an Email, it is indeed a valid email. --- No, dynamic type systems are not inherently more open Alexis King (2020) [https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/] An article debunking some misconceptions about dynamic type systems, namely, as the title suggests, that they are inherently more open and permissive. The author argues that both statically typed languages and dynamically typed languages are as a rule of thumb equally permissive and open, statically typed languages simply force you to upkeep invariants in the type system as well as in the code, which can help you catch errors. --- Types as axioms, or: playing god with static types Alexis King (2020) [https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/] --- Names are not type safety Alexis King (2020) [https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/] Article talking about wrapper type abuse. The examples are primarily around Haskell's `newtype`, but you could also draw perspectives to i.e. a `type Email = { inner: string };` as seen in TypeScript, or the various other representations of wrapper types in other languages.