vite: importMetaEnv doesn't work.

Describe the bug

I used the intellisense function as per the guide, but it doesn’t work.

/src/env.d.ts

interface ImportMetaEnv {
  VITE_API_TIMEOUT: number,
  VITE_MOCKUP: boolean
}

.env

VITE_API_TIMEOUT=3000
VITE_MOCKUP=true

And, when import.meta.env is printed from main.ts to the console…

BASE_URL: "/"
DEV: true
MODE: "development"
PROD: false
SSR: false
VITE_API_TIMEOUT: "3000"
VITE_MOCKUP: "true"
VITE_SET: "900"

Intellisense does not work for .env settings. It only comes in string type.

Reproduction

I want it to be converted to the type applied in intellisense (env.d.ts).

System Info

package.json

  "devDependencies": {
    "@antfu/eslint-config": "^0.6.5",
    "@iconify/json": "^1.1.347",
    "@intlify/vite-plugin-vue-i18n": "^2.1.2",
    "@types/nprogress": "^0.2.0",
    "@typescript-eslint/eslint-plugin": "^4.24.0",
    "@vitejs/plugin-vue": "^1.2.2",
    "@vue/compiler-sfc": "^3.0.11",
    "@vue/server-renderer": "^3.0.11",
    "cross-env": "^7.0.3",
    "eslint": "^7.27.0",
    "https-localhost": "^4.6.5",
    "markdown-it-prism": "^2.1.6",
    "npm-run-all": "^4.1.5",
    "pnpm": "^6.4.0",
    "sass": "^1.34.0",
    "typescript": "^4.2.4",
    "vite": "^2.3.3",
    "vite-plugin-components": "^0.9.1",
    "vite-plugin-icons": "^0.5.1",
    "vite-plugin-md": "^0.6.7",
    "vite-plugin-pages": "^0.12.1",
    "vite-plugin-pwa": "^0.7.3",
    "vite-plugin-vue-layouts": "^0.3.1",
    "vite-plugin-windicss": "^0.15.10",
    "vite-ssg": "^0.11.1",
    "vue-tsc": "^0.1.4"
  }

Used package manager: pnpm

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 23 (14 by maintainers)

Commits related to this issue

Most upvoted comments

Recommended in docs way to add types causes build problem:

interface ImportMetaEnv extends Readonly<Record<string, string>> {
  readonly VITE_VAR: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}
▶ pnpm build

> test@0.0.0 build /test
> vue-tsc --noEmit && vite build

node_modules/.pnpm/vite@2.6.13/node_modules/vite/types/importMeta.d.ts:62:11 - error TS2430: Interface 'ImportMetaEnv' incorrectly extends interface 'Readonly<Record<string, string>>'.
  'string' index signatures are incompatible.
    Type 'string | boolean | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.

62 interface ImportMetaEnv {
             ~~~~~~~~~~~~~


Found 1 error.

 ELIFECYCLE  Command failed with exit code 2.

This works

interface ImportMetaEnv extends Readonly<Record<string, string | boolean | undefined>> {
  readonly VITE_VAR: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

@imShara I can confirm this 👍

Correct would be without extends Readonly<Record ... and maybe I think with the ///ref

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_VAR: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

Could you try

interface ImportMetaEnv {
  readonly VITE_API_TIMEOUT: number;
  readonly VITE_MOCKUP: boolean;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

I don’t think the types are converted by default. You have to do it yourself.

I think the env.d.ts is just a way to fight annoyance of import.meta.env values being typed as string | boolean | undefined, when it mostly should be string | undefined. Sadly, it’s because there are DEV and PROD keys in import.meta.env that are booleans. I think there could be fix introduced, but it’s not yet done - anyway, it would just make the defining own ImportMetaEnv unnecessary, the values still would be string | undefined.

Anyway, the solution is to convert the types by yourself.

If you want some inspiration for now, I use it like this in my project: config.ts, website sources:


const parseBoolean = (value: string | boolean | undefined, defaultValue: boolean) => {
	if (typeof value === 'undefined') {
		return defaultValue;
	}
	if (typeof value === 'boolean') {
		return value;
	}
	switch (value.toLowerCase().trim()) {
		case "true": case "yes": case "1": return true;
		case "false": case "no": case "0": return false;
		default: return defaultValue;
	}
}

const Config = {
	isDev: import.meta.env.DEV,
	firebaseConfig: {
		apiKey:                 import.meta.env.VITE_FIREBASE_API_KEY,
		authDomain:             import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
		databaseURL:            import.meta.env.VITE_FIREBASE_DATABASE_URL,
		projectId:              import.meta.env.VITE_FIREBASE_PROJECT_ID,
		storageBucket:          import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
		messagingSenderId:      import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
		appId:                  import.meta.env.VITE_FIREBASE_APP_ID,
		measurementId:          import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
	},
	useEmulators: parseBoolean(import.meta.env.VITE_USE_EMULATORS, import.meta.env.DEV),
};

export default Config;

env.d.ts:

interface ImportMetaEnv {
	VITE_FIREBASE_API_KEY:              string | undefined;
	VITE_FIREBASE_AUTH_DOMAIN:          string | undefined;
	VITE_FIREBASE_DATABASE_URL:         string | undefined;
	VITE_FIREBASE_PROJECT_ID:           string | undefined;
	VITE_FIREBASE_STORAGE_BUCKET:       string | undefined;
	VITE_FIREBASE_MESSAGING_SENDER_ID:  string | undefined;
	VITE_FIREBASE_APP_ID:               string | undefined;
	VITE_FIREBASE_MEASUREMENT_ID:       string | undefined;

	VITE_USE_EMULATORS:                 string | undefined;

	VITE_WEBSITE_URL:                   string | undefined;

	VITE_APP_LOCALHOST_PORT:            string | undefined;
	VITE_WEBSITE_LOCALHOST_PORT:        string | undefined;
}

My code base imports Config where necessary, it have types nicely converted.