TypeScript 泛型 <T> 详细解释

什么是泛型

  • T 是一个类型变量,代表"某种类型"

举个例子

  • loadMore: (page: number) => Promise<T[]>;
  • 相当于说:这个函数返回一个Promise,Promise解析的结果是T类型的数组

实际使用时的具体化

// 当 T = Product 时:
loadMore: (page: number) => Promise<Product[]>;

// 当 T = string 时:
loadMore: (page: number) => Promise<string[]>;

// 当 T = User 时:
loadMore: (page: number) => Promise<User[]>;

在组件中的完整应用

// 定义泛型组件
interface InfiniteScrollProps<T = any> {
  loadMore: (page: number) => Promise<T[]>;  // T 在这里定义
  renderItem: (item: T, index: number) => React.ReactNode;  // 这里也用 T
}

// 使用泛型组件时,T 会被具体类型替换
function ProductList() {
  return (
    <InfiniteScroll<Product>  // 这里明确指定 T = Product
      loadMore={fetchProducts}  // 现在这个函数需要返回 Promise<Product[]>
      renderItem={(product) => (  // 这里的 product 自动就是 Product 类型
        <div>{product.name} - ${product.price}</div>
      )}
    />
  );
}

举个例子

用户数据

// 定义用户类型
interface User {
  id: number;
  name: string;
  email: string;
  avatar: string;
}

function UserList() {
  // 加载用户数据的函数 - 必须返回 User[]
  const loadUsers = async (page: number): Promise<User[]> => {
    const response = await fetch(`/api/users?page=${page}`);
    const data = await response.json();
    return data.users; // 这里必须是 User[] 类型
  };

  // 渲染用户项 - item 自动推断为 User 类型
  const renderUser = (user: User) => (
    <div className="user-card">
      <img src={user.avatar} alt={user.name} />
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );

  return (
    <InfiniteScroll<User>  // 明确指定类型
      loadMore={loadUsers}
      renderItem={renderUser}
    />
  );
}

文章数据

// 定义文章类型
interface Article {
  id: string;
  title: string;
  content: string;
  author: string;
  createdAt: Date;
}

function ArticleList() {
  const loadArticles = async (page: number): Promise<Article[]> => {
    const response = await fetch(`/api/articles?page=${page}`);
    return response.json(); // 返回 Article[]
  };

  const renderArticle = (article: Article) => (
    <article>
      <h2>{article.title}</h2>
      <p>By {article.author}</p>
      <div>{article.content}</div>
    </article>
  );

  return (
    <InfiniteScroll<Article>
      loadMore={loadArticles}
      renderItem={renderArticle}
    />
  );
}

类型自动推断

  • 如果不显式指定 ,TypeScript 会尝试自动推断:
function Example() {
  // TypeScript 会根据 loadMore 的返回值推断 T = Product
  const loadMore = async (page: number): Promise<Product[]> => {
    // ...
  };

  // 这里不需要显式写 <Product>
  return (
    <InfiniteScroll
      loadMore={loadMore}  // TypeScript 知道这里 T = Product
      renderItem={(product) => {  // product 自动就是 Product 类型
        product.id;    // ✅ 正确
        product.name;  // ✅ 正确  
        product.abc;   // ❌ 错误:Product 类型没有 abc 属性
      }}
    />
  );
}

泛型的优势

类型安全

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

// 如果 loadMore 返回了错误类型,TypeScript 会报错
const loadMore = async (page: number): Promise<Product[]> => {
  return [
    { id: 1, name: "Phone", price: 999 },
    { id: 2, name: "Tablet", price: 699 },
    { id: 3, name: "Laptop" } 
    // ❌ 错误:缺少 price 属性
  ];
};

智能提示

const renderItem = (product: Product) => {
  // 编辑器会提供 Product 的所有属性提示:
  product. // 输入 . 后会提示:id, name, price
};

复杂类型示例

嵌套对象

interface Post {
  id: number;
  title: string;
  author: {
    id: number;
    name: string;
    avatar: string;
  };
  tags: string[];
}

function PostList() {
  const loadMore = async (page: number): Promise<Post[]> => {
    // 必须返回 Post[] 类型
  };

  const renderItem = (post: Post) => (
    <div>
      <h3>{post.title}</h3>
      <img src={post.author.avatar} alt={post.author.name} />
      <div>
        {post.tags.map(tag => (
          <span key={tag}>{tag}</span>
        ))}
      </div>
    </div>
  );

  return (
    <InfiniteScroll<Post>
      loadMore={loadMore}
      renderItem={renderItem}
    />
  );
}

默认类型

  • 在类型定义中,我们可以给泛型一个默认类型:
// T = any 表示如果不指定类型,就使用 any
interface InfiniteScrollProps<T = any> {
  loadMore: (page: number) => Promise<T[]>;
  renderItem: (item: T, index: number) => React.ReactNode;
}

// 这样使用时,T 就是 any 类型(不推荐,失去类型安全)
function AnyList() {
  return (
    <InfiniteScroll  // 没有指定 <T>
      loadMore={async (page) => [{ anything: 'here' }]}  // 返回 any[]
      renderItem={(item) => (
        <div>
          {item.anything}     // ✅ 不会报错,但也没有类型提示
          {item.something}    // ✅ 也不会报错
        </div>
      )}
    />
  );
}

总结

  • 是 TypeScript 的泛型参数,代表"某种类型"
  • T[] 表示"T类型的数组"
  • Promise<T[]> 表示"异步返回T类型数组的Promise"
  • 使用泛型让组件类型安全且灵活
  • 在实际使用时,T 会被具体的类型(如 Product, User 等)替换
  • 这样设计的好处是:一次编写,多种类型复用,同时保持完整的类型检查和智能提示。
posted @ 2025-09-30 23:59  HuangBingQuan  阅读(9)  评论(0)    收藏  举报