strictNullChecks and the dissemination of null values

Considering implementing the strictNullChecks flag in a large code base presents some challenges. While it is undeniably a valuable tool, the abundance of null types in interface definitions may be impacting the code's readability. This is particularly noticeable when a function returns <type> | null and the null type permeates through the call stack, necessitating null types in interfaces.

Optional nullable fields, such as:

interface X {
  field?: string | null
}

This can become cumbersome, especially when both null and undefined are considered falsy values.

One solution could be to replace null types with undefined, like: <type> | undefined, to eliminate null types from optional interface fields, resulting in:

interface X {
  field?: string
}

However, this approach sacrifices the semantic value of null, which may be important to retain.

What are your thoughts on strictNullChecks? Do you find fields like field?: string | null awkward, or do you embrace and feel comfortable with them?

Answer №1

In my opinion, it would be more beneficial to completely eliminate the use of null and opt for undefined instead.

If there isn't a significant difference between a null and undefined value in your application, it could lead to confusion (both in TypeScript and plain JavaScript). If you are just randomly returning null or undefined, it would be more straightforward and consistent to stick with only one.

My recommendation is to use undefined primarily because TypeScript provides slightly better support for it (such as ?). While there are arguments for using only null, it tends to be more verbose, and having undefined as the indicator for missing data is often more convenient since it serves as the default value for uninitialized variables/fields. These should typically have the same semantics and behavior as explicitly unavailable data.

Answer №2

My perspective aligns with Tim Perry's answer regarding the use of either null or undefined, and I recommend sticking with undefined. This is mainly because it is inevitable due to its significance as the value for missing fields and uninitialized variables.

In addition, null poses limitations for default function parameters:

function f(x = 1) {
  // ...
}

In this scenario, f(undefined) will assign x a value of 1, while f(null) will not achieve the same result!

The argument in favor of null rests on JSON not supporting explicit undefined. However, this can be easily addressed when deserializing JSON data that necessitates specific null values instead of simply missing values (effectively denoting undefined).

In my projects, I take the approach of disallowing nulls through a linter configuration, such as in TSLint:

    "no-any": true,
    "no-null-keyword": true

Utilizing strictNullChecks effectively helps in preventing many types of errors.

It's worth noting that the individual responsible for creating NULL has admitted to it being his billion-dollar mistake. Having two null-like values could exacerbate the issue. However, opting for just one (like undefined) and implementing strict null checks in TypeScript can significantly mitigate the problems typically associated with null/undefined in most programming languages.

Regardless of whether you choose undefined, null, or both, I strongly recommend utilizing strictNullChecks, as it enhances the type safety of your code.

Reference the TypeScript 2.0 release notes to learn more about null- and undefined-aware types:

Prior to this update, null and undefined were considered assignable to any type, meaning they were valid values for every type, making it challenging to exclude them specifically (thereby preventing the detection of erroneous use). [emphasis added]

By default, this feature is not enabled to maintain compatibility with legacy code written before the updated null and undefined checking mechanism.

Consider this straightforward function:

const f = (x: string) => x.toLowerCase();

Without the strict flags, it is not type safe and is prone to crashing, leading to runtime TypeError exceptions like:

TypeError: Cannot read property 'toLowerCase' of undefined

With the strict flags:

const f = (x: string | null) => x.toLowerCase(); // COMPILE TIME ERROR
const f = (x: string | null) => x && x.toLowerCase(); // OK and safe

For further details, refer to:

Update: Soon, you will have the option to use optional chaining in the aforementioned example:

const f = (x: string | null) => x?.toLowerCase(); // OK and safe

Moreover, if x may not be null but could be a type devoid of the .toLowerCase() method:

const f = (x: string | number | null) => x?.toLowerCase?.(); // safe as well

To delve deeper into this topic, explore the documentation on the optional chaining operator.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Setting up ESLint and Prettier with TypeScript on Node 20: A Guide

I attempted to set up Prettier with ESLint and crafted a configuration in settings.json to rectify errors upon saving, but the errors only manifest in .js files and not .ts files. How can I adjust this? Dependencies: "@eslint/js": "^9.4.0& ...

Error TS2339 occurs when attempting to migrate to TypeScript due to the absence of the 'PropTypes' property on the 'React' type

