Last chance @ The Software Essentialist 🎄 Ends in 1 days.
Well, that's it!
After 2 and a half years, The Software Essentialist, truly the last programming course you'll need, has moved from the Early Adopter Phase to fully complete.
The Software Essentialist is a self-paced transformational course designed to take you from a junior developer to a senior developer in 12 weeks. It has everything you need to get there, including the most important parts of TDD, BDD, DDD, and the various other major topics that you will need to use for the rest of your career, despite the language or framework.
Students have landed jobs, gotten promotions, and even started their own companies 🚀.
If you want to join them, now is the time.
Before July 12th, you can join the course for 40% off. After that, we'll be closing the doors and switching to application only.
Join here before July 12th.
And always, To Mastery!
When to Use a Private Constructor | TypeScript OOP

One of the first things we learn when we start out is how to create instances of objects. Typically, we do this with the new
keyword.
class User {
public name: string;
constructor (name: string) {
this.name = name;
}
}
const user: User = new User('Khalil Stemmler');
The actual thing that does the creating is the constructor- and by default, it's public.
Have you ever seen a private
constructor? Changing the scope of a constructor to private
removes our ability to use the new
keyword.
class User {
public name: string;
private constructor (name: string) {
this.name = name;
}
}
const user: User = new User('Khalil Stemmler'); // Error
Why on Earth would you want to do that? We can't create instances anymore. How are we supposed to get Users
out of this class
now?
All is not lost, and it turns out that there is a very good reason for why you'd want to do this kind of thing.
In essence, it's to enforce object creation rules.
Using the new keyword
When we use the new
keyword, there's not really an elegant way to prevent a User
from being created if certain validation rules don't pass.
We can throw errors.
class User {
public name: string;
constructor (name: string) {
if (!!name === false) {
throw new Error ("Ya need to include a name")
}
this.name = name;
}
}
let user;
try {
user = new User();
} catch (err) {
}
console.log(user); // undefined
But honestly, who wants to live in a world where we can't trust that a simple new
statement won't throw errors. I don't feel like adopting trust issues with my codebase.
We've talked about why throwing errors is not great in the "Functional Error Handling" article, so let's think of a better way to prevent bad objects from being created.
Static factory methods
The best way to enforce validation logic against a domain object is to keep the constructor private
and use a static factory method to enforce the constraints.
Using the Result<T>
class from "Flexible Error Handling w/ the Result Class | Enterprise Node.js + TypeScript", we can statically represent a success or a failure.
interface UserProps {
name: string;
}
class User {
private props: UserProps;
get name (): string {
return this.props.name;
}
private constructor (props: UserProps) {
this.props = props;
}
public static create (props: UserProps): Result<User> {
const guardResult = Guard.againstNullOrUndefined(props.name, 'name');
const isAppropriateLength = TextUtils.isAtLeast(2, props.name)
&& TextUtils.isAtMost(31, props.name);
// Fail with reason
if (!guardResult.success) {
return Result.fail<User>(guardResult.message)
}
// Fail with reason
if (!isAppropriateLength) {
return Result.fail<User>("Must be between 2 and 31 characters")
}
// Static method can access the constructor
return Result.ok<User>(new User(props));
}
}
Now, object creation looks like this:
let user: User;
let userOrError: Result<User> = User.create({ name: 'Khalil Stemmler' });
if (userOrError.isSuccess) {
user = userOrError.getValue();
} else {
console.log(userOrError.error)
}
Feel free to get even more functional with these different types of errors as well. We can statically type a NullValue
error and an InvalidLength
error.
Using the Either<T, U>
monad from "Functional Error Handling with Express.js and DDD | Enterprise Node.js + TypeScript", we can build return types like:
type UserResult = Either<
// Failure types
UserErrors.NullValuesError |
UserErrors.InvalidFieldLengthError,
// Success type
User
>
Last chance @ The Software Essentialist 🎄 Ends in 1 days.
Well, that's it!
After 2 and a half years, The Software Essentialist, truly the last programming course you'll need, has moved from the Early Adopter Phase to fully complete.
The Software Essentialist is a self-paced transformational course designed to take you from a junior developer to a senior developer in 12 weeks. It has everything you need to get there, including the most important parts of TDD, BDD, DDD, and the various other major topics that you will need to use for the rest of your career, despite the language or framework.
Students have landed jobs, gotten promotions, and even started their own companies 🚀.
If you want to join them, now is the time.
Before July 12th, you can join the course for 40% off. After that, we'll be closing the doors and switching to application only.
Join here before July 12th.
And always, To Mastery!
Stay in touch!
Join 20000+ value-creating Software Essentialists getting actionable advice on how to master what matters each week. 🖖
View more in TypeScript