npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tolki/types

v1.3.0

Published

Utility TypeScript types for @tolki packages and Laravel HTTP responses.

Readme

Tolki JS Types Package

Utility TypeScript types for @tolki packages and Laravel HTTP responses.

Documentation

The full documentation for the TypeScript type utilities can be found at https://tolki.abe.dev.

TypeScript Type Utilities Installation

The @tolki/types package provides a variety of TypeScript types inspired by Laravel's data structures such as pagination results and models. You can install it via npm, yarn, or pnpm:

npm install @tolki/types
yarn add @tolki/types
pnpm add @tolki/types

Importing the Package

Example usage of types in a Vue SFC setup script section:

<script lang="ts" setup>
import type { LengthAwarePaginator, TimestampModel } from "@tolki/types";

interface User extends TimestampModel {
  name: string;
  email: string;
}

const { users } = defineProps<{
  users: LengthAwarePaginator<User>;
}>();
</script>

See the TypeScript Types Documentation for a full list of available types and their descriptions.

Tolki TypeScript Type Utilities List

Paginator Utilities Types

Laravel provides 3 built in pagination modes. Each returns a similar but slightly different structured response. When you're working on the front end and using TypeScript, you'd have to write out what each response structure looks like for each pagination mode. These utility types provide you the type-safe representations of these responses, making it easier to define pagination data structures in a type safe manner.

LengthAwarePaginator SimplePaginator CursorPaginator Pagination Components

LengthAwarePaginator

Imagine you have a Laravel controller with an Inertia response of your users table paginated.

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Inertia\Inertia;
use Inertia\Response;

class UserController extends Controller
{
    public function index(): Response
    {
        return Inertia::render('Users', [
            'users' => User::query()->paginate(15),
        ]);
    }
}

On your TypeScript based front end, you can quickly define what the pagination response structure looks like by importing the LengthAwarePaginator type.

The LengthAwarePaginator type receives a single generic type parameter which represents the type of the individual items in the paginated data.

<script setup lang="ts">
import type { LengthAwarePaginator } from "@tolki/types";

interface User {
  id: number;
  name: string;
  email: string;
  created_at: string;
  updated_at: string;
}

defineProps<{
  users: LengthAwarePaginator<User>;
}>();
</script>

The example above is a script section of a Vue single file component, but it can be applied to React or Svelte just as easily.

With that definition above, you can now use the structure with type safety of the pagination result in your front-end.

Example in Vue template:

<template>
  <div>
    <table v-if="users.data.length > 0">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Email</th>
          <th>Created At</th>
          <th>Updated At</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="user in users.data" :key="user.id">
          <td>{{ user.id }}</td>
          <td>{{ user.name }}</td>
          <td>{{ user.email }}</td>
          <td>{{ user.created_at }}</td>
          <td>{{ user.updated_at }}</td>
        </tr>
      </tbody>
    </table>

    <div v-else>No users found.</div>

    <div>
      Showing results from {{ users.from }} to {{ users.to }} of
      {{ users.total }}
    </div>
  </div>
</template>

SimplePaginator

If you use the simplePaginate response function as show below, you can use the SimplePaginator type to define the response structure.

Example Laravel controller:

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Inertia\Inertia;
use Inertia\Response;

class UserController extends Controller
{
    public function index(): Response
    {
        return Inertia::render('Users', [
            'users' => User::query()->simplePaginate(15),
        ]);
    }
}

In your TypeScript based front end, you can use the SimplePaginator type, which also accepts a single generic parameter to define the data structure.

<script setup lang="ts">
import type { SimplePaginator } from "@tolki/types";

interface User {
  id: number;
  name: string;
  email: string;
  created_at: string;
  updated_at: string;
}

defineProps<{
  users: SimplePaginator<User>;
}>();
</script>

CursorPaginator

The last pagination result that Laravel provides is the cursor pagination. For that response structure you can use the CursorPaginator type.

Example Laravel controller:

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Inertia\Inertia;
use Inertia\Response;

class UserController extends Controller
{
    public function index(): Response
    {
        return Inertia::render('Users', [
            'users' => User::query()->cursorPaginate(15),
        ]);
    }
}

In your TypeScript based front end, you can now use the CursorPaginator type to define the response structure, which also accepts a single generic parameter to define the data structure.

<script setup lang="ts">
import type { CursorPaginator } from "@tolki/types";

interface User {
  id: number;
  name: string;
  email: string;
  created_at: string;
  updated_at: string;
}

defineProps<{
  users: CursorPaginator<User>;
}>();
</script>

Pagination components

You can also use the pagination types to write type safe pagination components for your front end UI. A rough incomplete pagination example that supports all three pagination response types would look something like this.

<script setup lang="ts" generic="T">
import type {
  CursorPaginator,
  LengthAwarePaginator,
  SimplePaginator,
} from "@tolki/types";

defineProps<{
  data: LengthAwarePaginator<T> | SimplePaginator<T> | CursorPaginator<T>;
}>;
</script>

<template>
  <div>
    <!-- pagination UI implementation -->
  </div>
</template>

Then you'd use your pagination component like this:

<script setup lang="ts">
import type { LengthAwarePaginator } from "@tolki/types";

interface User {
  /* .. */
}

defineProps<{
  users: LengthAwarePaginator<User>;
}>();
</script>

<template>
  <div>
    <!-- T is inferred as User -->
    <Pagination :data="users" />
  </div>
</template>

Model Utilities Types

Unless you're using a package like Model Typer or Laravel Wayfinder, you probably find yourself defining models manually on a TypeScript based Laravel project.

