Basic data types
In this chapter, you learn TypeScript's basic data types.
What you learn in this chapter
- How to use the number, string, and boolean types
- How to write and work with array types (
Array<T>and T[]) - The characteristics of tuple types and how to use them
- When to use type inference versus type annotations
In this chapter, use the TypeScript Playground or the local environment you set up in the previous chapter, and learn by writing code.
The number type
As in JavaScript, all numbers in TypeScript are the number type. There is no distinction between integers and floating-point numbers.
// Integers
let integer: number = 42;
let negative: number = -10;
// Floating-point numbers
let decimal: number = 3.14;
let scientific: number = 1.5e10; // 1.5 × 10^10 = 15000000000
// Binary, octal, hexadecimal
let binary: number = 0b1010; // Binary: 10
let octal: number = 0o744; // Octal: 484
let hex: number = 0xff; // Hexadecimal: 255
// Special numbers
let infinity: number = Infinity;
let negInfinity: number = -Infinity;
let notANumber: number = NaN;
Numeric separators
To make large numbers easier to read, you can use an underscore (_) as a separator.
// Examples of numeric separators
let million: number = 1_000_000; // 1000000
let billion: number = 1_000_000_000; // 1000000000
let creditCard: number = 1234_5678_9012_3456;
// The separators are removed in the compiled JavaScript
console.log(million); // 1000000
Numeric operations
let a: number = 10;
let b: number = 3;
console.log(a + b); // 13 (addition)
console.log(a - b); // 7 (subtraction)
console.log(a * b); // 30 (multiplication)
console.log(a / b); // 3.333... (division)
console.log(a % b); // 1 (remainder)
console.log(a ** b); // 1000 (exponentiation: 10^3)
The string type
Strings can be written in three ways.
// Single quotes
let single: string = 'Hello';
// Double quotes
let double: string = "World";
// Template literals (backticks)
let template: string = `Hello, World!`;
Using template literals
Template literals let you embed variables and expressions in a string.
let name: string = 'Alice';
let age: number = 25;
// Embedding variables
let message: string = `My name is ${name} and I am ${age} years old.`;
console.log(message);
// Output: My name is Alice and I am 25 years old.
// Embedding an expression
let price: number = 100;
let taxRate: number = 0.1;
let total: string = `Total: ${price * (1 + taxRate)}`;
console.log(total);
// Output: Total: 110.00000000000001
// (0.1 cannot be represented exactly as a binary floating-point number, so there is rounding error)
// Multi-line strings (line breaks are preserved)
let poem: string = `
Roses are red,
Violets are blue,
TypeScript is awesome,
And so are you!
`;
Comparison with string concatenation
// Traditional string concatenation (hard to read)
let oldStyle: string = 'My name is ' + name + ' and I am ' + age + ' years old.';
// Template literal (easy to read)
let newStyle: string = `My name is ${name} and I am ${age} years old.`;
The boolean type
A type that holds only one of two values: true or false.
// Basic usage
let isActive: boolean = true;
let isCompleted: boolean = false;
// Results of comparison operations
let age: number = 20;
let isAdult: boolean = age >= 18; // true
let isEqual: boolean = (10 === 10); // true
let isGreater: boolean = (5 > 10); // false
// Logical operations
let a: boolean = true;
let b: boolean = false;
console.log(a && b); // false (AND: true only when both are true)
console.log(a || b); // true (OR: true when either is true)
console.log(!a); // false (NOT: inverts the boolean)
A practical example
// Managing user state
let isLoggedIn: boolean = false;
let hasPermission: boolean = true;
let isAdmin: boolean = false;
// Conditional branching
if (isLoggedIn && hasPermission) {
console.log('Access granted');
} else {
console.log('Access denied');
}
// Debug flag
let debugMode: boolean = true;
if (debugMode) {
console.log('Debug information: ....');
}
JavaScript's truthy/falsy values are different from the boolean type. 1 and "true" are not of the boolean type.
Type annotations and type inference
TypeScript has a feature that infers types automatically.
// Pattern 1: write the type annotation explicitly
let message: string = 'Hello';
let count: number = 10;
let isActive: boolean = true;
// Pattern 2: leave it to type inference (recommended)
let message2 = 'Hello'; // inferred as string
let count2 = 10; // inferred as number
let isActive2 = true; // inferred as boolean
Cases where a type annotation is needed
// Case 1: when you do not set an initial value
let username: string; // a type annotation is needed
username = 'John'; // assign a value later
// Case 2: when type inference is not enough
let data: string | null = null; // when you allow multiple types
Best practices
// Good: leave it to inference when there is an initial value
let message = 'Hello';
let count = 0;
// Good: write a type annotation when there is no initial value
let username: string;
let age: number;
// Redundant: writing a type annotation when inference is enough
let message: string = 'Hello'; // inference is enough
Array types
An array is a data structure that can hold multiple values of the same type.
How to declare arrays
// Approach 1: the type[] syntax (recommended)
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ['Alice', 'Bob', 'Charlie'];
let flags: boolean[] = [true, false, true];
// Approach 2: the Array<type> syntax (generic syntax)
let numbers2: Array<number> = [1, 2, 3, 4, 5];
let names2: Array<string> = ['Alice', 'Bob', 'Charlie'];
// Using type inference
let numbers3 = [1, 2, 3, 4, 5]; // inferred as number[]
let names3 = ['Alice', 'Bob']; // inferred as string[]
let mixed = [1, 'two', true]; // inferred as (string | number | boolean)[]
Working with arrays
let fruits: string[] = ['apple', 'banana', 'orange'];
// Accessing elements (indexes start at 0)
console.log(fruits[0]); // 'apple'
console.log(fruits[1]); // 'banana'
console.log(fruits[2]); // 'orange'
// Changing an element
fruits[1] = 'grape';
console.log(fruits); // ['apple', 'grape', 'orange']
// Getting the array length
console.log(fruits.length); // 3
// Adding an element
fruits.push('melon'); // add to the end
console.log(fruits); // ['apple', 'grape', 'orange', 'melon']
// Removing an element
let lastFruit = fruits.pop(); // remove the last element and return it
console.log(lastFruit); // 'melon'
Array methods
let numbers: number[] = [1, 2, 3, 4, 5];
// map: create a new array with each element transformed
let doubled: number[] = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter: create a new array with only the elements that match a condition
let evens: number[] = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce: aggregate the array elements into a single value
let sum: number = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15 (1+2+3+4+5)
// find: get the first element that matches a condition
let found: number | undefined = numbers.find(n => n > 3);
console.log(found); // 4
// includes: check whether the array contains a specific value
let hasThree: boolean = numbers.includes(3);
console.log(hasThree); // true
An example of type safety
let numbers: number[] = [1, 2, 3];
// OK: add a number value
numbers.push(4);
// Error: a string value cannot be added
// numbers.push('5');
// Error: Argument of type 'string' is not assignable to parameter of type 'number'
Multi-dimensional arrays
// A 2D array (think of it as a matrix)
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// Accessing elements
console.log(matrix[0][0]); // 1 (row 1, column 1)
console.log(matrix[1][1]); // 5 (row 2, column 2)
console.log(matrix[2][2]); // 9 (row 3, column 3)
Read-only arrays
To create an array that cannot be changed, use the readonly modifier.
// Declaring a readonly array
let numbers: readonly number[] = [1, 2, 3, 4, 5];
// Reading is OK
console.log(numbers[0]); // 1
console.log(numbers.length); // 5
// Trying to change it is an error
// numbers[0] = 10; // Error
// numbers.push(6); // Error
// numbers.pop(); // Error
Tuple types
A tuple is an array with a fixed length where the type of each element is determined.
Tuple basics
// Declaring a tuple: [type1, type2, type3, ...]
let person: [string, number, boolean] = ['Alice', 25, true];
The difference from an array
// Array: all elements are the same type, the number of elements is variable
let array: number[] = [1, 2, 3, 4, 5];
// Tuple: the type is determined at each position, the number of elements is fixed
let tuple: [string, number] = ['Alice', 25];
Type safety with tuples
let userInfo: [string, number, boolean] = ['Alice', 25, true];
// ↑first ↑second ↑third
// The type of each element is guaranteed
let name: string = userInfo[0]; // OK: string
let age: number = userInfo[1]; // OK: number
let isActive: boolean = userInfo[2]; // OK: boolean
// A wrong type is an error
// let wrong: number = userInfo[0]; // Error: cannot assign a string to a number
// A wrong number of elements is an error
// let wrong: [string, number] = ['Alice', 25, true];
// Error: the number of elements does not match
Destructuring
let data: [string, number, boolean] = ['Alice', 25, true];
// Use destructuring to extract each element into a variable
let [name, age, isActive] = data;
console.log(name); // 'Alice'
console.log(age); // 25
console.log(isActive); // true
Labeled tuples
From TypeScript 4.0 onward, you can label each element of a tuple.
// Without labels (the traditional approach)
let user1: [string, number, boolean] = ['Alice', 25, true];
// With labels (easier to read)
let user2: [name: string, age: number, isActive: boolean] = ['Alice', 25, true];
// Using it as a function's return value
function getUserInfo(): [name: string, age: number, email: string] {
return ['Alice', 25, 'alice@example.com'];
}
let [userName, userAge, userEmail] = getUserInfo();
Practical examples of tuples
// Example 1: representing coordinates
type Point2D = [x: number, y: number];
type Point3D = [x: number, y: number, z: number];
let point: Point2D = [10, 20];
let point3d: Point3D = [10, 20, 30];
// Example 2: representing an RGB color
type RGB = [red: number, green: number, blue: number];
let red: RGB = [255, 0, 0];
let green: RGB = [0, 255, 0];
let blue: RGB = [0, 0, 255];
// Example 3: an API response
type ApiResponse = [status: number, data: unknown, error: string | null];
let successResponse: ApiResponse = [200, { name: 'Alice' }, null];
let errorResponse: ApiResponse = [404, null, 'Not Found'];
// Example 4: a key-value pair (such as the return value of Map.entries())
type Entry<K, V> = [key: K, value: V];
let userEntry: Entry<string, number> = ['age', 25];
// Example 5: the return value of a React hook (useState)
// const [state, setState] = useState(initialValue);
// The return value is a tuple [T, (value: T) => void]
// Example 6: a function that returns multiple values
function getMinMax(numbers: number[]): [min: number, max: number] {
return [Math.min(...numbers), Math.max(...numbers)];
}
const [min, max] = getMinMax([3, 1, 4, 1, 5, 9, 2, 6]);
console.log(`Min: ${min}, Max: ${max}`); // Min: 1, Max: 9
Tuple vs. object
A tuple is suited to grouping a small number of values where the order matters. When you want to make the meaning clear with property names, use an object.
// Tuple: the meaning is determined by the order (coordinates, RGB, min/max, and so on)
const point: [number, number] = [10, 20];
// Object: the meaning is clear from the property names
const user = { name: 'Alice', age: 25 };
Try it: type your user information ★
Create variables for user information that meet the following requirements.
Requirements:
userName: your name (string)userAge: your age (number)isStudent: whether you are a student (boolean)hobbies: an array of hobbies (string[])profile: a tuple [name, age, location]
Print the data you created to the console to verify it.
Hint
- It is fine to use type inference for the basic-type variables
- Create the array in the form
['hobby1', 'hobby2', 'hobby3'] - Add the type annotation
[string, number, string]to the tuple - Using a template literal makes the output easy to read
Answer and explanation
// Basic types (using type inference)
let userName = 'Taro Tanaka';
let userAge = 25;
let isStudent = true;
// Array (a list of hobbies)
let hobbies: string[] = ['Reading', 'Programming', 'Watching movies'];
// Tuple (data with a fixed structure)
let profile: [string, number, string] = ['Taro Tanaka', 25, 'Tokyo'];
// Print to the console
console.log(`Name: ${userName}`);
console.log(`Age: ${userAge}`);
console.log(`Student: ${isStudent ? 'Yes' : 'No'}`);
console.log(`Hobbies: ${hobbies.join(', ')}`);
console.log(`Profile: ${profile[0]} (${profile[1]}) - ${profile[2]}`);
// Example output:
// Name: Taro Tanaka
// Age: 25
// Student: Yes
// Hobbies: Reading, Programming, Watching movies
// Profile: Taro Tanaka (25) - Tokyo
Explanation:
userName,userAge, andisStudenthave their types inferred from their initial values, so the type annotations are omittedhobbieshas an explicitstring[]annotation, but it would be fine to leave it to inferenceprofileis a tuple type, so writing the[string, number, string]annotation fixes the type at each position- Template literals let you write strings with embedded variables concisely
Summary
What you learned in this chapter:
- The number type: represents integers and floating-point numbers. Numeric separators make it easier to read
- The string type: represents strings. Template literals let you embed variables
- The boolean type: represents the true/false boolean value
- Array types: hold multiple values of the same type. Declared with
type[]orArray<type> - Tuple types: arrays with a fixed length where the type of each element is determined
- Type inference: infers the type from the initial value. An explicit type annotation is needed when there is no initial value
In the next chapter, you will learn about object types and type aliases (type).
Common pitfalls for beginners
Common mistakes
❌ Confusing string with String
// ❌ Wrong: using the object wrapper type
let name: String = 'Alice';
// ✅ Correct: use the primitive type
let name: string = 'Alice';
Cause: JavaScript has String (an object) and string (a primitive), and in TypeScript you basically use the lowercase primitive type.
Solution: Always use lowercase string, number, and boolean in type annotations.
❌ Misunderstanding the return value of an array's push method
let fruits: string[] = ['apple'];
// ❌ Wrong: thinking push returns the array
let newArray = fruits.push('banana');
console.log(newArray); // 2 (the new length is returned, not the array)
// ✅ Correct: push returns the length. The array itself is modified
fruits.push('banana');
console.log(fruits); // ['apple', 'banana']
Cause: push() returns the length of the array after adding.
Solution: When you need a new array, use spread syntax.
let newArray = [...fruits, 'orange'];
❌ Confusing a tuple with an array
// ❌ Wrong: declaring a tuple without a type annotation makes it an array
let point = [10, 20]; // inferred as number[]
point.push(30); // OK (it is an array, so you can add elements)
// ✅ Correct: write an explicit type annotation for a tuple
let point: [number, number] = [10, 20];
// point.push(30); // not a type error, but unintended behavior
Cause: With type inference, [10, 20] is treated as number[].
Solution: When you want a tuple, always write an explicit type annotation.
❌ Trying to modify a read-only array
let numbers: readonly number[] = [1, 2, 3];
// ❌ Wrong: trying to modify a readonly array
numbers.push(4); // Error: Property 'push' does not exist
numbers[0] = 10; // Error: Index signature only permits reading
// ✅ Correct: create a new array
let newNumbers = [...numbers, 4];
Cause: The readonly modifier forbids changing the array.
Solution: When you need a change, create a new array or remove readonly.
❌ Using === to compare NaN
let result = parseInt('abc'); // NaN
// ❌ Wrong: NaN is not equal to itself
if (result === NaN) { // always false
console.log('Invalid number');
}
// ✅ Correct: use Number.isNaN()
if (Number.isNaN(result)) {
console.log('Invalid number');
}
Cause: In the JavaScript spec, NaN === NaN is always false.
Solution: Use the Number.isNaN() function to check for NaN.