bugl
bugl
HomeLearnPatternsSearch
HomeLearnPatternsSearch

Loading lesson path

Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript Literal Types

Understanding Literal Types in TypeScript

Literal types in TypeScript allow you to specify exact values that variables can hold, providing more precision than broader types like string or number.

Formula

They are the building blocks for creating precise and type - safe applications.

Key Concepts

String Literals

: Exact string values like "success" | "error"

Numeric Literals

: Specific numbers like 1 | 2 | 3

Boolean Literals

: Either true or false

Template Literal Types

: String literal types built using template string syntax

Common Use Cases

Defining specific sets of allowed values

Creating discriminated unions

Type-safe event handling

API response typing

Configuration objects

String Literal Types

A string literal type represents a specific string value:

Example

// A variable with a string literal type let direction: "north" | "south" | "east" | "west";
// Valid assignments direction = "north";
direction = "south";

// Invalid assignments would cause errors

// direction = "northeast"; // Error: Type '"northeast"' is not assignable to type '"north" | "south" | "east" | "west"'
// direction = "up";        // Error: Type '"up"' is not assignable to type '"north" | "south" | "east" | "west"'
// Using string literal types in functions function move(direction: "north" | "south" | "east" | "west") {
console.log(`Moving ${direction}`);
}
move("east");  // Valid
// move("up");  // Error: Argument of type '"up"' is not assignable to parameter of type...

Numeric Literal Types

Similar to string literals, numeric literal types represent specific numeric values:

Example

// A variable with a numeric literal type let diceRoll: 1 | 2 | 3 | 4 | 5 | 6;
// Valid assignments diceRoll = 1;
diceRoll = 6;

// Invalid assignments would cause errors

// diceRoll = 0;      // Error: Type '0' is not assignable to type '1 | 2 | 3 | 4 | 5 | 6'
// diceRoll = 7;      // Error: Type '7' is not assignable to type '1 | 2 | 3 | 4 | 5 | 6'
// diceRoll = 2.5;    // Error: Type '2.5' is not assignable to type '1 | 2 | 3 | 4 | 5 | 6'
// Using numeric literal types in functions function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 {
return Math.floor(Math.random() * 6) + 1 as 1 | 2 | 3 | 4 | 5 | 6;
}
const result = rollDice();
console.log(`You rolled a ${result}`);

Boolean Literal Types

Boolean literal types are less commonly used since there are only two boolean values, but they can be useful in specific scenarios:

Example

// A type that can only be the literal value 'true'

type YesOnly = true;
// A function that must return true function alwaysSucceed(): true {
// Always returns the literal value 'true'
return true;
}
// Boolean literal combined with other types type SuccessFlag = true | "success" | 1;
type FailureFlag = false | "failure" | 0;
function processResult(result: SuccessFlag | FailureFlag) {
if (result === true || result === "success" || result === 1) {
console.log("Operation succeeded");
} else {
console.log("Operation failed");
}
}
processResult(true);      // "Operation succeeded"
processResult("success"); // "Operation succeeded"
processResult(1);         // "Operation succeeded"
processResult(false);     // "Operation failed"

Literal Types with Objects

Literal types can be combined with object types to create very specific shapes:

Example

// Object with literal property values type HTTPSuccess = {
status: 200 | 201 | 204;
statusText: "OK" | "Created" | "No Content";
data: any;
};
type HTTPError = {
status: 400 | 401 | 403 | 404 | 500;
statusText: "Bad Request" | "Unauthorized" | "Forbidden" | "Not Found" | "Internal Server Error";
error: string;
};
type HTTPResponse = HTTPSuccess | HTTPError;
function handleResponse(response: HTTPResponse) {
if (response.status >= 200 && response.status < 300) {
console.log(`Success: ${response.statusText}`);
console.log(response.data);
} else {
console.log(`Error ${response.status}: ${response.statusText}`);
console.log(`Message: ${response.error}`);
}
}
// Example usage const successResponse: HTTPSuccess = {
status: 200, statusText: "OK", data: { username: "john_doe", email: "john@example.com" }
};
const errorResponse: HTTPError = {
status: 404, statusText: "Not Found", error: "User not found in database"
};
handleResponse(successResponse);
handleResponse(errorResponse);

Template Literal Types

TypeScript 4.1+ introduced template literal types, which allow you to create new string literal types by combining existing ones using template string syntax:

Example

// Basic template literals type Direction = "north" | "south" | "east" | "west";
type Distance = "1km" | "5km" | "10km";
// Using template literals to combine them type DirectionAndDistance = `${Direction}-${Distance}`;

Formula

// "north - 1km" | "north - 5km" | "north - 10km" | "south - 1km" | ...
let route: DirectionAndDistance;
route = "north-5km";   // Valid route = "west-10km";   // Valid
// route = "north-2km";   // Error
// route = "5km-north";   // Error
// Advanced string manipulation type EventType = "click" | "hover" | "scroll";
type EventTarget = "button" | "link" | "div";
type EventName = `on${Capitalize<EventType>}${Capitalize<EventTarget>}`;
// "onClickButton" | "onClickLink" | "onClickDiv" | ...
// Dynamic property access type User = {
id: number;
name: string;
email: string;
createdAt: Date;
};
type GetterName<T> = `get${Capitalize<string & keyof T>}`;
type UserGetters = {
[K in keyof User as GetterName<User>]: () => User[K];
};
// { getId: () => number; getName: () => string; ... }

Formula

// String pattern matching type ExtractRouteParams < T extends string > =
T extends `${string}:${infer Param}/${infer Rest}`

Formula

? Param | ExtractRouteParams < Rest >
: T extends `${string}:${infer Param}`

? Param

: never;
type Params = ExtractRouteParams<"/users/:userId/posts/:postId">;  // "userId" | "postId"
// CSS units and values type CssUnit = 'px' | 'em' | 'rem' | '%' | 'vh' | 'vw';
type CssValue = `${number}${CssUnit}`;
let width: CssValue = '100px';    // Valid let height: CssValue = '50%';     // Valid
// let margin: CssValue = '10';   // Error
// let padding: CssValue = '2ex'; // Error
// API versioning type ApiVersion = 'v1' | 'v2' | 'v3';
type Endpoint = 'users' | 'products' | 'orders';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiUrl = `https://api.example.com/${ApiVersion}/${Endpoint}`;
// Complex example: Dynamic SQL query builder type Table = 'users' | 'products' | 'orders';

Formula

type Column < T extends Table > =

T extends 'users' ? 'id' | 'name' | 'email' | 'created_at' : T extends 'products' ? 'id' | 'name' | 'price' | 'in_stock' :

T extends 'orders' ? 'id' | 'user_id' | 'total' | 'status' : never;
type WhereCondition<T extends Table> = {
[K in Column<T>]?: {
equals?: any;
notEquals?: any;
in?: any[];
};
};
function query<T extends Table>(

Formula

table: T, where?: WhereCondition < T >
): `SELECT * FROM ${T}${string}` {
// Implementation would build the query return `SELECT * FROM ${table}` as const;
}
// Usage const userQuery = query('users', {
name: { equals: 'John' }, created_at: { in: ['2023-01-01', '2023-12-31'] }
});

Formula

// Type: "SELECT * FROM users WHERE ..."

Best Practices

Do's and Don'ts

Previous

TypeScript Mapped Types

Next

TypeScript Namespaces