阿里云主机折上折
  • 微信号
Current Site:Index > Generic interface

Generic interface

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

Basic Concepts of Generic Interfaces

Generic interfaces allow us to create reusable components that can handle multiple types instead of a single type. In TypeScript, generic interfaces are implemented by using type parameters in the interface definition. Type parameters are declared in angle brackets after the interface name and can be used in the interface's properties and methods.

interface GenericIdentityFn<T> {
  (arg: T): T;
}

function identity<T>(arg: T): T {
  return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

Syntax Structure of Generic Interfaces

The syntax of generic interfaces is similar to that of regular interfaces, but type parameters are added after the interface name. Type parameters can be any valid identifier, typically using single uppercase letters such as T, U, V, etc. These type parameters can be used in various parts of the interface, including property types, method parameter types, and return types.

interface KeyValuePair<K, V> {
  key: K;
  value: V;
}

let pair1: KeyValuePair<number, string> = { key: 1, value: "one" };
let pair2: KeyValuePair<string, boolean> = { key: "isValid", value: true };

Generic Interfaces and Functions

Generic interfaces are often used to describe function types, enabling the creation of flexible function signatures. Function generic interfaces can specify relationships between parameter types and return types while maintaining type safety.

interface Transformer<T, U> {
  (input: T): U;
}

const stringToNumber: Transformer<string, number> = (str) => parseFloat(str);
const numberToString: Transformer<number, string> = (num) => num.toString();

Generic Interfaces and Classes

Classes can implement generic interfaces, which requires the class to either become a generic class or provide concrete types for the interface's type parameters. This pattern is particularly useful when creating reusable components.

interface Repository<T> {
  add(item: T): void;
  get(id: number): T;
  getAll(): T[];
}

class UserRepository implements Repository<User> {
  private users: User[] = [];
  
  add(user: User): void {
    this.users.push(user);
  }
  
  get(id: number): User {
    return this.users.find(user => user.id === id)!;
  }
  
  getAll(): User[] {
    return [...this.users];
  }
}

Constraints on Generic Interfaces

We can add constraints to generic parameters using the extends keyword, limiting the types that can be used. This ensures that type parameters have specific properties or methods while still maintaining flexibility.

interface Lengthwise {
  length: number;
}

interface SizedContainer<T extends Lengthwise> {
  contents: T;
  size(): number;
}

const stringContainer: SizedContainer<string> = {
  contents: "hello",
  size() {
    return this.contents.length;
  }
};

Default Types for Generic Interfaces

TypeScript allows specifying default types for generic parameters. When the interface is used without explicitly providing type parameters, the default type will be used.

interface PaginatedResponse<T = any> {
  data: T[];
  total: number;
  page: number;
  perPage: number;
}

const userResponse: PaginatedResponse<User> = {
  data: [{ id: 1, name: "Alice" }],
  total: 1,
  page: 1,
  perPage: 10
};

const anyResponse: PaginatedResponse = {
  data: [1, 2, 3],
  total: 3,
  page: 1,
  perPage: 10
};

Generic Interfaces and Index Signatures

Generic interfaces can be combined with index signatures to create flexible dictionary or mapping types. This pattern is particularly useful when working with dynamic key-value pairs.

interface Dictionary<T> {
  [key: string]: T;
}

const numberDict: Dictionary<number> = {
  "one": 1,
  "two": 2,
  "three": 3
};

const stringDict: Dictionary<string> = {
  "en": "English",
  "es": "Spanish",
  "fr": "French"
};

Advanced Usage of Generic Interfaces

Generic interfaces can be nested to create more complex type structures. This pattern is especially valuable when building large applications or libraries.

interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
}

interface UserProfile {
  id: number;
  name: string;
  email: string;
}

interface PaginatedApiResponse<T> extends ApiResponse<T[]> {
  page: number;
  totalPages: number;
}

const userResponse: ApiResponse<UserProfile> = {
  success: true,
  data: {
    id: 1,
    name: "John Doe",
    email: "john@example.com"
  }
};

const usersResponse: PaginatedApiResponse<UserProfile> = {
  success: true,
  data: [
    { id: 1, name: "John Doe", email: "john@example.com" },
    { id: 2, name: "Jane Smith", email: "jane@example.com" }
  ],
  page: 1,
  totalPages: 3
};

Generic Interfaces and Conditional Types

In TypeScript's advanced type system, generic interfaces can be combined with conditional types to create different type structures based on type parameters.

type CheckType<T> = T extends string ? "string" : "not string";

interface TypeChecker<T> {
  type: CheckType<T>;
  value: T;
}

const stringChecker: TypeChecker<string> = {
  type: "string",
  value: "hello"
};

const numberChecker: TypeChecker<number> = {
  type: "not string",
  value: 42
};

Generic Interfaces and Mapped Types

Mapped types can be combined with generic interfaces to dynamically transform properties of existing types. This technique is particularly useful when creating utility types.

interface ReadonlyWrapper<T> {
  readonly [P in keyof T]: T[P];
}

interface User {
  id: number;
  name: string;
  email: string;
}

const readonlyUser: ReadonlyWrapper<User> = {
  id: 1,
  name: "Alice",
  email: "alice@example.com"
};

// readonlyUser.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property

Practical Applications of Generic Interfaces in Projects

In real-world projects, generic interfaces are commonly used in API response handling, state management, data access layers, and other scenarios. They provide type safety while maintaining code flexibility.

// API response handling
interface ApiResponse<T> {
  status: number;
  data: T;
  message?: string;
}

// State management
interface State<T> {
  loading: boolean;
  error: string | null;
  data: T | null;
}

// Data access layer
interface DataAccess<T, ID> {
  create(item: T): Promise<T>;
  read(id: ID): Promise<T | null>;
  update(id: ID, item: T): Promise<boolean>;
  delete(id: ID): Promise<boolean>;
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.