Custom type declaration
TypeScript's custom type declarations are core tools for enhancing code readability and maintainability. Through mechanisms like type aliases, interfaces, and union types, developers can precisely describe data structures, constrain variable shapes, and even extend type definitions for third-party libraries.
Type Aliases and Basic Declarations
Type aliases (type
) allow creating concise names for complex types. They support primitive types, union types, tuples, and any other valid TypeScript type:
type UserID = string | number; // Union type
type Point = [number, number]; // Tuple type
type Callback<T> = (data: T) => void; // Generic function type
Unlike interfaces, type aliases can directly define union types or use template literal types:
type Status = 'active' | 'inactive'; // Literal union
type EventName = `${'click' | 'hover'}_${'menu' | 'button'}`; // Template literal
Advanced Applications of Interfaces
Interfaces (interface
) are particularly suited for describing object structures and support inheritance, declaration merging, and other features:
interface User {
id: number;
name: string;
readonly registerDate: Date; // Read-only property
}
// Interface inheritance
interface Admin extends User {
permissions: string[];
}
// Declaration merging
interface User {
email?: string; // Optional property
}
Dynamic properties can be defined using index signatures:
interface StringMap {
[key: string]: string; // Arbitrary string properties
}
Practical Use of Union and Intersection Types
Union types (|
) represent "or" relationships, while intersection types (&
) represent "and" relationships:
type NetworkState =
| { status: 'loading' }
| { status: 'success', data: string }
| { status: 'error', code: number };
type Draggable = {
drag: () => void
};
type Resizable = {
resize: () => void
};
type UIWidget = Draggable & Resizable;
Type Guards and Type Inference
Type guards can narrow the scope of union types:
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function process(input: string | number) {
if (isString(input)) {
input.toUpperCase(); // Type inferred as string
}
}
The typeof
, instanceof
, and in
operators can all serve as type guards:
class Bird {
fly() {}
}
class Fish {
swim() {}
}
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
Generic Constraints and Advanced Patterns
Generics can be combined with constraints for flexible type control:
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
// Conditional type example
type Flatten<T> = T extends Array<infer U> ? U : T;
type Nested = Array<string>;
type Flat = Flatten<Nested>; // string
Mapped types can transform properties in bulk:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
Declaration Files and Module Augmentation
Adding type declarations for third-party libraries:
// globals.d.ts
declare module '*.svg' {
const content: string;
export default content;
}
// Extending existing modules
declare module 'axios' {
interface AxiosRequestConfig {
retry?: number;
}
}
Ambient declarations allow describing non-TypeScript code:
declare const __VERSION__: string;
declare function showToast(message: string): void;
Utility Types Explained
TypeScript includes built-in utility types:
interface Todo {
title: string;
description: string;
completed: boolean;
}
// Construct new types
type TodoPreview = Pick<Todo, 'title' | 'completed'>;
type TodoInfo = Omit<Todo, 'completed'>;
// Modify properties
type ReadonlyTodo = Readonly<Todo>;
type PartialTodo = Partial<Todo>;
Custom utility type examples:
type Nullable<T> = T | null;
type ValueOf<T> = T[keyof T];
type AsyncReturnType<T> = T extends (...args: any[]) => Promise<infer R> ? R : never;
Type Compatibility and Structural Subtyping
TypeScript uses a structural type system:
interface Named {
name: string;
}
class Person {
constructor(public name: string) {}
}
let p: Named;
p = new Person('Alice'); // Compatible because structures match
Function parameter compatibility follows bivariance:
type Handler = (event: Event) => void;
const clickHandler: Handler = (e: MouseEvent) => {}; // Allowed
const genericHandler = (e: Event) => {};
const mouseHandler: (e: MouseEvent) => void = genericHandler; // Also allowed
Decorators and Reflection Metadata
Combining decorators for type metaprogramming:
import 'reflect-metadata';
function validate(target: any, key: string) {
const type = Reflect.getMetadata('design:type', target, key);
console.log(`${key} type: ${type.name}`);
}
class User {
@validate
name: string; // Output: name type: String
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn