Primitive types and literal types
Primitive Types
The primitive types in TypeScript are largely consistent with JavaScript, including number
, string
, boolean
, null
, undefined
, symbol
, and bigint
. These types represent the most basic, indivisible units of data.
let age: number = 25;
let name: string = "Alice";
let isActive: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;
let uniqueKey: symbol = Symbol("key");
let bigNumber: bigint = 9007199254740991n;
Primitive types are characterized by immutability—once created, they cannot be modified. TypeScript enforces strict type checking for these types:
let price: number = 100;
price = "200"; // Error: Type '"string"' is not assignable to type 'number'
Literal Types
Literal types are a unique feature of TypeScript's type system, allowing values themselves to serve as types. Literal types are divided into string literals, numeric literals, and boolean literals.
String Literal Types
type Direction = "north" | "south" | "east" | "west";
function move(direction: Direction) {
console.log(`Moving ${direction}`);
}
move("north"); // Correct
move("up"); // Error: Argument of type '"up"' is not assignable to parameter of type 'Direction'
String literal types are often used to define limited sets of strings, such as API endpoints or status codes.
Numeric Literal Types
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
function rollDice(): DiceRoll {
return Math.floor(Math.random() * 6) + 1 as DiceRoll;
}
const result: DiceRoll = rollDice();
console.log(`You rolled a ${result}`);
Numeric literal types are suitable for scenarios with fixed numerical ranges, such as dice rolls or HTTP status codes.
Boolean Literal Types
type Yes = true;
type No = false;
function confirmAction(confirmation: Yes | No): void {
if (confirmation) {
console.log("Action confirmed");
} else {
console.log("Action cancelled");
}
}
confirmAction(true); // Correct
confirmAction(false); // Correct
confirmAction(1); // Error
Boolean literal types are less commonly used but can be useful in scenarios requiring strict true/false distinctions.
Union Types and Literals
Literal types are often combined with union types to create more precise type constraints:
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
function fetchData(method: HttpMethod, url: string) {
// Implement fetch logic
}
fetchData("GET", "/api/users"); // Correct
fetchData("OPTIONS", "/api/users"); // Error
This pattern is particularly common in Redux action type definitions:
type Action =
| { type: "ADD_TODO"; text: string }
| { type: "TOGGLE_TODO"; id: number }
| { type: "DELETE_TODO"; id: number };
function todoReducer(state: Todo[], action: Action): Todo[] {
switch (action.type) {
case "ADD_TODO":
return [...state, { id: Date.now(), text: action.text, completed: false }];
case "TOGGLE_TODO":
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case "DELETE_TODO":
return state.filter(todo => todo.id !== action.id);
default:
return state;
}
}
Type Inference and Literals
TypeScript can automatically infer literal types, but sometimes explicit specification is needed:
const name = "Alice"; // Inferred as "Alice" (literal type)
let age = 25; // Inferred as number
// Explicitly specifying literal types
const direction: "north" = "north";
Using the as const
assertion converts object or array properties into literal types:
const colors = ["red", "green", "blue"] as const;
// Type is readonly ["red", "green", "blue"]
const user = {
name: "Alice",
age: 25,
active: true
} as const;
// Type is { readonly name: "Alice"; readonly age: 25; readonly active: true; }
Template Literal Types
TypeScript 4.1 introduced template literal types, enabling the creation of more complex types based on string literals:
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiEndpoint = `/api/${string}`;
function callApi(method: HttpMethod, endpoint: ApiEndpoint) {
// Implement API call
}
callApi("GET", "/api/users"); // Correct
callApi("POST", "/data/products"); // Error: Does not start with /api/
Template literal types can be combined with conditional types to create powerful type utilities:
type GetterName<T extends string> = `get${Capitalize<T>}`;
type NameGetter = GetterName<"name">; // "getName"
type AgeGetter = GetterName<"age">; // "getAge"
function createGetter<T extends string>(property: T): GetterName<T> {
return `get${property.charAt(0).toUpperCase() + property.slice(1)}` as GetterName<T>;
}
const getName = createGetter("name"); // Type is "getName"
const getAge = createGetter("age"); // Type is "getAge"
Enums and Literal Types
Enums and literal union types can sometimes be used interchangeably, but each has its pros and cons:
// Using enums
enum Direction {
North = "NORTH",
South = "SOUTH",
East = "EAST",
West = "WEST"
}
// Using literal union types
type Direction = "NORTH" | "SOUTH" | "EAST" | "WEST";
// Enums provide reverse mapping
console.log(Direction.North); // "NORTH"
console.log(Direction["NORTH"]); // "North"
// Literal types are lighter but require manual value maintenance
const directions = {
North: "NORTH",
South: "SOUTH",
East: "EAST",
West: "WEST"
} as const;
Performance Considerations
Literal types are erased at compile time and do not affect runtime performance. However, extensive use of complex union literal types may increase compilation time:
// Simple literal union - Fast compilation
type SmallUnion = "A" | "B" | "C";
// Complex literal union - May increase compilation time
type LargeUnion =
| "A1" | "A2" | "A3" | "A4" | "A5"
| "B1" | "B2" | "B3" | "B4" | "B5"
| "C1" | "C2" | "C3" | "C4" | "C5"
// ...More literals
| "Z99";
Type Guards and Literals
Type guards are particularly useful when working with literal types:
type Result = { status: "success"; data: string } | { status: "error"; message: string };
function handleResult(result: Result) {
if (result.status === "success") {
console.log(result.data); // Here, result is inferred as { status: "success"; data: string }
} else {
console.error(result.message); // Here, result is inferred as { status: "error"; message: string }
}
}
Application in Configuration Patterns
Literal types are well-suited for defining configuration object types:
type LogLevel = "debug" | "info" | "warn" | "error";
interface LoggerConfig {
level: LogLevel;
format: "json" | "text";
timestamp: boolean;
}
function createLogger(config: LoggerConfig) {
// Implement logger creation logic
}
createLogger({
level: "info",
format: "json",
timestamp: true
}); // Correct
createLogger({
level: "verbose", // Error: Not a valid LogLevel
format: "yaml", // Error: Not a valid format
timestamp: "yes" // Error: Should be boolean
});
Combining with Generics
Literal types can be combined with generics to create flexible APIs:
type EventMap = {
click: { x: number; y: number };
keypress: { key: string; code: number };
scroll: { position: number };
};
function addEventListener<T extends keyof EventMap>(
event: T,
handler: (payload: EventMap[T]) => void
) {
// Implement event listening
}
addEventListener("click", (payload) => {
console.log(payload.x, payload.y); // payload is correctly inferred as { x: number; y: number }
});
addEventListener("keypress", (payload) => {
console.log(payload.key); // payload is correctly inferred as { key: string; code: number }
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:热力图(Heatmap)实现
下一篇:数组与元组类型