Skip to main content

TypeScript Data Types

Introduction

TypeScript, an extension of JavaScript, introduces a robust typing system that helps catch errors during development rather than at runtime. Understanding TypeScript's data types is fundamental to leveraging its full potential as they form the foundation for type checking - one of TypeScript's core benefits.

In this guide, we'll explore various data types in TypeScript, from basic primitives to more complex types. We'll see how these types can make your code more reliable, self-documenting, and maintainable.

Why Types Matter

Before diving into TypeScript's type system, let's understand why types are important:

  1. Error Detection: Types help catch errors during development
  2. Code Documentation: Types serve as built-in documentation
  3. IDE Support: Better autocompletion and intellisense
  4. Refactoring: Makes code changes safer and easier

Basic Types

Boolean

The most basic data type is the boolean, which can have either true or false values.

typescript
let isCompleted: boolean = false;
isCompleted = true; // Valid
// isCompleted = "yes"; // Error: Type 'string' is not assignable to type 'boolean'

Number

All numbers in TypeScript are floating-point values. These can be integers, decimals, hexadecimals, binary, and octal literals.

typescript
let decimal: number = 10;
let hex: number = 0xf00d; // hexadecimal
let binary: number = 0b1010; // binary
let octal: number = 0o744; // octal

String

Text data is represented using the string type. You can use single quotes ('), double quotes (") or template strings (``) to define strings.

typescript
let firstName: string = "John";
let lastName: string = 'Doe';
let greeting: string = `Hello, my name is ${firstName} ${lastName}`;

console.log(greeting); // Output: Hello, my name is John Doe

Array

TypeScript offers two ways to define arrays:

typescript
// Using square brackets
let numbers: number[] = [1, 2, 3, 4, 5];

// Using generic Array type
let fruits: Array<string> = ["apple", "banana", "cherry"];

// Mixed array (using union type, which we'll cover later)
let mixed: (number | string)[] = [1, "two", 3, "four"];

Tuple

Tuples let you express an array with a fixed number of elements whose types are known.

typescript
// Define a tuple type
let person: [string, number] = ["Alice", 30];

console.log(person[0]); // Output: Alice
console.log(person[1]); // Output: 30

// This would be an error:
// person[2] = "developer"; // Error: Tuple type '[string, number]' has no element at index '2'

Enum

Enums allow us to define a set of named constants.

typescript
enum Direction {
Up,
Down,
Left,
Right
}

let playerDirection: Direction = Direction.Up;
console.log(playerDirection); // Output: 0

// You can also set custom values
enum HttpStatus {
OK = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401
}

console.log(HttpStatus.OK); // Output: 200

Any

The any type allows you to opt-out of type checking. It's useful when you're working with dynamic content or migrating from JavaScript to TypeScript.

typescript
let dynamicValue: any = 4;
dynamicValue = "a string"; // No error
dynamicValue = true; // No error

// Be careful with any as it loses type safety!

Void

void represents the absence of a type, commonly used as the return type of functions that don't return a value.

typescript
function logMessage(message: string): void {
console.log(message);
// No return statement
}

let unusable: void = undefined; // You can only assign undefined or null (if strictNullChecks is not enabled)

Null and Undefined

In TypeScript, both null and undefined have their own types.

typescript
let u: undefined = undefined;
let n: null = null;

// By default, null and undefined are subtypes of all other types
// However, when using --strictNullChecks flag, they can only be assigned to themselves or any

Never

The never type represents values that never occur. It's typically used for functions that always throw exceptions or never return.

typescript
function throwError(message: string): never {
throw new Error(message);
}

function infiniteLoop(): never {
while (true) {}
}

Object

The object type represents non-primitive types.

typescript
let user: object = {
name: "Alice",
age: 30
};

// More specific object type (recommended)
let typedUser: { name: string; age: number } = {
name: "Bob",
age: 25
};

console.log(typedUser.name); // Output: Bob

Special Types in TypeScript

Union Types

Union types allow you to specify that a value can be one of several types.

typescript
let id: string | number;
id = 101; // Valid
id = "A-101"; // Also valid
// id = true; // Error: Type 'boolean' is not assignable to type 'string | number'

Intersection Types

Intersection types combine multiple types into one.

typescript
type Person = {
name: string;
};

type Employee = {
employeeId: number;
department: string;
};

type EmployeeDetails = Person & Employee;

const employee: EmployeeDetails = {
name: "John Doe",
employeeId: 12345,
department: "Engineering"
};

Type Aliases

Type aliases create new names for types. They're similar to interfaces but can name primitives, unions, tuples, and other types.

typescript
type Age = number;
type Point = [number, number];
type UserID = string | number;

let myAge: Age = 30;
let coordinate: Point = [10, 20];
let userId: UserID = "user-123";

Literal Types

Literal types allow you to specify exact values a variable can have.

typescript
type Direction = "North" | "East" | "South" | "West";
let currentDirection: Direction = "North";

// Only these values are allowed:
currentDirection = "East"; // Valid
// currentDirection = "Northeast"; // Error: Type '"Northeast"' is not assignable to type 'Direction'

// Number literals work too
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
let roll: DiceRoll = 6;

Type Assertions

Type assertions are a way to tell the compiler "trust me, I know what I'm doing." It's like type casting in other languages but has no runtime impact.

typescript
// Using angle-bracket syntax (not valid in JSX)
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// Using the 'as' syntax (works in JSX)
let otherValue: any = "hello world";
let otherLength: number = (otherValue as string).length;

Type Inference

TypeScript can infer types when you don't explicitly specify them.

typescript
// TypeScript infers 'message' to be of type 'string'
let message = "Hello, TypeScript!";

// message = 42; // Error: Type 'number' is not assignable to type 'string'

Practical Examples

Form Validation

typescript
// Define the structure of a form
interface RegistrationForm {
username: string;
email: string;
password: string;
age: number;
newsletter?: boolean; // Optional field
}

function validateForm(form: RegistrationForm): boolean {
// Email validation with a simple regex
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(form.email)) {
console.error("Invalid email format");
return false;
}

// Password validation
if (form.password.length < 8) {
console.error("Password must be at least 8 characters");
return false;
}

// Age validation
if (form.age < 18 || form.age > 120) {
console.error("Age must be between 18 and 120");
return false;
}

return true;
}

