Erase null off the face of the Earth
See original GitHub issueš feature request
Relevant Package
This feature request is for @angular/core
and @angular/forms
, and maybe more.
Description
- What is widely regarded as a big mistake in programming languages?
null
is. - Whatās worse? Two forms of
null
with different types and different behavior (null
andundefined
). - Whatās bad for optimization? Type coercion.
null
/nil
/nullptr
/etc. have already caused plenty of harm across many programming languages. The fact that JavaScript has both null
and undefined
just makes things much much worse for developers.
I understand both Angular devs and many Angular users are coming from Java and lots of people want an extremely semantic type system which distinguishes between cases like āit was never given a valueā (undefined
) and āit was explicitly given no valueā (null
). I have written many, many thousands of lines of TypeScript code over my 4 years as an Angular developer and I must say that the distinction between the two is rarely important. A hallmark of good code is simplicity (good for the reader and good for the computer), and one way to obtain simple code is to write it for the common case and only get verbose for the rare cases. The onlyāand I mean onlyātime I have ever needed to use null
in JavaScript/TypeScript was with a CRUD API where the PATCH body could contain a null
expiration to clear the expiration date for an item (JSON does not have undefined
and so undefined
values get omitted by default when encoding). Additionally, if I were the API designer, I would have simply offered a query parameter in the URL to clear the expiration.
My point is that 99% of the time, null
and undefined
are used interchangeably and the end user sees the same thing regardless of which one made its way through the code. You can be type-religious all you want but I think most of us can agree that making rare distinctions with more verbose code beats using ānullishā coercions all over the place (and crashing when you forget to).
That still leaves the question: which is the winner, null
or undefined
? There is an excellent article comparing them which you should definitely go read, but, in short, undefined
is better supported by the language.
undefined
is the initial value for unassigned variables, just asnull
is in Java.- The above also applies to object properties, and even if you need to distinguish between implicit/explicit absence of a value, you can use the
in
operator or thehasOwnProperty
method (doesnāt check inherited properties). null
does not work with parameter defaults or destructuring defaults.- Saner
typeof
check:typeof undefined
yields"undefined"
whereastypeof null
yields"object"
but it still does not behave as a regular object at all.
I only use undefined
in my code and it works out very well since, as I explained, it has lots of language support. I almost never need null
checks (only for old APIs like local storage), but opting to use null
religiously does not get anyone out of undefined
checks.
Unfortunately, it seems like the Angular team disagrees, and now I have to write lots of special checks for FormControl
values (Angular reactive forms), as well as for optional injection (the NullInjector
).
class MyModalComponent {
constructor(
@Inject(MAT_DIALOG_DATA)
@Optional()
private readonly data: {
readonly project?: ProjectInfo;
readonly parentID?: string;
} | null
) {
// Could've just been a parameter default :/
if (data === null) {
this.data = data = {};
}
}
}
Because the application I work on has a lot of data types, it also has a lot of forms. I have been porting the application from AngularJS to Angular for months now, and both Angular material and reactive forms have been a big letdown. I do not mean to be rudeālots of the new Angular features are fantastic and the performance difference with AOT compilation is massiveābut writing forms has proven incredibly tedious and frustrating, mostly due to bad APIs (like the new version of ng-messages
). Having to coerce null
to undefined
everywhere when making API requests is just the cherry on top.
Describe the solution youād like
Well, breaking existing project compatibility is bad. Then again, backwards-compatibility as the number one priority is also responsible for all of JavaScript and C++'s historical baggage. Rust bit the bullet and dropped green threads and garbage collection halfway through and became truly innovative as a result (a modern replacement for C/C++).
That said, changing the behavior of optional injection is probably not the move. š
Maybe provide an @Opt()
decorator which yields undefined
if the injection token is not found and deprecate @Optional()
?
Forms have proven much more of a nuisance than the optional injection null
checks, and reactive formsā usage of null
is just the tip of the iceberg there.
I suppose Iām mostly just trying to start a conversation with the hopes that a future forms API benefits from it. I will also do my part and open source any reactive forms replacement I end up writing (which I certainly will!).
Describe alternatives youāve considered
Writing lots of ānullishā coercion code.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:7
- Comments:5 (2 by maintainers)
I, and other folks on the team, definitely agree that the codebase should be much more consistent with
null
andundefined
. The reality, though, is that it would be a really large undertaking to go do a comprehensive cleanup of this for the entire framework. Instead, we would want to focus on specific area, e.g. #25395 for the typing of@Optional
and #13721 for forms. Weāll realistically be able to prioritize those kinds of issues sooner than a comprehensive code cleanup like this issue suggests. In that vein, Iām going to close this issue since it wouldnāt be practically actionable and suggest opening more specific issues for different parts of the codebase that arenāt already tracked.Iām also going to try to incorporate guidance on
null
vs.undefined
into our coding standards; Iāve added an item to our next team meeting to discuss this.Awesome! Thatās all I couldāve hoped for.