Vite передає в клієнтський код змінні оточення з префіксом VITE_ через import.meta.env. За замовчуванням TypeScript знає лише вбудовані поля (MODE, DEV, PROD, BASE_URL, SSR), а власні змінні мають тип any чи не існують.
Підключити типи Vite - у tsconfig.json:
{ "compilerOptions": { "types": ["vite/client"] } }
(з TypeScript 6.0 types за замовчуванням порожній - без цього навіть import.meta.env невідомий).
Описати власні змінні - файл resources/js/env.d.ts (чи src/vite-env.d.ts):
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_NAME: string;
readonly VITE_REVERB_APP_KEY: string;
readonly VITE_REVERB_PORT?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Тепер import.meta.env.VITE_APP_NAME - string, редактор підказує назви, а друкарська помилка VITE_APP_NAM дасть помилку компіляції.
Важливо: тип - не гарантія наявності. Оголошення string не означає, що змінна справді задана в .env на сервері збирання. Відсутня змінна буде undefined в зібраному коді. Надійніше перевірити при старті застосунку:
import * as z from 'zod';
export const env = z
.object({
VITE_APP_NAME: z.string().min(1),
VITE_REVERB_PORT: z.coerce.number().default(443),
})
.parse(import.meta.env);
Неправильна конфігурація виявляється одразу з зрозумілим повідомленням, а не дивною поведінкою в продакшені. Заодно рядкові значення перетворюються на числа й булеві.
Що варто пам'ятати:
- усі значення з
.env- рядки:VITE_FEATURE_X=falseдає рядок'false', який уif- істина; - змінні з префіксом
VITE_потрапляють у зібраний JavaScript і видні будь-кому - секрети туди не кладуть; - значення підставляються під час збирання: зміна
.envна сервері без перезбирання нічого не змінить.
У Node.js-коді (конфіги, SSR) - process.env з типами з @types/node і така сама перевірка схемою.
Докладніше в документації: Vite: IntelliSense для TypeScript