Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Typeable Laravel Package

kiwilan/laravel-typeable

Laravel Typeable adds polymorphic types to Eloquent models, letting you attach reusable Type records to any model through a typeables pivot. Includes migrations, a Type model, and a Typeable trait with helpers to sync, filter, and query types.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal steps

composer require kiwilan/laravel-typeable --dev
php artisan vendor:publish --tag="typeable-config"   # check `php artisan vendor:publish` output for the exact tag
php artisan list typeable                            # confirm the generator command name in your version
php artisan typeable:generate                        # writes .ts/.d.ts files

Then include the output directory in tsconfig.json (usually resources/js/types/**/*.ts) so editors and vue-tsc/tsc pick it up automatically.

Where to look first

  1. config/typeable.php — output paths, model namespaces, globs of what to include/exclude, Settings classes to scan.
  2. The generated files themselves — read them before writing any frontend code; they are the contract.
  3. composer.json of the package — confirms which Laravel/PHP versions and whether the optional Inertia NPM package is needed.

First use case

The highest-value first win is typing Inertia page props for a single resource you're actively building. Generate types, then:

import type { User } from '@/types/models'

defineProps<{ user: User; users: User[] }>()

You immediately get autocomplete on user.name, user.email, and a compile error the moment a migration adds/renames a column and you forget to update the frontend. That feedback loop — PHP change → regenerate → TypeScript error — is the entire reason to adopt the package.

Implementation Patterns

Regenerate as part of your dev loop, not as a one-off

Add it to composer.json scripts and a file watcher so types never drift:

"scripts": {
  "typeable": "@php artisan typeable:generate",
  "dev": ["@typeable", "vite"]
}

For long-running npm run dev sessions, either re-run the artisan command manually after model changes or wire a watcher (e.g. chokidar on app/Models + routes/) that shells out to the generator. Treat generated output the same way you'd treat any build artifact.

Model pattern: type at the boundary, not the ORM

Generated model types describe the serialized shape (attributes + casts + loaded relations) — which is exactly what crosses the wire. Use them for:

  • Inertia page props
  • API response typing in your fetch/axios wrappers
  • Form initial values (useForm<UserForm>({...}))

Don't try to model lazy-loaded relations in the frontend type; instead make the controller's eager-loading explicit and let the generated type reflect what you actually ->with().

Route pattern: kill hardcoded URL strings

The route typing is the sleeper feature. Named routes become typed helpers/constants, so a renamed route produces a TypeScript error instead of a 404 discovered in staging.

// instead of fetch(`/admin/users/${id}/edit`)
import { adminUsersEdit } from '@/types/routes'
fetch(adminUsersEdit({ user: id }))

Pair this with route:list --json-style review during refactors: rename a route in routes/web.php, regenerate, and let tsc tell you every consumer.

Spatie Settings pattern: one schema, two languages

If you use spatie/laravel-settings, this is where the package earns its keep. Register the settings class in the config, generate, and the frontend gets a typed shape matching Settings properties — no more guessing whether it's site_name or siteName, or whether a value is string or string | null.

import type { GeneralSettings } from '@/types/settings'

const props = defineProps<{ settings: GeneralSettings }>()

Per-domain generation for large apps

On bigger codebases, run generation scoped per area (models vs. routes vs. settings, or per-directory) if the command supports flags. Smaller output files mean faster incremental tsc and clearer diffs in code review.

CI drift check

The most valuable pattern for teams:

php artisan typeable:generate
git diff --exit-code resources/js/types

Fail the build if generated types are stale. This forces every PR that touches a model or route to include the regenerated types — no more "works on my machine" prop mismatches.

Inertia integration

The optional NPM package gives you Inertia-specific helpers (page prop typing, shared-data shapes). Install it only if you're on Inertia; for plain Blade + Alpine or a separate SPA consuming an API, the PHP-generated files alone are sufficient.

Gotchas and Tips

Stale types are the #1 failure mode. Nothing regenerates automatically on php artisan migrate or a model edit. If a type looks wrong, first suspicion: you haven't re-run the generator. Second suspicion: you're importing from the wrong path.

Casts are the source of truth, not the DB schema. $casts drives string vs number vs boolean in the output. A column added to the migration but missing from $casts (or $fillable, depending on how the package introspects) can produce a type that lies. Audit $casts when types surprise you.

Dates are strings unless you say otherwise. Timestamps typically emit as string (ISO), not Date. Don't write new Date(user.created_at).getTime() style code without confirming the generated declaration — and if you want them as Date, that's a frontend concern (a transform layer), not something the generator will do for you.

Accessors and appended attributes are the weak spot. Computed attributes (getFullNameAttribute, $appends) may not appear, or may appear as optional/unknown depending on the version. For anything derived, define a local intersection type:

type UserWithFullName = User & { full_name: string }

Nullable relations → optional properties. A belongsTo that isn't eager-loaded, or a nullable FK, tends to surface as T | null. Handle that at the boundary (v-if="user.profile") rather than casting it away with ! — the ! will bite you when the relation genuinely is null.

Route params and model binding don't always align. Typed route helpers expect the parameter names from the URI definition ({user} → { user: ... }) unless the route uses implicit binding with a custom key. Optional params ({page?}) become optional properties; mixing them with required ones in the wrong order is a common source of undefined in URLs.

Config excludes matter more than includes. Most "why is my model missing?" issues are solved by adding the namespace/glob to config/typeable.php, not by fighting the generator. Check both the include list and any exclude patterns — an overly broad exclude (e.g. app/Models/Admin/*) silently drops a whole subtree.

Settings must be discovered. A Settings class that isn't registered/loaded by Spatie (or isn't in the config's scan path) simply won't produce output. Verify by grepping the generated settings file for the class name.

Pin the NPM package to the PHP package version. The PHP generator and the JS consumer evolve together; a minor mismatch can produce prop types that don't line up with the runtime helpers. Lock both in composer.lock and package-lock.json and bump together.

Commit vs. ignore generated files. Commit them if you want drift checks in CI and reviewable diffs (recommended for teams). Gitignore them if you regenerate on every install — but then you lose the CI drift gate and every developer must remember to run the generator before tsc.

Maturity caveat. 39 stars, one maintainer, last release March 2025. That's fine for a dev-dependency that only emits types — worst case you fork it or replace it with a hand-rolled artisan command. Avoid letting it become load-bearing in a way you can't rip out: keep generated output in a single directory and import through a thin @/types barrel file so a future migration is a one-file change.

Debugging checklist when output looks wrong:

  1. php artisan typeable:generate -v (or equivalent verbosity flag) — read the console output for skipped files.
  2. Open the generated .ts file directly; confirm the class/property is present.
  3. Confirm tsconfig.json include/paths actually covers the output dir and your alias resolves.
  4. Restart the TS language server in your editor (VS Code: "TypeScript: Restart TS Server") — stale language-server state is a frequent false alarm.
  5. php artisan optimize:clear if config caching is masking your config/typeable.php edits.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus