skip to Main Content

I’m making an API call that 99% of the time has this value defined: awayTeam.record[0].displayValue

The one percent of the time, however, record is not defined or some other nested object is not defined. Is there a clean one line solution to check wether record exists?

I have tried things like this:

<h1> {awayTeam.record[0]?.displayValue || "0-0"} </h1>

and

<h1> {awayTeam.record[0].displayValue ?? "0-0"} </h1>

I have quite a lot of properties like this, so I would like to find a clean solution for this issue. Thanks!

4

Answers


  1. Your solution seems fine and "clean".

    If you want to prevent the usage of the optional chaining (‘?’),
    You can declare a function that returns a boolean indicating whether a property exists

    /**
     * Checks if a nested property exists within an object.
     * 
     * @param {Object} obj - The object to check.
     * @param {...string|number} args - Path to the nested property (as a series of arguments).
     * @returns {boolean} Returns true if the property exists, false otherwise.
     */
    const checkNestedProperty = (obj, ...args) => {
        return args.reduce((current, key) => (current && current[key] !== undefined) ? current[key] : undefined, obj) !== undefined;
    }
    

    Using it as the following:

    function TeamComponent({ awayTeam }) {
        const hasDisplayValue = checkNestedProperty(awayTeam, 'record', 0, 'displayValue');
    
        return (
            <div>
                {hasDisplayValue ? (
                    <p>Record: {awayTeam.record[0].displayValue}</p>
                ) : (
                    <p>Record not available</p>
                )}
            </div>
        );
    }
    
    Login or Signup to reply.
  2. You can define a small function like

    function safe(fn, alt) {
        try {
            return fn()
        } catch (e) {
            return alt
        }
    }
    

    and use it like

    <h1>{safe(_ => awayTeam.record[0].displayValue, "0-0")}</h1>
    

    This way you don’t have to think where to put these question marks.

    Login or Signup to reply.
  3. The one percent of the time, however, record is not defined or some other nested object is not defined.

    Read Below Code:

    const arr = [{displayValue:"Test 1"},{displayValue:"Test 2"},{displayValue:""},{displayValue:null}];
    
    const awayTeam = {record:arr};
    
    console.log(awayTeam?.record);
    console.log(awayTeam?.record?.[0]?.displayValue || "0-0");
    console.log(awayTeam?.record?.[1]?.displayValue || "0-0");
    console.log(awayTeam?.record?.[2]?.displayValue || "0-0");
    console.log(awayTeam?.record?.[3]?.displayValue || "0-0");
    
    /* || (Logical OR)  returns the right hand value if the left hand value evaluates to false and includes undefined and null but also 0 and ''.*/
    
    /* ?? (Nullish Coalescing Operator) returns the right hand value only if the left hand value is either null or undefined.*/
    Login or Signup to reply.
  4. @T.J. Crowder made a comment suggesting optional array chaining:

    awayTeam.record?.[0]?.displayValue 
    

    This is great, but not strictly type-safe in TypeScript because the nullability on array access is swallowed. There is a new array method at() which addressed this by returning T | undefined:

    const array = [1,2,3]
    
    const unchecked = array?.[0] // number
    const checked = array.at(0) // number | undefined
    

    You can use

    awayTeam.record.at(0)?.displayValue 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search