LearnFrenzy
Sign in Take a free test

JavaScript in LWC: The Basics · Lesson 2 of 25

Variables, Data Types and Strings in LWC

6 min read Updated Nov 8, 2026 Saurabh Samir

Records from Salesforce arrive as JavaScript values: strings, numbers, booleans, objects – and quite often undefined, because a field was blank or not queried. Knowing how each type behaves saves you from the classic LWC bugs: an Amount of 0 shown as "No amount", a list that doesn't refresh, or "undefined" printed on screen.

In this lesson

  • let and const (and why var is gone)
  • Primitive and reference types
  • null vs undefined in Salesforce data
  • Numbers, strings and template literals

let and const

const recordId = '001Hp00002ABCDe';   // never reassigned → const
let attempts = 0;                     // changes → let
attempts += 1;

const account = { Name: 'Acme' };
account.Name = 'Acme Ltd';            // allowed: const protects the variable, not the object inside it

console.log(recordId, attempts, account);

try {
    attempts = 'two';                 // allowed, but a bad idea: keep one type per variable
    recordId = '002';                 // TypeError
} catch (e) {
    console.log(e.name + ': ' + e.message);
}

Output

001Hp00002ABCDe 1 { Name: 'Acme Ltd' }
TypeError: Assignment to constant variable.
  • Use const by default and let only when the value really changes. LWC's ESLint rules nudge you this way.
  • Don't use var: it ignores block scope and is hoisted in confusing ways.
  • Inside a component, state lives in class fields (this.count), not in variables – a local let disappears when the method ends.

Primitive vs reference types

The seven primitives – copied by value

  • string'Acme'
  • number42, 3.5, NaN
  • booleantrue / false
  • undefinednot set
  • nulldeliberately empty
  • bigint9007199254740993n
  • symbolunique keys

Everything else – objects, arrays, functions, dates – is a reference type. Assigning one copies the reference, not the data. That matters in LWC, because the engine decides whether to re-render by checking whether a field got a new value:

const original = { Name: 'Acme', Rating: 'Warm' };
const alias = original;               // same object
const copy = { ...original };         // a new object with the same properties

alias.Rating = 'Hot';
console.log(original.Rating);         // changed through the alias
console.log(copy.Rating);             // the copy is independent
console.log(alias === original, copy === original);

Output

Hot
Warm
true false

What the variables hold

  • original→ object #1{ Name, Rating: 'Hot' }
  • alias→ object #1same reference
  • copy→ object #2{ Name, Rating: 'Warm' }

You'll lean on this idea in the reactivity lesson: to update a list or an object in LWC, you usually assign a new one.

null and undefined in Salesforce data

When Apex returns an sObject, blank fields are simply missing from the JSON, so reading them gives undefined. Lightning Data Service represents a blank field value as null. Your code has to handle both:

// what an Apex method returns for an Account with a blank Phone and no Owner queried
const account = { Id: '001A', Name: 'Acme', AnnualRevenue: 0 };

console.log(account.Phone);                  // missing field
console.log(account.Owner?.Name);            // optional chaining: no crash on missing parent
console.log(typeof account.Phone, typeof null);

// the classic bug: || treats 0 as "missing"
console.log(account.AnnualRevenue || 'Not provided');
// the fix: ?? only falls back for null and undefined
console.log(account.AnnualRevenue ?? 'Not provided');
console.log(account.Phone ?? 'Not provided');

Output

undefined
undefined
undefined object
Not provided
0
Not provided

typeof null is "object"

A 30-year-old quirk of the language. To test for "no value", use value == null (true for both null and undefined) or value ?? fallback, not typeof.

Numbers: decimals and conversions

console.log(0.1 + 0.2);                         // binary floating point
console.log((0.1 + 0.2).toFixed(2));            // a string, rounded for display
console.log(Math.round((19.99 * 3) * 100) / 100);

// values from lightning-input arrive as strings
const qty = '3';
console.log(qty + 1);                           // string concatenation!
console.log(Number(qty) + 1);
console.log(Number(''), Number('abc'), Number.isNaN(Number('abc')));
console.log(parseInt('42px', 10), Number.parseFloat('7.5kg'));

Output

0.30000000000000004
0.30
59.97
31
4
0 NaN true
42 7.5
  • lightning-input type="number" still gives you a string in event.target.value. Convert it with Number() before doing maths.
  • Number('') is 0, not NaN – check for an empty string first if blank means "not entered".
  • For money, round only when displaying, or work in the smallest unit (paise, cents); and let Intl.NumberFormat format currency (see dates and Intl).

Strings and template literals

const contact = { FirstName: 'Ravi', LastName: 'Kumar', Title: undefined, Email: ' RAVI@Example.com ' };

const fullName = `${contact.FirstName} ${contact.LastName}`;
const subtitle = `${contact.Title ?? 'No title'} · ${contact.Email.trim().toLowerCase()}`;
console.log(fullName);
console.log(subtitle);

console.log('Acme Corporation'.includes('Corp'), 'INV-0042'.startsWith('INV-'));
console.log('a,b,,c'.split(',').filter(Boolean));
console.log('7'.padStart(3, '0'), 'Pending Approval'.replaceAll(' ', '_').toUpperCase());

Output

Ravi Kumar
No title · ravi@example.com
true true
[ 'a', 'b', 'c' ]
007 PENDING_APPROVAL

Template literals (backticks) are perfect for getters that build labels. Here is the same idea in a component – including the "0 is a real value" rule:

// opportunityTile.js
import { LightningElement, api } from 'lwc';

export default class OpportunityTile extends LightningElement {
    @api opportunity = {};

    get title() {
        const { Name, StageName } = this.opportunity;
        return `${Name} (${StageName ?? 'No stage'})`;
    }

    get amountText() {
        const amount = this.opportunity.Amount;
        return amount == null ? 'Amount not set' : `Amount: ${amount.toLocaleString('en-IN')}`;
    }
}
<!-- opportunityTile.html -->
<template>
    <p>{title}</p>
    <p>{amountText}</p>
</template>

Result

Acme renewal (Proposal)
Amount: 0

› parent sets opportunity = {"Name":"Globex deal","Amount":250000}
Globex deal (No stage)
Amount: 2,50,000

Practice

Try it yourself

  1. A getter returns this.record.Discount__c || 'None'. Users with a 0% discount see "None". Fix it.

    Show answer

    Use this.record.Discount__c ?? 'None'. || falls back for every falsy value including 0; ?? only for null and undefined.

  2. Two quantity inputs hold "2" and "5". Your total shows 25. Why, and how do you fix it?

    Show answer

    Input values are strings, so + concatenates. Convert first: Number(a) + Number(b).

Quick check

What does reading a blank field from an Apex-returned sObject give you?

undefined – blank fields are left out of the JSON.

Does const make an object immutable?

No. It stops the variable being reassigned; the object's properties can still change.

What is typeof null?

"object" – a historic quirk; test with value == null instead.

Summary

  • Prefer const, use let when needed, never var; component state lives in fields.
  • Primitives are copied, objects and arrays are shared by reference.
  • Expect undefined and null in Salesforce data; use ?. and ?? to handle them.
  • Input values are strings – convert before maths; use template literals for labels.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links