1. The 7 Primitive Types in JavaScript & TypeScript#
JavaScript defines 7 primitive values (data that is not an object and has no methods). TypeScript provides corresponding lowercase static types for each:
stringnumberbooleanbigintsymbolundefinednull
2. Core Primitives: string, number, boolean#
The string Type#
Represents textual data formatted as single quotes ('), double quotes ("), or template literals (``).
const firstName: string = "Rachmat";
const message: string = `Welcome back, ${firstName}!`;The number Type#
Unlike languages with distinct int, float, or double types, JavaScript numbers are all 64-bit double-precision IEEE 754 floating-point numbers. TypeScript’s number type represents all integers, floats, NaN (Not a Number), and Infinity.
const count: number = 42;
const price: number = 99.99;
const invalidResult: number = NaN;
const maxBoundary: number = Infinity;Because NaN is technically a value of type number at runtime, TypeScript will permit NaN wherever a number is expected.
The boolean Type#
Accepts only two literal values: true and false.
const isProduction: boolean = true;
const hasFeatureFlag: boolean = false;3. The String vs string Trap (Object Wrappers)#
A very common mistake for developers coming from Java or C# is using capitalized type names like String, Number, or Boolean.
In JavaScript, String is a constructor function for object wrappers (created via new String("hello")), while string is the primitive type.
// ❌ ANTI-PATTERN: Do NOT use uppercase wrapper types!
function printMessage(msg: String) {
console.log(msg);
}
// 🟢 CORRECT: Always use lowercase primitive types!
function printMessageCorrect(msg: string) {
console.log(msg);
}Why String as a Type is Dangerous#
String represents an instance of the String object wrapper class. While a primitive string can be assigned to String, the reverse is not true:
let primitiveStr: string = "hello";
let objectStr: String = new String("hello");
objectStr = primitiveStr; // Allowed!
// Error: Type 'String' is not assignable to type 'string'.
// 'string' is a primitive, but 'String' is a wrapper object.
// primitiveStr = objectStr;
| Type | Description | Usage |
|---|---|---|
string | Primitive string value | Always use this |
String | Object wrapper instance (new String()) | Never use as a type annotation |
4. null and undefined with strictNullChecks#
In JavaScript:
undefinedmeans a variable has been declared, but no value has been assigned yet.nullis an explicit assignment representing the deliberate absence of any object value.
The Impact of strictNullChecks#
When "strictNullChecks": false is configured in tsconfig.json, null and undefined can be assigned to any type (e.g., let x: number = null is valid). This leads to runtime TypeError: Cannot read properties of undefined crashes.
When "strictNullChecks": true is enabled (the mandatory setting for modern codebases), null and undefined are treated as distinct, unassignable types:
// With strictNullChecks: true
let username: string = "Alice";
// username = null; // Error: Type 'null' is not assignable to type 'string'.
// username = undefined; // Error: Type 'undefined' is not assignable to type 'string'.
// To allow null or undefined, use Union Types:
let nullableUser: string | null = null;
nullableUser = "Bob"; // Valid!
5. bigint and symbol#
The bigint Type#
Introduced in ES2020, bigint represents arbitrary-precision integers larger than $2^{53} - 1$ (Number.MAX_SAFE_INTEGER). BigInt literals end with an n suffix.
const maxSafeInt: number = Number.MAX_SAFE_INTEGER; // 9007199254740991
const hugeNumber: bigint = 9007199254740992n;
const calculatedBigInt: bigint = BigInt(9007199254740992);You cannot mix bigint and number arithmetic without explicit conversion (e.g., hugeNumber + 5 will throw a compiler error).
The symbol Type#
Symbols are primitive values created via Symbol() that are guaranteed to be unique and immutable. They are frequently used as unique object property keys.
const apiKey: symbol = Symbol("API_KEY");
const backupKey: symbol = Symbol("API_KEY");
console.log(apiKey === backupKey); // false (always unique!)
const config = {
[apiKey]: "secret_12345",
};Code Example: Type Narrowing Primitives#
When dealing with functions that accept primitive unions, use typeof checks to narrow types safely:
function processInput(input: string | number | null): string {
if (input === null) {
return "No input provided.";
}
// TypeScript narrows input to 'string | number' here
if (typeof input === "number") {
// TypeScript knows input is 'number' inside this block
return `Formatted Currency: $${input.toFixed(2)}`;
}
// TypeScript knows input MUST be 'string' here
return `Upper Message: ${input.toUpperCase()}`;
}
console.log(processInput(null)); // "No input provided."
console.log(processInput(49.5)); // "Formatted Currency: $49.50"
console.log(processInput("hello")); // "Upper Message: HELLO"
Summary & Next Steps#
In this episode:
- We covered JavaScript’s 7 primitive types:
string,number,boolean,bigint,symbol,null, andundefined. - We learned why uppercase types like
StringorNumberare object wrapper anti-patterns. - We analyzed how
strictNullCheckseliminates unhandlednullandundefinedcrashes. - We demonstrated primitive type narrowing using
typeof.
In Episode 3: Type Annotations vs. Inference, we will discover when you should explicitly annotate variables and when you should let the TypeScript compiler infer them automatically!

