Skip to content

Commit ffc9e2d

Browse files
feat(paginate): Return exact types from paginate() (#8229)
* feat(paginate): Return exact types from paginate() * chore: changeset * fix(types): Fix infer utils while I'm here --------- Co-authored-by: Nate Moore <[email protected]>
1 parent 57e9a28 commit ffc9e2d

File tree

5 files changed

+53
-13
lines changed

5 files changed

+53
-13
lines changed

.changeset/shiny-dryers-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Paginate will now return exact types instead of a naive Record

packages/astro/client.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ declare module 'astro:assets' {
5757
};
5858

5959
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
60-
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] };
60+
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
6161
type ImgAttributes = WithRequired<
6262
Omit<import('./types').HTMLAttributes<'img'>, 'src' | 'width' | 'height'>,
6363
'alt'

packages/astro/src/@types/astro.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,9 @@ export type GetStaticPaths = (
16001600
* const { slug } = Astro.params as Params;
16011601
* ```
16021602
*/
1603-
export type InferGetStaticParamsType<T> = T extends () => infer R | Promise<infer R>
1603+
export type InferGetStaticParamsType<T> = T extends (
1604+
opts?: GetStaticPathsOptions
1605+
) => infer R | Promise<infer R>
16041606
? R extends Array<infer U>
16051607
? U extends { params: infer P }
16061608
? P
@@ -1631,7 +1633,9 @@ export type InferGetStaticParamsType<T> = T extends () => infer R | Promise<infe
16311633
* const { propA, propB } = Astro.props;
16321634
* ```
16331635
*/
1634-
export type InferGetStaticPropsType<T> = T extends () => infer R | Promise<infer R>
1636+
export type InferGetStaticPropsType<T> = T extends (
1637+
opts: GetStaticPathsOptions
1638+
) => infer R | Promise<infer R>
16351639
? R extends Array<infer U>
16361640
? U extends { props: infer P }
16371641
? P
@@ -1678,13 +1682,13 @@ export type MarkdownContent<T extends Record<string, any> = Record<string, any>>
16781682
*
16791683
* [Astro reference](https://docs.astro.build/en/reference/api-reference/#paginate)
16801684
*/
1681-
export interface PaginateOptions {
1685+
export interface PaginateOptions<PaginateProps extends Props, PaginateParams extends Params> {
16821686
/** the number of items per-page (default: `10`) */
16831687
pageSize?: number;
16841688
/** key: value object of page params (ex: `{ tag: 'javascript' }`) */
1685-
params?: Params;
1689+
params?: PaginateParams;
16861690
/** object of props to forward to `page` result */
1687-
props?: Props;
1691+
props?: PaginateProps;
16881692
}
16891693

16901694
/**
@@ -1718,7 +1722,33 @@ export interface Page<T = any> {
17181722
};
17191723
}
17201724

1721-
export type PaginateFunction = (data: any[], args?: PaginateOptions) => GetStaticPathsResult;
1725+
type OmitIndexSignature<ObjectType> = {
1726+
// eslint-disable-next-line @typescript-eslint/ban-types
1727+
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
1728+
? never
1729+
: KeyType]: ObjectType[KeyType];
1730+
};
1731+
// eslint-disable-next-line @typescript-eslint/ban-types
1732+
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
1733+
export type PaginateFunction = <
1734+
PaginateData,
1735+
AdditionalPaginateProps extends Props,
1736+
AdditionalPaginateParams extends Params,
1737+
>(
1738+
data: PaginateData[],
1739+
args?: PaginateOptions<AdditionalPaginateProps, AdditionalPaginateParams>
1740+
) => {
1741+
params: Simplify<
1742+
{
1743+
page: string | undefined;
1744+
} & OmitIndexSignature<AdditionalPaginateParams>
1745+
>;
1746+
props: Simplify<
1747+
{
1748+
page: Page<PaginateData>;
1749+
} & OmitIndexSignature<AdditionalPaginateProps>
1750+
>;
1751+
}[];
17221752

17231753
export type Params = Record<string, string | undefined>;
17241754

packages/astro/src/core/render/paginate.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import type {
2-
GetStaticPathsResult,
32
Page,
43
PaginateFunction,
4+
PaginateOptions,
55
Params,
66
Props,
77
RouteData,
88
} from '../../@types/astro';
99
import { AstroError, AstroErrorData } from '../errors/index.js';
1010

11-
export function generatePaginateFunction(routeMatch: RouteData): PaginateFunction {
11+
export function generatePaginateFunction(
12+
routeMatch: RouteData
13+
): (...args: Parameters<PaginateFunction>) => ReturnType<PaginateFunction> {
1214
return function paginateUtility(
1315
data: any[],
14-
args: { pageSize?: number; params?: Params; props?: Props } = {}
15-
) {
16+
args: PaginateOptions<Props, Params> = {}
17+
): ReturnType<PaginateFunction> {
1618
let { pageSize: _pageSize, params: _params, props: _props } = args;
1719
const pageSize = _pageSize || 10;
1820
const paramName = 'page';
@@ -31,7 +33,7 @@ export function generatePaginateFunction(routeMatch: RouteData): PaginateFunctio
3133
}
3234
const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
3335

34-
const result: GetStaticPathsResult = [...Array(lastPage).keys()].map((num) => {
36+
const result = [...Array(lastPage).keys()].map((num) => {
3537
const pageNum = num + 1;
3638
const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize; // currentPage is 1-indexed
3739
const end = Math.min(start + pageSize, data.length);

packages/astro/src/core/render/route-cache.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
GetStaticPathsItem,
44
GetStaticPathsResult,
55
GetStaticPathsResultKeyed,
6+
PaginateFunction,
67
Params,
78
RouteData,
89
RuntimeMode,
@@ -50,7 +51,9 @@ export async function callGetStaticPaths({
5051
// Calculate your static paths.
5152
let staticPaths: GetStaticPathsResult = [];
5253
staticPaths = await mod.getStaticPaths({
53-
paginate: generatePaginateFunction(route),
54+
// Q: Why the cast?
55+
// A: So users downstream can have nicer typings, we have to make some sacrifice in our internal typings, which necessitate a cast here
56+
paginate: generatePaginateFunction(route) as PaginateFunction,
5457
rss() {
5558
throw new AstroError(AstroErrorData.GetStaticPathsRemovedRSSHelper);
5659
},

0 commit comments

Comments
 (0)