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 <natemoo-re@users.noreply.github.com>
This commit is contained in:
parent
57e9a28063
commit
ffc9e2d3de
5 changed files with 53 additions and 13 deletions
5
.changeset/shiny-dryers-swim.md
Normal file
5
.changeset/shiny-dryers-swim.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Paginate will now return exact types instead of a naive Record
|
2
packages/astro/client.d.ts
vendored
2
packages/astro/client.d.ts
vendored
|
@ -57,7 +57,7 @@ declare module 'astro:assets' {
|
|||
};
|
||||
|
||||
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
||||
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] };
|
||||
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
||||
type ImgAttributes = WithRequired<
|
||||
Omit<import('./types').HTMLAttributes<'img'>, 'src' | 'width' | 'height'>,
|
||||
'alt'
|
||||
|
|
|
@ -1600,7 +1600,9 @@ export type GetStaticPaths = (
|
|||
* const { slug } = Astro.params as Params;
|
||||
* ```
|
||||
*/
|
||||
export type InferGetStaticParamsType<T> = T extends () => infer R | Promise<infer R>
|
||||
export type InferGetStaticParamsType<T> = T extends (
|
||||
opts?: GetStaticPathsOptions
|
||||
) => infer R | Promise<infer R>
|
||||
? R extends Array<infer U>
|
||||
? U extends { params: infer P }
|
||||
? P
|
||||
|
@ -1631,7 +1633,9 @@ export type InferGetStaticParamsType<T> = T extends () => infer R | Promise<infe
|
|||
* const { propA, propB } = Astro.props;
|
||||
* ```
|
||||
*/
|
||||
export type InferGetStaticPropsType<T> = T extends () => infer R | Promise<infer R>
|
||||
export type InferGetStaticPropsType<T> = T extends (
|
||||
opts: GetStaticPathsOptions
|
||||
) => infer R | Promise<infer R>
|
||||
? R extends Array<infer U>
|
||||
? U extends { props: infer P }
|
||||
? P
|
||||
|
@ -1678,13 +1682,13 @@ export type MarkdownContent<T extends Record<string, any> = Record<string, any>>
|
|||
*
|
||||
* [Astro reference](https://docs.astro.build/en/reference/api-reference/#paginate)
|
||||
*/
|
||||
export interface PaginateOptions {
|
||||
export interface PaginateOptions<PaginateProps extends Props, PaginateParams extends Params> {
|
||||
/** the number of items per-page (default: `10`) */
|
||||
pageSize?: number;
|
||||
/** key: value object of page params (ex: `{ tag: 'javascript' }`) */
|
||||
params?: Params;
|
||||
params?: PaginateParams;
|
||||
/** object of props to forward to `page` result */
|
||||
props?: Props;
|
||||
props?: PaginateProps;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1718,7 +1722,33 @@ export interface Page<T = any> {
|
|||
};
|
||||
}
|
||||
|
||||
export type PaginateFunction = (data: any[], args?: PaginateOptions) => GetStaticPathsResult;
|
||||
type OmitIndexSignature<ObjectType> = {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
|
||||
? never
|
||||
: KeyType]: ObjectType[KeyType];
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
||||
export type PaginateFunction = <
|
||||
PaginateData,
|
||||
AdditionalPaginateProps extends Props,
|
||||
AdditionalPaginateParams extends Params,
|
||||
>(
|
||||
data: PaginateData[],
|
||||
args?: PaginateOptions<AdditionalPaginateProps, AdditionalPaginateParams>
|
||||
) => {
|
||||
params: Simplify<
|
||||
{
|
||||
page: string | undefined;
|
||||
} & OmitIndexSignature<AdditionalPaginateParams>
|
||||
>;
|
||||
props: Simplify<
|
||||
{
|
||||
page: Page<PaginateData>;
|
||||
} & OmitIndexSignature<AdditionalPaginateProps>
|
||||
>;
|
||||
}[];
|
||||
|
||||
export type Params = Record<string, string | undefined>;
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import type {
|
||||
GetStaticPathsResult,
|
||||
Page,
|
||||
PaginateFunction,
|
||||
PaginateOptions,
|
||||
Params,
|
||||
Props,
|
||||
RouteData,
|
||||
} from '../../@types/astro';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
|
||||
export function generatePaginateFunction(routeMatch: RouteData): PaginateFunction {
|
||||
export function generatePaginateFunction(
|
||||
routeMatch: RouteData
|
||||
): (...args: Parameters<PaginateFunction>) => ReturnType<PaginateFunction> {
|
||||
return function paginateUtility(
|
||||
data: any[],
|
||||
args: { pageSize?: number; params?: Params; props?: Props } = {}
|
||||
) {
|
||||
args: PaginateOptions<Props, Params> = {}
|
||||
): ReturnType<PaginateFunction> {
|
||||
let { pageSize: _pageSize, params: _params, props: _props } = args;
|
||||
const pageSize = _pageSize || 10;
|
||||
const paramName = 'page';
|
||||
|
@ -31,7 +33,7 @@ export function generatePaginateFunction(routeMatch: RouteData): PaginateFunctio
|
|||
}
|
||||
const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
|
||||
|
||||
const result: GetStaticPathsResult = [...Array(lastPage).keys()].map((num) => {
|
||||
const result = [...Array(lastPage).keys()].map((num) => {
|
||||
const pageNum = num + 1;
|
||||
const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize; // currentPage is 1-indexed
|
||||
const end = Math.min(start + pageSize, data.length);
|
||||
|
|
|
@ -3,6 +3,7 @@ import type {
|
|||
GetStaticPathsItem,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPathsResultKeyed,
|
||||
PaginateFunction,
|
||||
Params,
|
||||
RouteData,
|
||||
RuntimeMode,
|
||||
|
@ -50,7 +51,9 @@ export async function callGetStaticPaths({
|
|||
// Calculate your static paths.
|
||||
let staticPaths: GetStaticPathsResult = [];
|
||||
staticPaths = await mod.getStaticPaths({
|
||||
paginate: generatePaginateFunction(route),
|
||||
// Q: Why the cast?
|
||||
// A: So users downstream can have nicer typings, we have to make some sacrifice in our internal typings, which necessitate a cast here
|
||||
paginate: generatePaginateFunction(route) as PaginateFunction,
|
||||
rss() {
|
||||
throw new AstroError(AstroErrorData.GetStaticPathsRemovedRSSHelper);
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue