Extend TypeScript types for third-party modules in an Astro site

Jason LengstorfJason Lengstorf

Why would you need to change a third-party type in an Astro project?

In cases where a third-party package provides a way for developers to add custom data into their tool, there's no way for the package developers to know ahead of time what that data will look like.

In those cases, if we want to avoid TypeScript errors when we work with our custom data, we need to modify the types of the third-party package to include type declarations for our custom data.

A real-world example: Astro + Clerk and user metadata

In one of my Astro projects, I'm using the Clerk Astro integration and attaching information about a given user's Stripe payment status into the publicMetadata field on the Clerk user.

By default, the TypeScript definitions for Clerk user metadata mark everything as unknown, which causes TypeScript errors when I try to use that custom data in my project.

VS Code screenshot showing the custom metadata having a type of unknown

The unknown is in there by design — Clerk's team expects that we'll override the type if and when we add custom metadata. Inside @clerk/types, the UserPublicMetadata interface looks like this:

user.ts
declare global {
/**
* If you want to provide custom types for the user.publicMetadata object,
* simply redeclare this rule in the global namespace.
* Every user object will use the provided type.
*/
interface UserPublicMetadata {
[k: string]: unknown;
}
/* omitting the rest of the declaration for brevity */
}

Perfect! All I need to do is "simply redeclare this rule in the global namespace".

Except... I'm not good at TypeScript beyond getting it to autocomplete for me, so I don't know what that means. I also don't know where the global namespace is for Astro.

How to extend a third-party module's types in an Astro project

Step 1: Define the types for your custom data

In my project, I'm adding an object that looks like this to the publicMetadata field:

interface UserPublicMetdadata {
stripe?: {
customer?: string;
status?: string;
level?: string;
}
}

Next, I need to override the default UserPublicMetadata type from Clerk with my custom implementation above.

Step 2: Update src/env.d.ts in your Astro site

Astro generates src/env.d.ts for configuring extra TypeScript features in Astro projects. This is where we can add our own custom types and overrides.

Inside src/env.d.ts, use the declare keyword to add your override for the third-party interface:

src/env.d.ts
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="astro-clerk-auth/env" />
declare interface UserPublicMetadata {
stripe?: {
customer?: string;
status?: string;
level?: string;
};
}

After saving this file, you'll now have the correct types available on your custom metadata.

VS Code screenshot showing the correct types when hovering the custom metadata

Heads up! You may need to reload your IDE for the types to update.

Get the latest Astro news, tutorials, and updates delivered to your inbox.

I respect your privacy. Unsubscribe at any time.