JavaScript in LWC: The Basics · Lesson 2 of 25
Variables, Data Types and Strings in LWC
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);
}
001Hp00002ABCDe 1 { Name: 'Acme Ltd' }
TypeError: Assignment to constant variable.
- Use
constby default andletonly 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 localletdisappears 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);
Hot Warm true false
What the variables hold
original→ object #1{ Name, Rating: 'Hot' }alias→ object #1same referencecopy→ 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');
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'));
0.30000000000000004 0.30 59.97 31 4 0 NaN true 42 7.5
lightning-input type="number"still gives you a string inevent.target.value. Convert it withNumber()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.NumberFormatformat 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());
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>
Acme renewal (Proposal)
Amount: 0
› parent sets opportunity = {"Name":"Globex deal","Amount":250000}
Globex deal (No stage)
Amount: 2,50,000Practice
Try it yourself
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.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.