Start learning free

LESSON 130 OF 675 · DICTIONARIES

Reading a Key That May Be Missing in TypeScript

Lesson Read the full explanation

TL ; DR:

Start with the default. Then an if with in replaces it with the value, when the key is there.


const points: { [key: string]: number } = {"Ana": 7};

let score: number = 0;
if ("Ben" in points) {
    score = points["Ben"];
}
console.log(score);   // 0


Full lesson:

A missing key often means "none yet". A player with no points yet has 0 points.

points["Ben"] doesn't stop the program. It gives undefined, not 0.

Then points["Ben"] + 1 is NaN, "not a number".

So start score at the default. The if changes it only when "Ben" is a key.

The type says number. Still, a missing key gives undefined when the program runs.

Reading this way doesn't add the key. points still has one key afterwards.

With "Ana", the same code gives 7.


Your task

stock counts the fruit in a shop. The code reads and prints the pears.

There are no plums yet. Declare plums at 0. Then use an if with in to read stock["plums"] when the key is there.

Then log the plums line, the same way as the pears line.

Expected output:

We have 2 pears
We have 0 plums
STEP 1 OF 3
    Ctrl + Enter to check

    TYPESCRIPT · DICTIONARIES

    Dictionary Default Value in TypeScript, explained

    The problem with a missing key

    A missing key often means "none yet": a shop with no plums has 0 plums. But a plain read does not give 0. It gives undefined, and doing arithmetic with it gives NaN, "not a number":

    const stock: { [key: string]: number } = {"apples": 5, "pears": 2};
    console.log(stock["plums"], stock["plums"] + 1);
    undefined NaN

    The type of stock says every value is a number, but types are erased when the code is compiled, so a missing key still reads as undefined when the program runs. The compiler does not warn you.

    Start with the default, then override

    The lesson's pattern has two parts. First set a variable to the default. Then use an if with in to replace it with the real value, but only when the key is there:

    let plums: number = 0;
    if ("plums" in stock) {
        plums = stock["plums"];
    }

    If "plums" is a key, plums takes its value. If not, it keeps the 0. Reading this way never adds the key: Object.keys(stock).length is still 2 afterwards. With "apples" in place of "plums", the same code gives 5.

    The exercise, step by step

    The starter code reads and prints the pears. The task is to print the plums the same way, even though stock has none.

    1. Declare the default. let plums: number = 0;
    2. Override when the key exists. An if with "plums" in stock and, inside, plums = stock["plums"].
    3. Print. Copy the pears line with plums in place of pears.
    const stock: { [key: string]: number } = {"apples": 5, "pears": 2};
    
    let pears: number = stock["pears"];
    console.log("We have " + pears + " pears");
    
    let plums: number = 0;
    if ("plums" in stock) {
        plums = stock["plums"];
    }
    console.log("We have " + plums + " plums");
    We have 2 pears
    We have 0 plums

    Where this pattern leads

    Reading with a default is the first half of updating a count. The second half is to store the result back. Here is a variable fruit holding a name that is not in stock yet:

    const fruit = "plums";
    let have: number = 0;
    if (fruit in stock) { have = stock[fruit]; }
    stock[fruit] = have + 1;
    console.log(stock);
    { apples: 5, pears: 2, plums: 1 }

    The first time, the key is missing, so have stays 0 and the entry is created with 1. Run it again and the key exists, so the count goes to 2. Reading never added the key; the assignment on the last line did.

    Another way: ??

    The nullish coalescing operator ?? gives its right side when the left side is undefined or null. It can replace the whole pattern in one line:

    console.log(stock["plums"] ?? 0, stock["apples"] ?? 0);
    0 5

    Do not use || for this. It replaces every falsy value, including a real 0. With {"plums": 0}, z["plums"] || 99 gives 99, while z["plums"] ?? 99 correctly gives 0. The lesson uses in so that the check is visible and explicit.

    Traps

    • Skipping the check. let plums: number = stock["plums"]; compiles and then holds undefined, so the message prints We have undefined plums.
    • Declaring the default after the if. It would overwrite the value read from the dictionary. The default goes first.
    • Expecting the read to add the key. Reading never changes the object; only an assignment does.

    Choose the default to match the meaning of a missing key. For counts it is 0, for text it might be an empty string, and for a list it might be an empty array. The type annotation on the variable, such as number, documents which default fits.

    Key takeaways

    • A missing key reads as undefined, not 0.
    • Set the default first, then override it inside an if (key in obj).
    • obj[key] ?? default is the short form.
    Keep going with TypeScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free