什么是泛型
举个例子
- 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 = ProductloadMore={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 = Productconst loadMore = async (page: number): Promise<Product[]> => {// ...};// 这里不需要显式写 <Product>return (<InfiniteScrollloadMore={loadMore} // TypeScript 知道这里 T = ProductrenderItem={(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 等)替换
- 这样设计的好处是:一次编写,多种类型复用,同时保持完整的类型检查和智能提示。