It’s a more generalized solution, that’s for sure, but it feels like it adds a lot of complexity and indirection in the process. The “flow of code execution” is all around with the callbacks, and it took me a few back and forths to actually understand how it was working.
Now, if you have a large system and many more of these “type keys”, by all means, this is an appropriate solution. But in the example of user type / creation, I beg to differ: the earlier example is much simpler to understand and reason about.
This article also inadvertently shows what I think is a big drawback of the inversion of control pattern.
Imagine you are trying to debug an issue with user creation.
In the last example, you would have to look up everywhere `createUser` is being called, and follow the code path through several different scattered files until you find your issue.
In the original code, you can simply look up `createUser` and you have the complete code flow in front of you.
This comment speaks to me. I like to be able to follow code flow to figure out what's going on, and that means I prefer "from here to <somewhere>" rather than trying to figure out "to here from <somehwhere>". Mind you, sometimes passing in a function is the right answer, but I try to limit it to things more like higher order functions than "adding logic to an existing method".
Agreed. Another situation where type keys aren’t the grinch is de/serialisation. A contrived example: it’s somewhat simple to imagine the first example as half of a request handler, taking in some string input, asserting it’s within the set of type keys, then acting on it. Later examples, not so much. IMO a great example of “don’t let the perfect be the enemy of the good”
Now, if you have a large system and many more of these “type keys”, by all means, this is an appropriate solution. But in the example of user type / creation, I beg to differ: the earlier example is much simpler to understand and reason about.