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.
More Tips
Type Predicates
1 min
TypeScript 5.1 Beta is OUT!
2 mins
How to Name your Types
4 mins
Don't use return types, unless...
4 mins
TypeScript 5.0 Beta Deep Dive
6 mins
Conform a Derived Type Without Losing Its Literal Values
1 min
Avoid unexpected behavior of React’s useState
1 min
Understand assignability in TypeScript
2 mins
Compare function overloads and generics
1 min
Use infer in combination with string literals to manipulate keys of objects
1 min
Access deeper parts of objects and arrays
1 min
Ensure that all call sites must be given value
1 min
Understand how TypeScript infers literal types
1 min
Get a TypeScript package ready for release to NPM in under 2 minutes
1 min
Assign local variables to default generic slots to dry up your code and improve performance
2 mins
Know when to use generics
2 mins
Map over a union type
1 min
Make accessing objects safer by enabling 'noUncheckedIndexedAccess' in tsconfig
1 min
Use generics to dynamically specify the number, and type, of arguments to functions
1 min
Use 'declare global' to allow types to cross module boundaries
2 mins
Turn a module into a type
2 mins
Create autocomplete helper which allows for arbitrary values
2 mins
Use deep partials to help with mocking an entity
1 min
Throw detailed error messages for type checks
1 min
Create a 'key remover' function which can process any generic object
1 min
Use generics in React to make dynamic and flexible components
1 min
Create your own 'objectKeys' function using generics and the 'keyof' operator
1 min
Write your own 'PropsFrom' helper to extract props from any React component
1 min
Use 'extends' keyword to narrow the value of a generic
1 min
Use function overloads and generics to type a compose function
2 mins
Decode URL search params at the type level with ts-toolbelt
2 mins
Use 'in' operator to transform a union to another union
2 mins
Derive a union type from an object
2 mins