The purpose of the following model utility types is to simplify and standardize the way you define these models.

Model Types

You can use the simplest Model type which only defines a possibly undefined id key and any number of string keyed unknown values.

The Model type receives a generic parameter to define the type of ID it should be. With that defined, you can now use any number of columns on a User type, however, they'll simply register as unknown.

import type { Model } from "@tolki/types";

interface User extends Model<number> {
  title: string;
}

const user: User = getUser();

user.id; // inferred as number
user.title; // inferred as string
user.created_at; // inferred as unknown

If you'd like to auto define time stamp columns, you can use the TimestampModel type, the SoftDeleteModel, or the AllTimestampsModel type to define created_at, updated_at, and deleted_at in one go.

import type {
  TimestampModel,
  SoftDeleteModel,
  AllTimestampsModel,
} from "@tolki/types";

interface User extends TimestampModel<number> {
  title: string;
}

const user: User = getUser();

user.id; // inferred as number
user.title; // inferred as string
user.created_at; // inferred as string
user.updated_at; // inferred as string

interface Post extends SoftDeleteModel<string> {}

const post: Post = getPost();

post.id; // inferred as string
post.deleted_at; // inferred as string

interface Notification extends AllTimestampsModel<number> {
  message: string;
}

const notification: Notification = getNotification();

notification.id; // inferred as number
notification.message; // inferred as string
notification.created_at; // inferred as string
notification.updated_at; // inferred as string
notification.deleted_at; // inferred as string

Model Relationship Types

Many times when you load eloquent results, it is common to use relationship aggregation eloquent methods like count, max, min, sum, average or if a relationship exists using eloquent methods like withCount. Those methods create a dynamic column on an eloquent result.

For example, let's say you queried this:

Post::select(['title', 'body'])
    ->withCount('comments')
    ->get();

That creates the following result structure

interface Post {
    title: string;
    body: string;
    comments_count: number;
}

For these type of situations, you can use the following helpers to quickly define one or more relationships.

WithCount WithMax WithMin WithSum WithAvg WithExists

WithCount

import type { WithCount } from "@tolki/types";

interface Post extends WithCount<"comments" | "authors">{
    title: string;
    body: string;
}

const post: Post = getPost();

post.title // inferred as string
post.comments_count // inferred as number | null
post.authors_count // inferred as number | null

WithMax

import type { WithMax } from "@tolki/types";

interface Post extends WithMax<"comments" | "authors">{
    title: string;
    body: string;
}

const post: Post = getPost();

post.title // inferred as string
post.comments_max // inferred as number | null
post.authors_max // inferred as number | null

WithMin

import type { WithMin } from "@tolki/types";

interface Post extends WithMin<"comments" | "authors">{
    title: string;
    body: string;
}

const post: Post = getPost();

post.title // inferred as string
post.comments_min // inferred as number | null
post.authors_min // inferred as number | null

WithSum

import type { WithSum } from "@tolki/types";

interface Post extends WithSum<"comments" | "authors">{
    title: string;
    body: string;
}

const post: Post = getPost();

post.title // inferred as string
post.comments_sum // inferred as number | null
post.authors_sum // inferred as number | null

WithAvg

import type { WithAvg } from "@tolki/types";

interface Post extends WithAvg<"comments" | "authors">{
    title: string;
    body: string;
}

const post: Post = getPost();

post.title // inferred as string
post.comments_avg // inferred as number | null
post.authors_avg // inferred as number | null

WithExists

import type { WithExists } from "@tolki/types";

interface Post extends WithExists<"comments" | "authors">{
    title: string;
    body: string;
}

const post: Post = getPost();

post.title // inferred as string
post.comments_exists // inferred as boolean
post.authors_exists // inferred as boolean

JsonResource Utilities Types

When working with Laravel's JsonResource, you often need to define the structure of the JSON response in your TypeScript front end. By default, JSON resources either return a single resource object or a collection of resource objects, which do not have a different data structure from your models' properties.

However, when you paginate results in JSON sources, the structure changes. The following utility types help you define these structures in a type-safe manner.

For example, given this Laravel controller snippet:

use App\Http\Resources\UserCollection;
use App\Models\User;

Route::get('/users', function () {
    return new UserCollection(User::paginate());
});

The JSON structure returned will look like this:

{
  "data": [
    {
      "id": 1,
      "name": "Eladio Schroeder Sr.",
      "email": "[email protected]"
    },
    {
      "id": 2,
      "name": "Liliana Mayert",
      "email": "[email protected]"
    }
  ],
  "links": {
    "first": "http://example.com/users?page=1",
    "last": "http://example.com/users?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "http://example.com/users",
    "per_page": 15,
    "to": 10,
    "total": 10
  }
}

To define this structure in TypeScript, you can use the JsonResourcePaginator utility type as shown below:

import type { JsonResourcePaginator } from "@tolki/types";

interface User {
  id: number;
  name: string;
  email: string;
}

defineProps<{
  users: JsonResourcePaginator<User>;
}>();

That utility type accepts a single generic parameter which represents the type of the individual items in the paginated data.

The JsonResourcePaginator type itself uses two additional utility types: JsonResourceLinks and JsonResourceMeta, which represent the links and meta sections of the JSON response respectively.

export interface JsonResourcePaginator<T> {
  data: T[];
  meta: JsonResourceMeta;
  links: JsonResourceLinks;
}

If needed, you can also use these types individually to define the structure of the links and meta sections separately. You can also compose your own JSON resource structures if your PHP resources return custom structures.