Typescript exported type from module is any

574 views Asked by At

I have one Typescript module that exports a complex type created with the typeof operator, like below:

// controller/createProfile.ts

import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';

const createProfileBody = z.object({
  id: z.string({
    required_error: 'Id is required.',
  }),
  username: z.string({
    required_error: 'User name is required.',
  }),
  firstname: z.string({
    required_error: 'First name is required',
  }),
  lastname: z.optional(z.string()),
  avatar: z.optional(z.string()),
});

const route = app.post('/', zValidator('json', createProfileBody), async c => {
  ...
});

export type CreateProfileType = typeof route;
// types.d.ts
export * from './controller/createProfile';

The type of route is this complex type:

Hono<{
    Bindings: Binding;
}, Schema<"post", "/", {
    json: {
        lastname?: string | undefined;
        avatar?: string | undefined;
        id: string;
        username: string;
        firstname: string;
    };
}, ReturnType>>

It's similar to tRPC since it creates the type for the createProfile endpoint.
I now want to import the CreateProfileType from a different module but its type is always any when importing the type.
This is my tsconfig for the module that exports the type:

{
    "include": ["src/**/*.ts"],
    "compilerOptions": {
        "baseUrl": "src",
        "target": "esnext",
        "module": "esnext",
        "lib": ["esnext"],
        "resolveJsonModule": true,
        "moduleResolution": "node",
        "allowJs": true,
        "checkJs": false,
        "isolatedModules": true,
        "allowSyntheticDefaultImports": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    }
}

In my package.json of the exporting module I have specified the types field to my types.d.ts file.

One weird think I have noticed is, when the type of CreateProfileType is just a simple string, the imported type is not any but string and it works as expected. So my guess would be that this is somehow linked to exporting a type which is just an alias to a type from a different package in my case hono. Is it possible to export the typeof route to make it accessible for different packages?

Edit

When I generate a declaration file with tsc I get the following output. So somehow the type of the route is lost.

export declare const route: any;
export type CreateProfileType = typeof route;

What I also noticed is, when I manually specify the type of route like below, the exported type is the correct on and not any

const route: Hono<
  {
    Bindings: Binding;
  },
  Schema<'post','/',
    {
      json: {
        lastname?: string | undefined;
        avatar?: string | undefined;
        id: string;
        username: string;
        firstname: string;
      };
    },Profile
  >
> = app.post('/', zValidator('json', createProfileBody), async c => {
...
});
1

There are 1 answers

0
JonasLevin On

I just found the answer to my question.
The reason the exported type was any was because I set the baseURL to src in my tsconfig. After switching to absolute imports the type was correct, I think for some reason typescript was not able to locate the ReturnType and Binding type that was used by the inferred type of route.