阿里云主机折上折
  • 微信号
Current Site:Index > Type parameter constraints

Type parameter constraints

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

Basic Concepts of Type Parameter Constraints

TypeScript's type parameter constraints allow us to limit the range of generic type parameters. By using the extends keyword after a generic type parameter, we can specify the conditions that the type parameter must satisfy. This mechanism ensures type safety while maintaining the flexibility of generics.

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

Single Constraint vs. Multiple Constraints

Type parameters can be constrained by a single interface or type, or multiple constraints can be achieved through union types. When multiple conditions need to be satisfied, intersection types can be used.

// Single constraint
function process<T extends string>(value: T): void {
  console.log(value.toUpperCase());
}

// Multiple constraints
interface Serializable {
  serialize(): string;
}

interface Identifiable {
  id: number;
}

function persist<T extends Serializable & Identifiable>(item: T): void {
  const serialized = item.serialize();
  localStorage.setItem(`item_${item.id}`, serialized);
}

Combining Constraints with Default Types

Type parameter constraints can be combined with default types, which is particularly useful when creating flexible and type-safe APIs.

interface PaginationOptions<T = any> {
  pageSize?: number;
  currentPage?: number;
  filter?: T extends object ? Partial<T> : never;
}

function paginate<T extends object = {}>(options: PaginationOptions<T> = {}) {
  // Implementation logic
}

Property Constraints Using keyof

The keyof operator is often used with type constraints to ensure only existing properties of an object can be accessed.

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person = { name: "Alice", age: 30 };
getProperty(person, "name"); // Correct
getProperty(person, "gender"); // Error: gender is not a property of person

Constructor Constraints

Using the new constraint, we can restrict type parameters to be constructor functions, which is particularly useful in factory patterns.

interface Constructor<T> {
  new (...args: any[]): T;
}

function createInstance<T>(ctor: Constructor<T>, ...args: any[]): T {
  return new ctor(...args);
}

class Person {
  constructor(public name: string) {}
}

const alice = createInstance(Person, "Alice");

Recursive Type Constraints

Type constraints can be used to define recursive types, which is useful when working with tree structures or nested data.

interface TreeNode<T> {
  value: T;
  children?: TreeNode<T>[];
}

function walkTree<T>(node: TreeNode<T>, callback: (value: T) => void) {
  callback(node.value);
  node.children?.forEach(child => walkTree(child, callback));
}

Constraints in Conditional Types

Constraints can be used in conditional types to create more complex type logic.

type NonNullablePropertyKeys<T> = {
  [K in keyof T]: null extends T[K] ? never : K
}[keyof T];

interface User {
  name: string;
  age: number | null;
  email: string | null;
}

type RequiredUserKeys = NonNullablePropertyKeys<User>; // "name"

Constraints with Mapped Types

Combined with mapped types, constraints can be used to transform the properties of object types.

type ReadonlyProps<T, K extends keyof T> = {
  readonly [P in K]: T[P];
} & {
  [P in Exclude<keyof T, K>]: T[P];
};

interface Config {
  apiUrl: string;
  timeout: number;
  retries: number;
}

type ReadonlyConfig = ReadonlyProps<Config, "apiUrl">;

Advanced Constraints in Generic Functions

Complex constraints can be used to create highly type-safe utility functions.

function mergeObjects<T extends object, U extends object>(
  obj1: T,
  obj2: U
): T & U {
  return { ...obj1, ...obj2 };
}

const merged = mergeObjects({ a: 1 }, { b: 2 }); // { a: 1, b: 2 }

Type Constraints and Function Overloading

Type constraints can be combined with function overloading to provide precise type inference for different input types.

function formatInput<T extends string | number>(input: T): string;
function formatInput(input: boolean): "yes" | "no";
function formatInput(input: any): string {
  if (typeof input === "boolean") {
    return input ? "yes" : "no";
  }
  return String(input);
}

Constraints in React Components

In React components, type constraints can ensure the correctness of props.

interface BaseProps {
  className?: string;
  style?: React.CSSProperties;
}

function withCustomProps<T extends BaseProps>(
  Component: React.ComponentType<T>
) {
  return (props: T) => <Component {...props} />;
}

Type Constraints and Index Signatures

Constraints can be used to limit the key types of index signatures.

type StringMap<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface MixedData {
  id: string;
  count: number;
  name: string;
  timestamp: Date;
}

type StringOnly = StringMap<MixedData>; // { id: string; name: string }

Constraints Combined with Promises

In asynchronous programming, constraints can ensure the type of Promise resolution values.

async function fetchData<T extends { id: string }>(url: string): Promise<T[]> {
  const response = await fetch(url);
  return response.json();
}

interface Product {
  id: string;
  name: string;
  price: number;
}

fetchData<Product>("/api/products"); // Returns Promise<Product[]>

Performance Considerations for Type Constraints

While type constraints provide safety, overly complex constraints may impact compilation performance. For large projects, a balance between type safety and compilation speed is needed.

// Simple constraints compile faster
function simpleConstraint<T extends number | string>(value: T) {}

// Complex constraints may require more compilation time
type ComplexConstraint<T> = T extends infer U 
  ? U extends object 
    ? keyof U extends string 
      ? { [K in keyof U]: U[K] } 
      : never 
    : U 
  : never;

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

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