Extending interface Data for better user TypeScript experience
See original GitHub issueDescription
Since I’m currently using the JSX plugin in combination with TypeScript for creating my blog, after talking to @oscarotero we came up with the idea of extending the Data
interface with a PageData
interface. To make a better user experience it makes sense to include already known default properties of Lume types in that interface.
Example of TS errors, when no type assertions/definitions exist for [index: string]: unknown
.
(parameter) site: unknown
Object is of type 'unknown'.deno-ts(2571)
(parameter) title: unknown
Type 'unknown' is not assignable to type 'ComponentChildren'.deno-ts(2322)
This would reduce the needing of creating type assertions or type guards for those properties by the user.
Experience
While developing I was in need of the following missing but existing propperties in Data
:
- children:
JSX.IntrinsicElements
- comp:
any
sinceProxyComponents
did not work that great - paginate:
Paginator
- pagination:
?
- results:
unknown[]
(pain) - search:
Search
While developing as a workaround, I created a custom interface to extend Data
and also override Pages
and added the missing typings:
import type { Data, Helper, Page as BasePage } from "lume/core.ts";
// Create custom typings to override `PaginateResults` like described below
export interface PageData extends Data { ... }
export interface Page extends BasePage {
data: PageData;
}
export interface Helpers {
[key: string]: Helper;
}
// Now I could use inside my pages, templates and layouts (example of my layouts/posts.tsx):
import type { Helpers, Page, PageData } from "@types";
export default ({ comp, excerpt, title, results, pagination }: PageData, { url, date }: Helpers) => {
...
}
While I could easily use Search
and Paginator
, the following changes had to made on existing interfaces:
-
PaginateResults["pagination"]
had to be moved out ofPaginator
as it’s own interface -
ProxyComponents
some how needed a modification. I could not get into and usedany
to overcome the ts error:
any
Property 'navbar' does not exist on type 'ProxyComponents | ComponentFunction'.
Property 'navbar' does not exist on type 'ComponentFunction'.deno-ts(2339)
Suggestions for restructuring existing types
- Move
Pagination
object out ofPaginateResult
export interface Pagination {
/** The current page number */
page: number;
/** The total number of pages */
totalPages: number;
/** The total number of elements */
totalResults: number;
/** The url of the previous page */
previous: string | null;
/** The url of the next page */
next: string | null;
}
export interface PaginateResult<T = Page> {
/** The page url */
url: string;
/** The page elements */
results: T[];
/** The pagination info */
pagination: Pagination
/**
* (Maybe) important when defining `data.menu` or `data.type` inside `paginate()`
* without I get TS error:
* any
* Property 'menu' does not exist on type 'PaginateResult<Page>'.deno-ts(2339)
*/
[index: string]: unknown;
}
- I have no idea how to modify
ProxyComponents
orComponentFunction
to get rid of the error described above.
Suggestions for interface PageData
- Implementation of existing Lume types and default propperties
import type { Data, Page } from "lume/core.ts";
import type { MetaData } from "lume/plugins/metas.ts"
import type { Search } from "lume/plugins/search.ts";
// If restructuring would have been done already like descriped above
import type { Pagination, Paginator, PaginateResult } from "plugins/paginate.ts";
/**
* Like described in https://lume.land/plugins/search/#description
* Should be implemented as default
*/
export interface Menu {
title: string;
visible: boolean;
order: number;
}
export interface PageData extends Data {
/** The title of the page */
title?: string;
/**
* The JSX children/content
* In addition to make this work, we must modify all JSX plugins so they import `JSX` by default
*/
children?: JSX.IntrinsicElements;
/**
* The available components
* Maybe someone has an idea how to modify like described above to replace `any`
*/
comp?: any;
/** The menu for top level site navigation */
menu?: Menu;
/** The Metas plugin object */
metas?: MetaData;
/** The Pagination helper */
paginate?: Paginator;
/** The pagination info */
pagination?: Pagination;
/**
* The Paginator results
* Not sure this is best practice, but works.
*/
results?: PaginateResult[];
/** The Search helper */
search?: Search;
/**
* The `site` object from root `_data.{ts,yaml}` when not using Metas plugin
* Would be cool to integrate it somehow
* I set my object `as const` and imported it, then used `typeof SiteMeta`
*/
site?: any;
}
Conclusion
It would be awesome if we could restructure the interfaces like this. I’m not sure how many users are using JSX
with TypeScript in Lume, but I think this will be a great improvement, since I struggled with custom type guards and also type assertions and didn’t like them.
In addition it would be cool to have two more docs on the Lume page:
- Core > Navigation (which desrcribes the usage of
menu
, since it’s too deep nested/hidden at the moment) - Configuration > Typescript (a small guide on how to use with TypeScript, extend existing or implement custom typings)
@oscarotero, I appreciate that you take the time. Looking forward on your thoughts. If you need an example repo, let me know.
Issue Analytics
- State:
- Created a year ago
- Comments:9 (9 by maintainers)
Top GitHub Comments
After chatting in Discord, the Windows error is fixed. Preact will be added in 1.11.0.
You can run
deno upgrade --dev
to upgrade Lume to the latest development version (the last commit). An additional way is, in your local clone of Lume repo, rundeno task install
.