Currently in the process of converting a javascript/react project to a typescript/react/redux project. Encountering an issue with this particular file: import React from 'react'; import GoldenLayout from 'golden-layout'; import {Provi ...

Setting up data in Firebase can be challenging due to its complex structure definition

https://i.stack.imgur.com/iclx7.png UPDATE: In my firebase structure, I have made edits to the users collection by adding a special list called ListaFavorite. This list will contain all the favorite items that users add during their session. The issue I a ...

Is it necessary to 'type assert' the retrieved data in Axios if I have already specified the return type in the function declaration?

Consider the code snippet below: import axios from 'axios' async function fetchAPI<T>(path: string, data: any): Promise<T> { return (await axios.get(path, data)).data as T } async function getSomething(): Promise<SomeType> { ...

The process of extracting all arrays from a JSON object

Within my JSON object, there is a list of countries each with multiple regions stored in an array. My goal is to extract and combine all the regions into one single list. However, when I attempt to map the data, it does not consolidate all the regions as e ...

Utilizing ag-grid with Vue.js: Implementing TypeScript to access parent grid methods within a renderer

I've integrated ag-grid into my project and added a custom cell renderer: https://www.ag-grid.com/javascript-grid-cell-rendering-components/#example-rendering-using-vuejs-components Although the renderer is working well, I'm facing an issue whe ...

Unable to incorporate .tsx files into a Node.js Web Application project

After creating a new Node.js Web Application in Visual Studio 2015, I encountered an issue with adding and compiling .tsx files to the project. Instead of being added to the actual project, the .tsx file was placed into a Virtual Project. The project is co ...

The issue arises when interfaces are extended by another interface

Is there a way to have classes that implement both the Observer and Comparable interfaces together? interface Comparable<T> { equals: (item: T) => boolean; } interface Observer extends Comparable<Observer> { notify: () => void } ...

How to conceal duplicate items in Angular2

In my Angular 2/4 application, I am fetching data from a web API (JSON) and displaying it in a table. In AngularJS, I use the following code: <tbody ng-repeat="data in ProductData | filter:search | isAreaGroup:selectedArea"> <tr style="backgro ...

Nest may struggle with resolving dependencies at times, but rest assured they are indeed present

I've encountered a strange issue. Nest is flagging a missing dependency in a service, but only when that service is Injected by multiple other services. cleaning.module.ts @Module({ imports: [ //Just a few repos ], providers: [ ServicesService, ...

Error: XYZ has already been declared in a higher scope in Typescript setInterval

I've come across an interesting issue where I'm creating a handler function and trying to set the current ref to the state's value plus 1: const useTimer = () => { const [seconds, setSeconds] = useState(0); const counterRef = useRef(n ...

Exploring Angular's Subcomponents

While working on my application, I encountered a dilemma. I currently pass data from the main component to the subcomponents after pulling it from the API with a single request. Should I continue with this approach or have each component make its own separ ...

Give the Row ID as a parameter to a personalized component within MUI Datagrid Pro

I am currently exploring the idea of incorporating an intermediate state to row checkboxes based on the selection status of other checkboxes within a detailed panel. My approach involves crafting a custom checkbox component and implementing some logical ...

Developing a project using npm and Node.js with Typescript has been smooth sailing so far. However, an issue has arisen

Recently, I came across a helpful guide on setting up a Node.js project in Typescript on this website: https://basarat.gitbooks.io/typescript/docs/quick/nodejs.html The guide also provides instructions for auto-recompilation upon changes when using npm st ...

Angular 4's OrderBy Directive for Sorting Results

I've been working on implementing a sorting pipe based on the code provided in this resource: The issue I'm facing revolves around handling undefined values within my data. The sorting pipe functions correctly when there are no undefined values ...

Tips for streamlining the use of http.get() with or without parameters

retrievePosts(userId?: string): Observable<any> { const params = userId ? new HttpParams().set('userId', userId.toString()) : null; return this.http.get(ApiUrl + ApiPath, { params }); } I am attempting to streamline the two http.get ca ...

Unable to employ the .some() method with an array consisting of objects

I am currently attempting to determine whether my array of objects contains an attribute with a specific value. I wanted to use the array.some() method instead of a foreach loop, but I keep encountering an error: Error TS2345: Argument of type '(ele ...

Obtain the total number of requests submitted by the user within a 24-hour period

I have a POST request point in my API where I need to track and store all work journals made by a worker. export const registerPoint = async (req: Request, res: Response) => { const user = res.locals.decoded; const {id} = user; const point = new Point ...

Why is my React component not being updated with Routes?

I'm new to using react-router and I'm struggling with it for the first time. Here is the code snippet: App.tsx import React from 'react'; logo = require('./logo.svg'); const { BrowserRouter as Router, Link, Route } = require ...

Accessing results from geocoder.geocode is restricted to local variables only

I need to extract longitude and latitude coordinates from google.maps.GeocodeResults in order to store them in an external Array<any>. Currently, I am able to display results[0], but encounter an OVER_QUERY_LIMIT error when attempting to add it to t ...