We're going to take a look at TypeScript's
strictPropertyInitialization compiler option and how it helps us prevent using uninitialized class instance properties. If the
strictPropertyInitialization flag is enabled, the type checker verifies that each instance property declared in a class either
strictPropertyInitialization option is part of the family of compiler options that is enabled automatically when the
strict flag is set. As with all the other strict compiler options, you can set
strict to true and selectively opt out of strict property initialization checks by setting
Instructor: [0:00] In this lesson I want to talk about strictPropertyInitialization in typescript. I've written the simple userclass which defines a username property. Let's go ahead and let's create an instance of the userclass and let's also set a username.
[0:18] Now that that's done, let's read the username. Let's make sure it's lowercased and print it to the console. Let's quickly go ahead and run this. That is looking fine. Now, see what happens if we don't call the set username method.
[0:45] If I go ahead and run the program again, we can see that we get a type error. This is because we're trying to call the two lowercase method on the value undefined. How come we didn't get a type error here?
[0:58] Let's head over to our TS config file and have a look at our compiler configuration. We can see that we have enabled the strictNullChecks compiler option. We would have expected typescript to give us a type error here, right?
[1:11] It turns out we need to enable another compiler option to get a type error here. That option is called strictPropertyInitialization and we want to set that to true.
[1:20] Whenever we enable the strictPropertyInitialization option, we also need to enable the strictNullChecks option. The typescript compiler has a bunch of options related to stricter type checking.
[1:32] Whenever possible I would encourage you to enable all of them in your project. The easiest way to do that is to set the strict option to true. That will enable a bunch of compiler options related to stricter type checking including strictPropertyInitialization.
[1:48] If we head back to our index.TS, you can see that we now get a type error. Typescript is telling us that the property username has no initializer and that it is not definitely assigned in the constructor.
[2:00] We're essentially working with an uninitialized property which is why we get the value undefined. One way to work around this type error is to add the type undefined to our property declaration.
[2:13] We've solved the type error in line two, but now we get another type error in line 12. This is because we're working with a possibly undefined value. This is the behaviour that we want. Typescript is warning us that this operation might fail at run time.
[2:28] We can safely work with a username property by using optional chaining. We can even provide a fallback value using the nullish coalescing operator.
[2:37] If we now go ahead and run our program again, we can see that we get the fallback value when the username is missing.
[2:43] If we set a username and we run this again, we can see our username. Another approach we could take would be to initialize our username property with our fallback value. That way we always have a string value even if we don't call said username.
[3:02] This means that we can remove the undefined type from our property. In fact, we can remove the type annotation altogether because typescript can infer from the initial value that this must be a string property.
[3:14] We can also remove our null handing in line 12 because we no longer have to cater four undefined values. Now this approach works, but I feel like it's not the most idiomatic one.
[3:26] If username is a mandatory property of our userclass, we should accept the username parameter in the constructor. Go ahead and add a constructor here. It's going to accept the username as a parameter.
[3:39] Within the constructor, we're going to assign our username. We can also remove the initializer here and we can say that this is a string property. We're getting a type error in line 13 and this is because we're not providing an argument for the username parameter.
[3:55] Let's parse the username to the constructor. Typescript can figure out that after the constructor has run, the username property has definitely been assigned.
[4:08] However, if there is any path in the constructor that doesn't assign the username property, typescript gives us the type error again. It's telling us that the username property has not definitely been assigned in the constructor.
[4:21] For this analysis to work, you have to assign the properties directly within the constructor. Typescript does not track property assignments across method calls, because that could get arbitrarily complex and expensive.
[4:34] If we were to call the set username method instead of directly assigning the property, we would be back with our type error because typescript analysis cannot detect that we have definitely assigned the username property.
[4:46] There is a way to tell typescript that we will definitely assign this property even if typescript cannot detect that itself. That is to use what's called a definite assignment assertion. It's written as an exclamation mark after the property name.
[5:01] Typescript will now trust us that we will initialize the username property in all cases. Definite assignment assertions can be useful if you're initializing some of your properties in an initialize method. For example, we could have a method called initialize.
[5:16] Perhaps even a private method that will initialize our properties. In this case, the username property. That said, I want to caution you a little bit against definite assignment assertions.
[5:28] If we don't assign a property in our initialize method and we execute our program again, we will run into the same error we've had before. Typescript did not warn us about it. Typescript trusted us when we said that we were going to initialize all of our properties.
[5:46] The safest way to work with properties is to initialize all of them in the constructor. That's what I would recommend. If you initialize all of your properties in the constructor, you don't need a definite assignment assertion and typescript can give you more type safety.