What methods can be used to display data using TypeScript's Optional Chaining feature?

I came across this Try it Yourself TypeScript Optional Chaining example in W3Schools TypeScript Null & Undefined section, and I have attached a screenshot for reference.

https://i.sstatic.net/s8q1J.png

The example demonstrates that when data is undefined, it displays No Yard. However, I am curious to understand why this happens and how one can make it display Yard size is 500 sqft.

As per my understanding, the code provided defines a variable called home within a new type (House interface) with a property named sqft having a value of 500.

let home: House = {   sqft: 500 }

The subsequent code introduces an interface called House, outlining property names along with their respective types. It specifies sqft as a number type and declares yard? as an optional property with a nested sqft property of number type.

interface House {
  sqft: number;
  yard?: {
    sqft: number;
  };
}

The following snippet is where confusion arises as to why yard size is undefined and displaying No yard.

function printYardSize(house: House) {
  const yardSize = house.yard?.sqft;

  if (yardSize === undefined) {
    console.log('No yard');
  } else {
    console.log(`Yard is ${yardSize} sqft`);
  }
}

From what I gather, the function leverages properties and types from the House interface, utilizing a constant variable yardSize to access the optional yard sqft value. Subsequently, it employs an if-else statement to output the result.

If anyone could provide me with a clear explanation on why it functions the way it does, both displaying No yard for undefined scenarios and Yard is 500 sqft for specified values, I would greatly appreciate the insight. Thank you!

Answer №1

It seems like there may be some confusion between the two sqft properties:

The one at the top level is linked to the House itself:

interface House {
  sqft: number; // This one
  yard?: {
    sqft: number;
  };
}

and can be accessed like this for a value of type House:

house.sqft
    //^? (property) House.sqft: number

You can see that the type is number. It must be defined for any value of type House.

Moreover, the house could have a yard property or not — in which case it will be undefined.

Refer to optional properties in the TS handbook for more details.

Example:

house.yard
    //^? (property) House.yard?: { sqft: number } | undefined

If the yard property is not undefined, then it must be an object with its own sqft property that must be a number:

interface House {
  sqft: number;
  yard?: {
    sqft: number; // This one
  };
}

Trying to access that nested property without using the optional chaining operator (?.) will result in a compile-time type error:

house.yard.sqft /* Error
~~~~~~~~~~
'house.yard' is possibly 'undefined'.(18048) */

