什么是泛型
举个例子
- 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 等)替换
- 这样设计的好处是:一次编写,多种类型复用,同时保持完整的类型检查和智能提示。