Use assertion functions inside classes

Assertion functions are pretty useful when they're applied to classes, and there's some cool stuff you can do. Here we've got an SDK


export class SDK {
constructor(public loggedInUserId?: string) {}
createPost(title: string) {
this.assertUserIsLoggedIn()
createPost(this.loggedInUserId, title)
}
assertUserIsLoggedIn() {
if (this.loggedInUserId) {
throw new Error("User is not logged in")
}
}
}

It's kind of like if you imagine the back ends at a center point where you invoke class and you've got all your methods and stuff.

And here we've got a loggedInUserId and this can either be string or it can be undefined. When it's undefined it means the user isn't logged in and so there might be still anonymous things they can do.

But inside createPost we want to assert that the user is logged in and there we're going to use. the user ID. But the issue here is that we've got string or undefined in for the first argument of the call to createPost which is not good because actually we need string and really we've asserted that the logged in user ID exists.

So we should just be able to get this going. But we haven't told TypeScript that this.loggedInUserId is defined. If we were to move the if statement in our assertion to be inside createPost here then it would work.


export class SDK {
constructor(public loggedInUserId?: string) {}
createPost(title: string) {
this.assertUserIsLoggedIn()
if (this.loggedInUserId) {
throw new Error("User is not logged in")
}
createPost(this.loggedInUserId, title)
}
assertUserIsLoggedIn() {}
}

But how do we get this. kind of inference working? Well we can return asserts this is this &. Which means we are saying assertUserIsLoggedIn is an assertion function now and we're saying that this is this and something else and we can add loggedInUserId: string


assertUserIsLoggedIn(): asserts this is this & {
loggedInUserId: string
} {
if (this.loggedInUserId) {
throw new Error("User is not logged in")
}
}

What this does is it basically just adds an intersection to the class. and matches them both together and loggedInUserId: string wins. So we get the string inside the SDK and now this.loggedInUserId is typed string.

We could probably do even something more in here. We could say wow which is a Boolean. And then use this.wow in createPost


export class SDK {
constructor(public loggedInUserId?: string) {}
createPost(title: string) {
this.assertUserIsLoggedIn()
createPost(this.loggedInUserId, title)
this.wow
}
assertUserIsLoggedIn(): asserts this is this & {
loggedInUserId: string
wow: boolean
} {
if (this.loggedInUserId) {
throw new Error("User is not logged in")
}
}
}

So assertion functions are pretty useful. We do need to make sure that you check everything there. Like this.wow Boolean is technically unsafe.

But, it does give you a lot of flexibility and it means you can use it in a lot more of a reusable way.

Transcript

0:00 Assertion functions are pretty useful when they're applied to classes, and there's some cool stuff you can do. Here, we've got an SDK, which is like, if you imagine, the back end centerpoint, where you invoke this, and you've got your methods and stuff.

0:14 Here, we've got a logged-in user ID, and this can either be string, or it can be undefined. When it's undefined, it means the user isn't logged in, and so there might be still anonymous things they can do. Inside createPost, we want to assert that the user is logged in.

0:28 There, we're going to use the user ID. The issue here is that we've got string or undefined in this position, which is not good, because actually, we need string. Really, we've asserted that the logged-in user ID exists, so we should just be able to get this going, but we haven't told TypeScript that this is defined.

0:48 If we were move this stuff inside here, then it would work, but how do we get this inference working? Well, we can return assert this is this, and -- so we're saying this is an assertion function now, and we're saying that this is this and something else, and -- we're going to add logged-in user ID, string.

1:13 What this does is it basically just adds an intersection into the class and matches them both together, and this one wins. We get the string inside the SDK, and now this is typed a string. We could probably do even something more in here. We could say wow, which is a pretty Boolean, and I'm pretty sure we'll be able to go this.wow, which is a Boolean.

1:32 Assertion functions are pretty useful. You do need to make sure that you check everything there, like this wow Boolean is technically unsafe, but it does give you a lot of flexibility, and it means you can use it a lot more way of a reusable way.

You can do some really, really neat stuff with assertion functions inside classes.

Here, we assert that the user is logged in and get proper inference on the user's logged in user id.

Discuss on Twitter

More Tips

Assign local variables to default generic slots to dry up your code and improve performance