You can make those factory functions nearly as concise as the class examples if you wanted:
const createEmployee = (firstName, lastName) => ({ firstName, lastName })
const createContractor = (firstName, lastName) => ({
...createEmployee(firstName, lastName),
//overloaded propeties here
})
const createFullTimeEmployee = (firstName, lastName) => ({
...createEmployee(firstName, lastName),
//overloaded propeties here
})
I've always thought more in terms of consumption - who will be using this code and how will it be used?
My probably unscientific approach being a dutiful Typescript worker bee has been to take the general approach:- Try to just use a function, especially when your output will only be consumed one way
- If the domain of the logic that you are working must encapsulate multiple ways to be consumed and there isn't a clear primary use case, and much of the logic and state should be shared between functions, consider a class
I don't find the end result to be significant for anything other than code cleanliness/readability. Admittedly I've never really had language features themselves be performance bottlenecks doing mostly frontend web work. This may be too specific of an observation of Javascript itself to be a broad opinion though. I'd love to hear someone else's opinion as my experience is not from a compsci background.