Types
TypeScript type definitions exported by next-cool-cache
TypeScript Types
next-cool-cache exports several TypeScript types for advanced use cases and custom extensions.
Import
import type {
ParamsArray,
ParamsToObject,
IsLeaf,
ExtractParams,
LeafNode,
BranchNode,
BuildTree,
ScopedCache,
} from 'next-cool-cache';Parameter Types
ParamsArray
Represents an array of parameter names.
type ParamsArray = readonly string[];Usage:
// In schema definitions
const schema = {
byId: { _params: ['id'] as const satisfies ParamsArray },
byOwnerAndSlug: { _params: ['ownerId', 'slug'] as const satisfies ParamsArray },
} as const;ParamsToObject
Converts a params array type to an object type with string values.
type ParamsToObject<T extends ParamsArray> = {
[K in T[number]]: string;
};Examples:
type SingleParam = ParamsToObject<readonly ['id']>;
// Result: { id: string }
type MultipleParams = ParamsToObject<readonly ['ownerId', 'slug']>;
// Result: { ownerId: string; slug: string }
type NoParams = ParamsToObject<readonly []>;
// Result: {}Schema Type Utilities
IsLeaf
Determines if a schema node type is a leaf node.
type IsLeaf<T> = T extends Record<string, never>
? true
: keyof T extends '_params'
? true
: false;Rules:
{}(empty object) →true{ _params: [...] }→true{ _params: [...], list: {} }→false(has other keys){ list: {}, byId: {} }→false(has children)
Examples:
type Test1 = IsLeaf<{}>; // true
type Test2 = IsLeaf<{ _params: ['id'] }>; // true
type Test3 = IsLeaf<{ list: {} }>; // false
type Test4 = IsLeaf<{ _params: ['id']; list: {} }>; // falseExtractParams
Extracts the params array from a schema node type.
type ExtractParams<T> = T extends { _params: infer P extends ParamsArray }
? P
: readonly [];Examples:
type Params1 = ExtractParams<{ _params: ['id'] }>;
// Result: ['id']
type Params2 = ExtractParams<{ _params: ['a', 'b'] }>;
// Result: ['a', 'b']
type Params3 = ExtractParams<{}>;
// Result: readonly []Node Interfaces
LeafNode
Interface for leaf nodes in the cache tree.
interface LeafNode<P extends ParamsArray = readonly []> {
/** Register cache tags (call inside 'use cache' functions) */
cacheTag: P['length'] extends 0
? () => void
: (params: ParamsToObject<P>) => void;
/** Stale-while-revalidate invalidation */
revalidateTag: P['length'] extends 0
? () => void
: (params: ParamsToObject<P>) => void;
/** Expire immediately and fetch fresh */
updateTag: P['length'] extends 0
? () => void
: (params: ParamsToObject<P>) => void;
/** Raw path for debugging */
_path: string;
}Conditional Signatures:
- If
Pis empty (readonly []), methods take no arguments - If
Phas elements, methods require a params object
Examples:
// LeafNode with no params
const listNode: LeafNode<readonly []> = {
cacheTag: () => {},
revalidateTag: () => {},
updateTag: () => {},
_path: 'users/list',
};
listNode.cacheTag(); // No args
// LeafNode with params
const byIdNode: LeafNode<readonly ['id']> = {
cacheTag: (params) => {},
revalidateTag: (params) => {},
updateTag: (params) => {},
_path: 'users/byId',
};
byIdNode.cacheTag({ id: '123' }); // Requires paramsBranchNode
Interface for branch nodes in the cache tree.
interface BranchNode {
/** Invalidate entire subtree */
revalidateTag: () => void;
/** Update entire subtree */
updateTag: () => void;
/** Raw path for debugging */
_path: string;
}Note: Branch nodes never take parameters since they operate on entire subtrees.
Tree Types
BuildTree
Recursively builds the cache tree type from a schema type.
type BuildTree<T, Prefix extends string = ''> = {
[K in keyof T as K extends '_params' ? never : K]: IsLeaf<T[K]> extends true
? LeafNode<ExtractParams<T[K]>>
: BuildTree<T[K], `${Prefix}${K & string}/`> & BranchNode;
} & (Prefix extends '' ? object : BranchNode);How it works:
- Iterates over keys in
T, excluding_params - For each key, checks if the value is a leaf (
IsLeaf) - If leaf: creates
LeafNode<ExtractParams<...>> - If branch: recursively builds
BuildTreeand intersects withBranchNode - Non-root nodes are intersected with
BranchNodefor subtree operations
ScopedCache
The final cache type returned by createCache().
type ScopedCache<T, Scopes extends readonly string[]> = {
[S in Scopes[number]]: BuildTree<T> & BranchNode;
} & BuildTree<T> & BranchNode;Structure:
- One property per scope name, each containing
BuildTree<T> & BranchNode - Plus the root-level
BuildTree<T>for unscoped (cross-scope) access - Plus
BranchNodeat the root forrevalidateTag()andupdateTag()
Example:
const schema = {
users: {
list: {},
byId: { _params: ['id'] as const },
},
} as const;
const scopes = ['admin', 'public'] as const;
type MyCache = ScopedCache<typeof schema, typeof scopes>;
// MyCache has:
// - cache.admin.users.list (LeafNode<[]>)
// - cache.admin.users.byId (LeafNode<['id']>)
// - cache.admin.users (BranchNode)
// - cache.admin (BranchNode)
// - cache.public.users.list (LeafNode<[]>)
// - cache.public.users.byId (LeafNode<['id']>)
// - cache.public.users (BranchNode)
// - cache.public (BranchNode)
// - cache.users.list (LeafNode<[]>) - unscoped
// - cache.users.byId (LeafNode<['id']>) - unscoped
// - cache.users (BranchNode) - unscoped
// - cache.revalidateTag() - root BranchNode
// - cache.updateTag() - root BranchNodeUsing Types in Your Code
Extracting Types from Your Cache
const cache = createCache(schema, scopes);
// Get the full cache type
type MyCache = typeof cache;
// Get a specific node type
type UserByIdNode = typeof cache.admin.users.byId;
// UserByIdNode is LeafNode<readonly ['id']>Type-Safe Helper Functions
import type { LeafNode, ParamsToObject } from 'next-cool-cache';
// Helper that works with any leaf node
function invalidateLeaf<P extends readonly string[]>(
node: LeafNode<P>,
params: ParamsToObject<P>
) {
node.revalidateTag(params as any);
console.log(`Invalidated: ${node._path}`);
}
// Usage
invalidateLeaf(cache.admin.users.byId, { id: '123' });Typing Custom Cache Wrappers
import type { ScopedCache } from 'next-cool-cache';
function createAppCache<T extends Record<string, unknown>>(
schema: T
): ScopedCache<T, readonly ['admin', 'public']> {
return createCache(schema, ['admin', 'public'] as const);
}TypeScript Requirements
For full type safety:
-
Use
as conston schemas and scopes:const schema = { ... } as const; const scopes = ['admin', 'public'] as const; -
Enable strict mode in
tsconfig.json:{ "compilerOptions": { "strict": true } } -
TypeScript 5.0+ recommended for best inference
See Also
- createCache() - Using these types
- Schema Design - Defining schemas
- Cache Methods - Node method details