However, using that operator allows for accessing the nested value (if the yard object exists), or evaluating to undefined (if the yard object doesn't exist):

house.yard?.sqft
          //^? (property) sqft: number | undefined

This evaluated value can be stored in another variable, similar to the function you presented:

const yardSize = house.yard?.sqft;
    //^? const yardSize: number | undefined

TS Playground


Now, let's address your queries:

Why is it undefined and shows no yard?

let home: House = {
  sqft: 500, // sqft of home
};

printYardSize(home); // "No yard"

In the provided code snippet, the home lacks a yard property with a nested sqft property, so in the function, the yardSize variable is set to undefined. As a result, the initial conditional branch is executed:

const yardSize = house.yard?.sqft;

if (yardSize === undefined) {
  console.log("No yard");
}

How do you get it to display Yard size as 500 sqft?

let home2: House = {
  sqft: 500,
  yard: {
    sqft: 500, // sqft of yard
  },
};

printYardSize(home2); // "Yard is 500 sqft"

As illustrated above, you can assign the yard property to an object with a sqft property value of 500. This triggers the execution of the else branch instead:

else {
  console.log(`Yard is ${yardSize} sqft`);
}

Alternatively, you could set the yard property on the first home after initializing it like this:

home.yard = { sqft: 500 };
printYardSize(home); // "Yard is 500 sqft"

TS Playground

Answer №2

My exploration of this question involved examining it from two different angles

  1. Utilizing Optional Chaining to potentially find the yard size
  2. or potentially finding both the house and yard sizes.

Below is the code for optionally determining the yard size

interface House {
  sqft: number;
  yard?: {
    sqft: number;
  };
}
            
function printYardSize(house: House) {
  const yardSize = house.yard?.sqft;

  if (yardSize === undefined) {
    console.log('No yard');
  } else {
    console.log(`Yard is ${yardSize} sqft`);
  }
}
            
let home: House = {
  sqft: 7350,
  yard: {             
         sqft: 2300
        }
};

// Can also set yard property on home after initializing it with
home.yard = { sqft: 2300 }
  

printYardSize(home); // Either prints No yard or Yard is 2300 sqft

If the yard size is excluded, it will display 'No Yard', but if included, it will show 'Yard size is 2300 sqft'. This showcases how the use of the ? operator in the declaration of the home variable allows for optional data without triggering errors.

Here is the code for optionally determining the house and yard sizes

// This example showcases a house and yard size being displayed 
interface House {
  sqft?: number,
  yard?: {
          sqft: number;
         };  
}

function printHouseSize(house:House)  {
  const houseSize = house?.sqft;    

  if(houseSize === undefined)  {
    console.log('No House');
  } else {
    console.log(`House is ${houseSize} sqft`)
  }
}

function printYardSize(house:House)  {
  const yardSize = house.yard?.sqft;  

  if(yardSize === undefined)  {
    console.log('No Yard');
  } else {
    console.log(`Yard is ${yardSize} sqft`)
  }
} 

let home: House = {
  sqft: 7350,            
  yard: {       
         sqft: 2300    
        }
};

printHouseSize(home);
printYardSize(home);

If the house size is omitted, it will say 'No House', and if included, it will state 'House size is 7350 sqft'. The same applies for the yard size. This demonstrates that through the use of the ? optional chaining operator, errors can be avoided when dealing with optional property values.

It's worth noting that the ? operator is used in interfaces where property types are declared and in functions when retrieving property values, as seen in const yardSize = house.yard?.sqft; .

Please keep in mind: The optional chaining operator is not required in the declared home variable, as it pertains to setting values rather than accessing properties.

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

Hiding a div after three clicks using HTML

What is the best way to hide a div tag containing an input tag after clicking on the input tag three times using HTML and angular? ...

Typescript Error: Issue encountered while passing props. Unable to access properties as they are undefined

I encountered an issue where I created an object of a certain type and attempted to pass it to a component. However, when passing the props, I received an error message stating that it cannot read properties of undefined ('stepOne'). The error sp ...

Exploring Angular 10's advanced forms: delving into three levels of nested form groups combined with

Project Link: Click here to view the project In my testForm, I have 3 levels of formGroup, with the last formGroup being an array of formGroups. I am trying to enable the price field when a checkbox is clicked. I am unsure how to access the price contro ...

utilizing routerLinks to transfer data and trigger a specific function

I'm having trouble passing data through the routerLink and calling the function(). It doesn't seem to be working as expected. Here's an example of my code and you can view a working demo on StackBlitz. First Component(HTML) <span [route ...

Issue TS2349 occurs when attempting to use a combination of boolean and function types within union typing

In my class, there is a property called "isVisible" which can be either a boolean value or a function that returns a boolean. The code snippet below demonstrates what I am currently using. It works fine and achieves the desired result, but during compilat ...

The functionality of CSS transitions may be affected when dynamically adding a class

Creating a custom CSS for my main div: .main { height: 0%; position: absolute; width: 100%; z-index: 100; background: whitesmoke; bottom: 0; border-radius: 15px; padding: 20px; color: gray; left: 0; right: 0; transition: height 1s e ...

When running the test, a "is not defined" ReferenceError occurs in the declared namespace (.d.ts) in ts-jest

When running typescript with ts-jest, the application functions properly in the browser but encounters a ReferenceError: R is not defined error during testing. The directory structure: |--app | |--job.ts |--api | |--R.d.ts |--tests | |--job.test.ts ...

You must add the module-alias/register to each file in order to use path aliases in

I am currently utilizing typescript v3.6.4 and have the following snippet in my tsconfig.json: "compilerOptions": { "moduleResolution": "node", "baseUrl": "./src", "paths": { "@config/*": ["config/*"], "@config": ["config"], ...

Issue encountered: Jest database test does not end when using testcontainers

I am currently in the process of testing my database within my Node application written in Typescript. I have implemented Postgres 15 and Testcontainers for this purpose. Strangely, my code functions correctly when executed manually, with all clients being ...

After upgrading to version 8 of the Firebase JS SDK, the "firestore" module (imported as "firebase") could not be located within the "firebase" package

After upgrading the firebase JS SDK from v7 to v8.0.0, I changed my import of firebase as shown below. import * as firebase from 'firebase'; However, attempting to access certain properties resulted in an error message: firebase.firestore.FieldV ...

I am unable to refresh the table data in Angular

Here is the code that I am currently working with: I am facing an issue where my webpage is not updating its data after performing delete or any other operations. The user list is not being displayed in the data. Despite my attempts, I have been unable to ...

The formatting in vscode does not apply to .tsx files

For a while now, I've relied on the Prettier extension in Visual Studio Code for formatting my code. However, since switching to writing React with Typescript, I now need to configure Prettier to format .tsx files accordingly. ...

Tips for accurately documenting the Props parameter in a React component with Destructuring assignment

Attempting to create JSDocs for a React component using destructuring assignment to access props: const PostComponent: React.FC<IPostComponent> = ({ title, body }) => { ... } The issue arises when trying to document multiple parameters in JSDocs ...

No search results found for Mongoose text search query

Despite using Mongoose 5.7.8 for my app, the text search feature is not yielding any results. Below is a schema/model example: import mongoose, { Document, Schema } from 'mongoose'; import { Moment } from 'moment'; import { IUser } fr ...

What steps can I take to set a strict boundary for displaying the address closer to the current location?

While the autocomplete feature works perfectly for me, I encountered an issue where it suggests directions away from my current location when I start typing. I came across another code snippet that uses plain JavaScript to solve this problem by setting bou ...

The model fails to update when a blur event occurs

I am a beginner with Angular2 and I am currently working on creating a reactive form, specifically an interactive date input field. Here is my HTML code: <div class="date ui-input"> <input type="text" name="dateD" [ngModel]="model.date | dat ...

Issue with DevExtreme nested table not expanding when sorting the parent table

Utilizing the DevExtreme Nested Data Grid (dx-data-grid) within an Angular app to exhibit hierarchical data is proving challenging for me. The data structure comprises a parent table, where each row can have child rows, which in turn can have grandchild ro ...

Receiving an eslint error while trying to integrate Stripe pricing table into a React web application

If you're looking to incorporate a Stripe documentation code snippet for adding a stripe-pricing-table element, here's what they suggest: import * as React from 'react'; // If you're using TypeScript, don't forget to include ...

Creating a grid UI in AngularJS using Typescript: utilizing functions as column values

I am working on an AngularJS app that includes the following UI grid: this.resultGrid = { enableRowSelection: true, enableRowHeaderSelection: false, enableHorizontalScrollbar: 0, enableSorting: true, columnDefs: [ { name: &apos ...

Tips for utilizing functions in an inline HTML translation pipe

My objective is to streamline the code by using the Angular translate pipe. Currently, I find myself using 8 curly brackets and repeating the word "translate" twice... there must be a more efficient approach. Here is my current code setup: <s ...