Type definitions for Enhance
Required reading: Enhance with TypeScript
npm i -D @enhance/types
💁 The TypeScript examples are intentionally "over-typed" to demonstrate different uses. No need to declare a function's type and its call signature + return type.
Read more about Enhance API Routes, their request
object, and the expected response
to understand each interface's properties.
The simplest way to type an API middleware function is to use JSDoc's @type
and import "@enhance/types". Typically, the API handler functions should be marked aasync
.
You can also import the arugment (EnhanceApiReq
) and response (EnhanceApiRes
) types.
/**
* @type {import('@enhance/types').EnhanceApiFn}
*/
export async function get(request) {
console.log(`Handling ${request.path}...`);
const todos = [
{ title: "todo 1", completed: false },
{ title: "todo 2", completed: true },
{ title: "todo 3" },
];
const response = {
json: { todos },
};
return response;
}
Enhance API middlewares for named get
and post
functions are passed an EnhanceApiReq
object and expected to return a Promise
that resolves with an EnhanceApiRes
payload.
These types can be used independenty, or by simply typing each handler with EnhanceApiFn
.
import type {
EnhanceApiFn,
EnhanceApiReq,
EnhanceApiRes,
} from "@enhance/types";
type Todo = {
title: string;
completed?: boolean;
};
export const get: EnhanceApiFn = async function (
request: EnhanceApiReq,
): Promise<EnhanceApiRes> {
console.log(`Handling ${request.path}...`);
const todos: Todo[] = [
{ title: "todo 1", completed: false },
{ title: "todo 2", completed: true },
{ title: "todo 3" },
];
const response: EnhanceApiRes = {
json: { todos },
};
return response;
};
Single file components are the core of the Enhance developer experience.
Server rendered custom element functions receive EnhanceElemArg
with 2 keys:
html: EnhanceHtmlFn
to render HTML stringsstate: {attrs: object, store: object}
a record of the state used by Enhance
/**
* @type {import('@enhance/types').EnhanceElemFn}
*/
export default function TodoItem({
html,
state: { attrs }
}) {
const todoId = attrs["todo-id"];
const completed = typeof attrs.completed === "string";
return html`
<div class="flex gap-2 mb-1">
<input
type="checkbox"
name="completed"
${completed ? "checked" : ""}
/>
<slot></slot>
</div>
`;
}
Several type definitions are available for SSR custom elements. The simplest is to type your default export function as EnhanceElemFn
.
You probably shouldn't make a copy of html()
-- but its type, EnhanceHtmlFn
, is available if needed.
import type {
EnhanceElemArg,
EnhanceElemFn,
EnhanceHtmlFn,
EnhanceElemResult,
} from "@enhance/types";
const TodoItem: EnhanceElemFn = function ({
html,
state: { attrs },
}: EnhanceElemArg): EnhanceElemResult {
const todoId = attrs["todo-id"];
const completed = typeof attrs.completed === "string";
const myHtml: EnhanceHtmlFn = html;
return html`
<div class="flex gap-2 mb-1">
<input
type="checkbox"
name="completed"
${completed ? "checked" : ""}
/>
<slot></slot>
</div>
`;
};
export default TodoItem;
Customize your Enhance app by providing a Head function.
Utilize the EnhanceHeadFn
type to annotate your app/head.mjs file.
The function takes a EnhanceApiReq
object for access to things like the request path
and session
.
Return a standard EnhanceElemResult
(aka a string
of HTML).
/**
* @type {import('@enhance/types').EnhanceHeadFn}
*/
export default function Head(request) {
const { path } = request;
const title = `Todos — ${path}`;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
<link rel="stylesheet" href="/_static/styles.css">
</head>
`;
}
The EnhanceHeadFn
is similar to an API handler but returns a string of HTML as EnhanceElemResult
.
import type {
EnhanceApiReq,
EnhanceElemResult,
EnhanceHeadFn,
} from "@enhance/types";
const Head: EnhanceHeadFn = function (
request: EnhanceApiReq,
): EnhanceElemResult {
const { path } = request;
const title = `Todos — ${path}`;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
<link rel="stylesheet" href="/_static/styles.css">
</head>
`;
};
export default Head;