Little known technique for self-describing code
- Published
It is very common to describe your code with comments but they have one big issue. Quickly getting out of date.
One developer creates a code, adds few comments.Second developer changes the code a little bit and forgets about comments.Third developer changes the code and forgets about comments.So on so forth and then... You can no longer trust if the comments are actually valid/accurate.
Here I'd like to explain you one more method that I found useful, reduces amount of necessary comments and it's type-safe.
Monads!
My personal choice for implementation of monads is @sweet-monads
as it does not force you to go into full-blown FP
programming like fp-ts does.
Returning single entity but sometimes it cannot be found
If a method tries to find something but it might not be found then instead of returning Smth
or undefined
use Maybe
instead. That clearly describes the operation might return an empty result.
In other words we can say:
find article by id and optionally return result
class ArticleRepository {
findById(id: string): Promise<Maybe<Article>> {
}
}
Especially useful for environments where strictNullCheck
is not enabled for some reason.
Creating an entity but fails once it is a duplicate
If a method creates an entity but that entity violates some constraints then instead of throwing an error return Maybe
.
In other words we can say:
create entity and optionally return created entity if that succeeds, if none then there is a duplicate
class ArticleRepository {
create(input: ArticleCreateInput): Promise<Maybe<Article>> {
try {
// make insert to database
// if succeeds
return just(newlyCreatedArticle);
} catch (e) {
if (e.code === 'UNIQUE_VIOLATION') {
return none();
}
// throw other errors
throw e;
}
}
}
Parse something but parsing is likely to fail
This one is my favorite.
Parsing some input is very likely to fail since simply input might not be valid. How are you going to catch an error for it? Creating new error type? How do you know you've handled all type of errors possible to be thrown by parser?
For all that problems here it comes - Either
.
function parseDomain(
domain: string
): Either<'INVALID_SYNTAX' | 'UNKNOWN_TLD' | 'TOO_LONG', Domain> {
}
In other words we can say:
parse domain and return fail which might one of following strings, if succeeds return
Domain
object
- No extra error types
- No unnecessary try/catch when you don't need it
- You know upfront what kind of errors to expect
- If something unexpected happens (regular
TypeError
s or smth) then error gets thrown
Other use cases for Either
- regular data validation -
Either<Violations, SanitizedData>
- RPC operations -
Either<RPCError, TResult>
- Run a task but it might fail -
Either<TaskError, TResult>