DEV Community

Your brand-new Shopify app template ships with a TypeScript error (and the one-line fix)

Run this today:

shopify app init # choose Remix
cd your-app && npm install && npx tsc --noEmit

...and there's a good chance you'll be greeted by this wall:

app/shopify.server.ts: error TS2322: Type 'PrismaSessionStorage<PrismaClient<...>>' is not assignable to type 'SessionStorage'.
  Types of property 'storeSession' are incompatible.
    ...
    Types have separate declarations of a private property 'compressedScopes'.

You haven't written a single line of code yet. Welcome to Shopify app development.

What's actually happening

That last line is the tell: "separate declarations of a private property" means TypeScript is comparing two different copies of the same class from two different places in node_modules. Check it:

npm ls @shopify/shopify-api

On a fresh template you'll see two versions. As of this writing:

  • @shopify/shopify-app-session-storage-prisma@8 depends on @shopify/shopify-api@^12
  • @shopify/shopify-app-remix@4 bundles @shopify/shopify-api@^13

Those ranges don't overlap, so npm installs both - one at the top level, one nested under shopify-app-remix. Your PrismaSessionStorage is built against v12's Session class; the shopifyApp() config expects v13's. Same class name, different declarations, TypeScript (correctly) refuses.

Things that don't fix it

  • npm dedupe - can't collapse a genuine semver conflict. Tried it, still duplicated.
  • Deleting node_modules and reinstalling - same resolution, same result.
  • as any on the session storage - compiles, and now you've turned off type checking on the exact seam where your auth sessions live.

The one-line fix

npm install @shopify/shopify-app-session-storage-prisma@^9

v9 peers against @shopify/shopify-api@^13. The nested copy disappears, and:

npm ls @shopify/shopify-api   # exactly ONE version
npx tsc --noEmit              # green

The related mystery cast you'll see in older code

If you've read open-source Shopify apps, you may have seen billing plan names cast like this:

await billing.require({
  plans: [MONTHLY_PLAN as never], // โ† why??
  ...
});

Same root cause. With two @shopify/shopify-api copies, billing plan-name type inference breaks, and as never was the workaround that let people ship. With a single copy, the cast is unnecessary (though harmless).

Why I'm writing this down

I'm building ApprovedKit - a Shopify app boilerplate extracted from a real production app (currently going through App Store review, and we're logging the whole review process live). While verifying that our modules apply cleanly to a fresh official template, we hit this exact error on a clean clone - which means every new Shopify app developer hits it too.

That's the kit's whole philosophy: every gotcha we hit becomes a documented fix at the exact line it matters. If that sounds useful, the early-access version is $99 for the first 30 seats.

Happy shipping - and run npm ls @shopify/shopify-api before you trust your types.

Comments

No comments yet. Start the discussion.