// Example usage
const newUser: RegistrationForm = {
username: "dev_user",
email: "[email protected]",
password: "secure123",
age: 28,
newsletter: true
};

if (validateForm(newUser)) {
console.log("Form is valid, processing submission...");
// Process the form...
}

API Response Handling

typescript
// Define the API response structure
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}

// Mock API function
function fetchUserData(userId: number): Promise<User> {
return new Promise((resolve, reject) => {
// Simulate API call
setTimeout(() => {
if (userId > 0) {
const user: User = {
id: userId,
name: "Jane Smith",
email: "[email protected]",
isActive: true
};
resolve(user);
} else {
reject(new Error("Invalid user ID"));
}
}, 500);
});
}

// Using the API
async function displayUserInfo(userId: number): Promise<void> {
try {
const user = await fetchUserData(userId);

// With type information, we get autocompletion for user properties
console.log(`User: ${user.name} (${user.email})`);

if (user.isActive) {
console.log("This user is currently active");
}
} catch (error) {
console.error("Failed to fetch user:", error);
}
}

displayUserInfo(123);

Type Safety with Functions

typescript
// Function with parameter and return type annotations
function calculateTax(income: number, taxRate: number): number {
return income * taxRate;
}

// Function with optional parameters
function createGreeting(name: string, title?: string): string {
if (title) {
return `Hello, ${title} ${name}!`;
}
return `Hello, ${name}!`;
}

console.log(createGreeting("Alice", "Ms.")); // Output: Hello, Ms. Alice!
console.log(createGreeting("Bob")); // Output: Hello, Bob!

// Function with default parameters
function buildUrl(path: string, domain: string = "example.com"): string {
return `https://${domain}/${path}`;
}

console.log(buildUrl("products")); // Output: https://example.com/products
console.log(buildUrl("blog", "mysite.com")); // Output: https://mysite.com/blog

// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // Output: 15

Type Guard Functions

Type guards help TypeScript narrow down the type of a variable within a conditional block.

typescript
function isString(value: any): value is string {
return typeof value === 'string';
}

function processValue(value: string | number) {
if (isString(value)) {
// TypeScript knows value is a string in this block
console.log(value.toUpperCase());
} else {
// TypeScript knows value is a number in this block
console.log(value.toFixed(2));
}
}

processValue("hello"); // Output: HELLO
processValue(42); // Output: 42.00

Summary

TypeScript's type system offers a rich set of tools to make your code more robust and maintainable:

  1. Basic types (boolean, number, string, array, tuple, enum, any, void, null, undefined, never, object) form the foundation.

  2. Advanced types (unions, intersections, type aliases, literals) provide flexibility and precision.

  3. Type assertions let you override the inferred type when needed.

  4. Type inference reduces the need for explicit type annotations.

Understanding these concepts will help you write more reliable code with fewer bugs, better documentation, and improved developer experience.

Exercises

To reinforce your understanding, try these exercises:

  1. Create a function that accepts an array of mixed types (numbers and strings) and returns the sum of all numbers and the concatenation of all strings.

  2. Define a type for a task management application that includes task types (bug, feature, documentation) with different properties for each type.

  3. Implement a simple shopping cart with TypeScript types for products, cart items, and checkout functionality.

Additional Resources

Remember, mastering TypeScript types takes practice. Start simple and gradually incorporate more advanced typing features as you become comfortable with the basics.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)