Logo

dev-resources.site

for different kinds of informations.

🏗️ `Is` Methods

Published at
12/18/2024
Categories
typescript
functional
Author
oscareduardolp6
Categories
2 categories in total
typescript
open
functional
open
Author
15 person written this
oscareduardolp6
open
🏗️ `Is` Methods

Lately, I’ve been deeply immersed in functional programming with languages like TypeScript and to a much lesser extent with Haskell. Beyond the specificities of functional programming like Monads, I’ve been fascinated by concepts related to typing, such as Union Types and the utilities they bring.

Among these utilities, I’ve learned the concept of making invalid states unrepresentable. This means avoiding methods like isX and, instead of having to constantly check those conditions, knowing through the simple type system that your state is valid or invalid.

This is not exclusive to functional programming. In fact, it’s closely related to concepts like Value Objects in object-oriented programming, but it integrates well with Algebraic Data Types.

Make illegal states unrepresentable

- Yaron Minsky

🚯 Making Invalid States Unrepresentable

We’re used to situations where entities can exist in multiple states, and we typically use tools like enums or constants. While this is certainly better than using magic numbers or strings, it still has some drawbacks.

For example, we often have to check if the entity is in the state we need before we can work with it.

❎ Alternative with is or enums

With this approach, methods that depend on the User being in the verified status must check the status before executing their logic. This can lead to runtime exceptions and forces us to implement ways to handle those errors.

type User = {
  id: string
  name: string
  email: string
  verified: boolean
}

const createUser = (name: string, email: string): User => ({
  id: crypto.randomUUID(),
  verified: false,
  name,
  email
});

const verifyUser = (user: User): User => ({
  ...user,
  verified: true
})

const sendPromoCode = (user: User): void => {
  if (!user.verified) throw new Error('Cannot send code to unverified user')
  console.log('Promo code sent')
}

const unverifiedUser = createUser('Oscar', '[email protected]')

sendPromoCode(unverifiedUser) // No error & insecure at runtime

const verifiedUser = verifyUser(unverifiedUser)

sendPromoCode(verifiedUser) // No error
Enter fullscreen mode Exit fullscreen mode

✅ Without is Methods or enums

By ensuring that the sendPromoCode method can only receive verified users, this method focuses solely on the logic of sending the promo code and doesn’t have the responsibility of validating if the User is in a valid state. Additionally, the validation is moved to a higher level, with type system verification providing additional safety.

// Without is methods or enums
type UnverifiedUser = {
  id: string
  name: string
  email: string
}

type VerifiedUser = UnverifiedUser & { verifiedAt: Date }

const createUser = (name: string, email: string): UnverifiedUser => ({
  id: crypto.randomUUID(),
  name,
  email
});

const verifyUser = (unverifiedUser: UnverifiedUser): VerifiedUser => ({
  ...unverifiedUser,
  verifiedAt: new Date()
})

const sendPromoCode = (verifiedUser: VerifiedUser): void => {
  console.log('Promo code sent')
}

const unverifiedUser = createUser('Oscar', '[email protected]')

sendPromoCode(unverifiedUser) // Error: 'UnverifiedUser' is not assignable to parameter of type 'VerifiedUser'

const verifiedUser = verifyUser(unverifiedUser)
sendPromoCode(verifiedUser) // No error
Enter fullscreen mode Exit fullscreen mode

📖 Conclusion

Adopting the approach of making invalid states unrepresentable allows us to build safer, more robust, and maintainable systems. By delegating validation to the type system itself, we reduce the burden of redundant checks in our code and eliminate an entire class of runtime errors, allowing the compiler to serve as our first line of defense.

Moreover, this practice is not limited to a specific paradigm. It fits well within both functional programming and object-oriented programming. By combining concepts such as Algebraic Data Types, Value Objects, or even techniques inspired by Domain-Driven Design, we can model our domains in a clearer, more readable, and semantic way.

Ultimately, type-driven design not only improves the development experience but also forces us to carefully consider our models, ensuring that our code faithfully represents business rules. In this way, we prevent invalid states from existing, even as a possibility, within our system.

💡 I invite you to reflect on your current practices and experiment with this technique in your next projects. The benefits might surprise you!

🗒️ References

This article is based on information from the following sources:

functional Article's
30 articles in total
Favicon
A monad is a monoid in the category of endofunctors...
Favicon
Rust-like Iteration in Lua
Favicon
Transducer: A powerful function composition pattern
Favicon
🏗️ `Is` Methods
Favicon
7.bet’s Bold Move: Play Smarter, Play Safer, Play Better!
Favicon
Harnessing the Power of Object-Oriented and Functional Programming Paradigms in Software Development
Favicon
Lambda vs. Named Functions: Choosing the Right Tool for the Job
Favicon
Object-Oriented vs Functional Programming—Why Not Both?
Favicon
From C# to Haskell and Back Again: My Journey into Functional Programming
Favicon
Comprehensive Guide to Automated Functional Testing
Favicon
Functional Programming in Go with IBM fp-go: Error Handling Made Explicit
Favicon
Razumevanje funkcija višeg reda (Higher-Order Functions) u JavaScript-u
Favicon
What is Functional Programming, and How Can You Do It in JavaScript?
Favicon
Parallel Testing: Best Practice for Load Testing & Functional Testing
Favicon
For loops and comprehensions in Elixir - transforming imperative code
Favicon
Advent of Code and Aesthetics
Favicon
PureScript for Scala developers
Favicon
Clojure REPL-Driven Development with VS Code
Favicon
Combining Object-Oriented and Functional Programming in Large Projects
Favicon
Non-Functional Requirements: A Comprehensive Guide
Favicon
Unpacking Lambda Expressions: What They Are and Why They Matter
Favicon
Functional Programming: A Misfit for Backend Engineering
Favicon
Scope progression
Favicon
JavaScript Functions for Beginners: Quick Guide
Favicon
Tech Watch #2
Favicon
Either Algebraic Data Type
Favicon
Functional Programming in C#: The Practical Parts
Favicon
A 20-liner Drag'n'Drop feat using ObservableTypes
Favicon
On “superiority” of (functional) programming and other labels
Favicon
Top Open Source functional programming technologies

Featured ones: