阿里云主机折上折
  • 微信号
Current Site:Index > Custom type declaration

Custom type declaration

Author:Chuan Chen 阅读数:36200人阅读 分类: TypeScript

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.