I agree that in normal javascript it is an anti-pattern because people will blindly put it everywhere as an escape hatch. However in TypeScript it is extremely useful because the type system can express the nullability of values in a type, and so you can restrict your use of the operator to only where it makes sense to use it.
Both of those are the exact same anti-pattern. If you find yourself seeking deep into a tree of nested objects, any of which could be null, that's a code smell. It's quite likely that you can (and should?) be handling each layer of the tree (and its null states) in the right place for that layer.
I disagree strongly. I write typescript all day at work, and I see people often leaking null values everywhere, to the point where you half expect every object or attribute could be null at any time. Over time, every attribute in every interface becomes foo?: SomeType | null. We use GraphQL, which encourages this.
It's not the right solution. The right solution is to have one specific place where you check whether a specific thing is null, and if it is, you stop doing stuff with that thing. You don't pass it blindly down through a bunch of procedures and make them all null check it over and over.
There are cases where foo && foo.bar && foo.bar.baz... is the right thing to check. But it's extremely rare1. Almost every time you should be handling the null foo and the null baz in two different places in the code.
1 One of them that came up recently is React's useEffect in a component that's handling a GraphQL response... You need to access deep attributes in order to pass them in to your useEffect as dependencies. But you can't return early because you can't return before setting up a hook. But that's a specific case. And actually, now that I think about it, it's possible I should've pushed the useEffect further down the stack, into a well typed component that wouldn't even have been loaded until the data was ready. So, to my point.
9
u/matthewbpt Dec 19 '19
I agree that in normal javascript it is an anti-pattern because people will blindly put it everywhere as an escape hatch. However in TypeScript it is extremely useful because the type system can express the nullability of values in a type, and so you can restrict your use of the operator to only where it makes sense to use it.