vite icon indicating copy to clipboard operation
vite copied to clipboard

Recommend `vite-env.ts` over `vite-env.d.ts`

Open remcohaszing opened this issue 8 months ago • 10 comments

Documentation is

  • [ ] Missing
  • [ ] Outdated
  • [ ] Confusing
  • [x] Not sure?

Explain in Detail

There are two types of TypeScript files: Source files (.ts, .tsx, .cts, .mts) and declaration files (.d.ts, .d.cts, .d.mts, .d.*.ts).

Declaration files describe a JavaScript file. Typically users are supposed to author source files, and if relevant, emit declaration files as a build artifact. Users are typically not supposed to author declaration files. Yet, Vite recommends authoring a file named vite-env.d.ts.

The practical problem often comes from users who don’t understand these differences and TypeScript compiler options. It’s common to enable the option skipLibCheck. This option disables type checking of declaration files. So users end up with a file named vite-env.d.ts that’s not type-checked. Another confusing aspect is TypeScript scripts vs modules. The option moduleDetection affects source files, but not declaration files.

The convention to create a file named {tool}-env.d.ts is not limited to Vite. I believe there are also astro-env.d.ts and next-env.d.ts. These frameworks suffer from the same problems.

Your Suggestion for Changes

I suggest to remove all references to vite-env.d.ts, and replace them with vite-env.ts. This file should also be explicitly marked as a module to avoid ambiguity. This includes the documentation as well as all project templates.

// vite-env.ts
/// <reference types="vite/client" />

declare global {
  interface ImportMetaEnv {
    readonly VITE_APP_TITLE: string
    // more env variables...
  }

  interface ImportMeta {
    readonly env: ImportMetaEnv
 }
}

// Explicitly mark this file as a module.
export {}

Reproduction

No response

Steps to reproduce

No response

remcohaszing avatar Mar 21 '25 10:03 remcohaszing

I checked whether it's possible to do this, and noticed that ambient module declarations do not work in .ts. This means if the user wants to add .unknown to assetsInclude, they would need to add a separate file instead of vite-env.ts.

It would be better to have type check enabled for vite-env.d.ts, but I assume there won't be many types declared there, I think it's acceptable to keep using .d.ts. It'd be nice if TypeScript allowed enabling type check per-file by adding @ts-check to .d.ts though.

sapphi-red avatar May 30 '25 04:05 sapphi-red

According to the TypeScript handbook, ambient modules can be declared in script files, not in modules.

The following works:

// tsconfig.json
{
  "compilerOptions": {
    "module": "preserve",
    "strict": true
  }
}
// src/ambient.ts
declare module '*.unknown' {
  export const unknown: string
}
// src/test.ts
import { unknown } from 'whatever.unknown'

console.log(unknown)

However, this breaks if you force enable module detection. Some things that do this are the compiler options:

  • "moduleDetection": "force"
  • "module": "node16"
  • "module": "node18"
  • "module": "nodenext"

Vite users should use "module": "preserve". This means they shouldn’t be affected by this, but it’s not really obvious these options are linked. If you have to use "module": "nodenext", you can still disable module detection with "moduleDetection": "legacy".

The reason the augmentation always works for in declaration files without ESM syntax, is because declaration files are not affected by the moduleDetection compiler option.

remcohaszing avatar May 30 '25 09:05 remcohaszing

We recommend "moduleDetection": "force" and set it in the template because Vite treats all files as a module. So if there's a way to mark a single file as a script file, that would also solve our problem.

sapphi-red avatar May 30 '25 10:05 sapphi-red

Sadly no. It’s possible to force a file into module mode by adding export {}, but not the other way around. You recommendation to force module detection does make sense, though I wonder how many users actually author files without imports or exports.

I suppose this is a matter of trade-offs. There’s not one perfect solution that serves all projects.

remcohaszing avatar May 30 '25 10:05 remcohaszing

We're going to remove vite-env.d.ts in https://github.com/vitejs/vite/pull/20132

sapphi-red avatar Aug 27 '25 13:08 sapphi-red

Nice, I like it. But that doesn’t remove vite-env.d.ts from the IntelliSense for TypeScript section.

remcohaszing avatar Aug 27 '25 13:08 remcohaszing

Ah, good catch. I'll made a PR for that section once it's merged then.

sapphi-red avatar Aug 27 '25 13:08 sapphi-red

FWIW the TypeScript docs mentions vite-env.d.ts and doesn't recommend against it. Personally I don't think .d.ts strictly needs to mirror .js files and we have to discourage .d.ts ambient files, unless there's official docs discouraging that.

bluwy avatar Sep 22 '25 13:09 bluwy

In that section, it describes about the ambient module declaration. So it makes sense to use .d.ts. The file in "IntelliSense for TypeScript" section does not include an ambient module declaration, so it doesn't have to be .d.ts. I think it's better to use .ts so that the type-check runs on the file even if skipLibCheck is enabled.

sapphi-red avatar Oct 06 '25 06:10 sapphi-red

My point is that there's nothing wrong with manually authoring declaration files, like what the issue description has said:

Users are typically not supposed to author declaration files. Yet, Vite recommends authoring a file named vite-env.d.ts.

So if we decide to use either .ts or .d.ts, it's not because of that but another reason, like the skipLibCheck argument here.


About using .ts so it's unaffected by skipLibCheck, I'm personally not sure as vite-env.d.ts is usually already very simple, and if we mention adding ambient module declarations because they extend assetsInclude using vite-env.d.ts (we lack this docs now but we should have), it's going to make vite-env.ts and vite-env.d.ts confusing to explain.

  • "Why is there two files doing similar things?"
  • "What if I put this type into the other so I can keep one file?"
  • "Is there a typo in the extension? I'm going to put the types in the existing .ts/.d.ts" and things don't work

Keeping a single vite-env.d.ts is more consistent when suggesting any sort of ambient types.

bluwy avatar Oct 06 '25 08:10 bluwy