withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
45.45k stars 2.38k forks source link

Astro.locals type errors not cleared from VSCode with proper env.d.ts #7394

Closed gkatsanos closed 1 year ago

gkatsanos commented 1 year ago

What version of astro are you using?

2.6.1

Are you using an SSR adapter? If so, which one?

Node

What package manager are you using?

pnpm

What operating system are you using?

Mac

What browser are you using?

Chrome

Describe the Bug

image

/// <reference types="astro/client" />
declare interface User {
  products_admin: boolean
  agency: boolean
  email: string
  firstname: string
  lastname: string
  salutation: string
  remember_me: boolean
  roles: string[]
  user_id: string
  company_ids: string
  session_id: string
}

declare global {
  namespace App {
    interface Locals {
      brand: string
      userAuth: User
      referrer: string
      url: string
    }
  }
}
// middleware
import { getUserAuth } from "@store/auth"
import { USER_INFO_COOKIE_KEY } from "@constants/auth"

export function onRequest(context, next) {
  const forwardedHost = context?.request?.headers.get("x-forwarded-host")
  const url = new URL(context.request.url)

  if (forwardedHost?.includes("europages")) {
    context.locals.brand = "ep"
  } else {
    context.locals.brand = "wlw"
  }
  context.locals.url = url.pathname
  context.locals.referrer = url.origin

  if (context.cookies.has(USER_INFO_COOKIE_KEY)) {
    const cookie = context.cookies.get(USER_INFO_COOKIE_KEY)
    context.locals.userAuth = getUserAuth(cookie)
  }

  return next()
}
// tsconfig.json
{
  "extends": "astro/tsconfigs/base",
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "paths": {
      "@assets/*": ["src/assets/*"],
      "@components/*": ["src/components/*"],
      "@pages/*": ["src/pages/*"],
      "@helpers/*": ["src/helpers/*"],
      "@layouts/*": ["src/layouts/*"],
      "@constants/*": ["src/constants/*"],
      "@store/*": ["src/store/*"],
      "@i18n/*": ["src/i18n/*"]
    },
    "types": ["@astrojs/image/client"],
    "jsx": "preserve",
    "plugins": [
      {
        "name": "@astrojs/ts-plugin"
      }
    ]
  }
}

Link to Minimal Reproducible Example

-

Participation

ematipico commented 1 year ago

The documentation is old, that's on us.

That's how you should type the Locals

declare namespace App {
    interface Locals {
        user: {
            name: string;
            surname: string;
        };
    }
}
gkatsanos commented 1 year ago

I am willing to make a PR to update it but I assume you guys have bundled a ton of updates with docs already?

(would be cool if docs would be updated together)

whoiscarlo commented 9 months ago

I'm still running into this issue in Astro 3.5.7 in Typescript. Anytime I try to set a new property on locals, I get an error from VS Code saying: Property '[insert_name]' does not exist on type 'Locals'.ts(2339)

Jaszkowic commented 9 months ago

I am experiencing the same issue, even after updating .env.d.ts as recommended here: https://github.com/withastro/astro/issues/7394#issuecomment-1592728591

Property '...' does not exist on type 'Locals'.ts(2339)

Astro 3.6.0

san4d commented 9 months ago

I'm also experiencing this issue on Astro 3.6.4. I restarted VS code as well.

DnzzL commented 7 months ago

Still the issue with astro 4.1.3. Any news ?

ematipico commented 7 months ago

Our examples/ folder has an example where locals are typed, and we run TypeScript to make sure it's ok.

If you have errors, is it possible that you changed some TypeScript configuration?

san4d commented 7 months ago

This ended up being more about how TypeScript types behave within a namespace. This is noticeable when you're trying to import a custom type. Here's the solution:

Suppose you have a type called SessionData exported from @session (a path alias, but this also holds for relative paths). You can get the types to behave as expected with a dynamic import:

// in env.d.ts
declare namespace App {
    interface Locals {
            sessionData: import('@session').SessionData;
    }
}
DnzzL commented 7 months ago

This ended up being more about how TypeScript types behave within a namespace. This is noticeable when you're trying to import a custom type. Here's the solution:

Suppose you have a type called SessionData exported from @session (a path alias, but this also holds for relative paths). You can get the types to behave as expected with a dynamic import:

// in env.d.ts
declare namespace App {
  interface Locals {
            sessionData: import('@session').SessionData;
  }
}

Thank you that was it :pray:

Crazy I lost time on this :joy:

svaraborut commented 7 months ago

Im working with @cloudflare (ide Webstorm 2023.2.1)

Versions:

"astro": "^4.2.6",
"@astrojs/cloudflare": "^9.0.0",

The only way it works is using global and avoiding the Env name used in the documentation

/// <reference types="astro/client" />
import type { AdvancedRuntime } from '@astrojs/cloudflare'
import type { D1Database, R2Bucket } from '@cloudflare/workers-types'

// Calling it Env clashes with the @cloudflare type
type AppEnv = {
    // Bindings
    DB: D1Database
    R2_CDN: R2Bucket
    // Secrets
    AUTH_SECRET: string
}

declare global {
    namespace App {
        interface Locals extends AdvancedRuntime<AppEnv> {
        }
    }
}

Why the documented code is not working as expected?

johnmw commented 6 months ago

Thank you @san4d , that solved my problem. An additional note in case it helps anyone new to Typescript: none of my Locals types worked in VS Code until I removed all my "import X from Y" lines in env.d.ts file and replaced them with the solution from @san4d

For example, something like this didn't work for me (including the testTypeScript function):

/// <reference types="astro/client" />
import { SupabaseClient } from '@supabase/supabase-js';
declare namespace App {
  interface Locals {    
      testTypeScript: () => string;
      supabase: SupabaseClient;
  }
}

However this did work:

/// <reference types="astro/client" />
declare namespace App {
  interface Locals { 
      testTypeScript: () => string;
      supabase: import('@supabase/supabase-js').SupabaseClient;
  }
}
ben-xD commented 4 months ago

This seems broken again. Following the examples above don't work.

Our examples/ folder has an example where locals are typed, and we run TypeScript to make sure it's ok.

If you have errors, is it possible that you changed some TypeScript configuration?

@ematipico, you referred to examples, but I cannot find any examples in this repo. I see 2 instances of interface Locals {} (they're empty). Is there another repo or something I don't know about?

fearandesire commented 2 months ago

Here's what worked for me:

/// <reference types="astro/client" />
declare global {
  namespace App {
    interface Locals extends Record<string, any> {
      discordId: string | null;
      discordUsername: string | null;
    }
  }
}

I'm using Astro 4.10.8

@ben-xD