Значения по умолчанию для интерфейсов TypeScript: как задать?
Проблема: интерфейсы TypeScript не поддерживают значения по умолчанию
Вы правы — в TypeScript интерфейсы описывают только структуру данных (типы полей), но не могут содержать значения по умолчанию. Это сделано намеренно, чтобы отделить описание типа от реализации. Однако есть несколько проверенных способов решить вашу задачу.
Способ 1: Функция-фабрика с оператором spread (рекомендуется)
Самый популярный и чистый подход — создать функцию, которая принимает частичные данные и возвращает объект с заполненными значениями по умолчанию:
interface UserConfig {
name: string;
age: number;
isActive: boolean;
email: string;
}
function createUserConfig(overrides?: Partial<UserConfig>): UserConfig {
const defaults: UserConfig = {
name: "Гость",
age: 18,
isActive: true,
email: ""
};
return { ...defaults, ...overrides };
}
// Использование:
const config1 = createUserConfig({ name: "Иван", age: 25 });
console.log(config1);
// { name: "Иван", age: 25, isActive: true, email: "" }
const config2 = createUserConfig();
console.log(config2);
// { name: "Гость", age: 18, isActive: true, email: "" }
Как это работает:
- Тип
Partial<UserConfig>делает все поля необязательными - Оператор
...(spread) объединяет объекты, причем значения изoverridesперезаписываютdefaults - Функция гарантированно возвращает полный объект
UserConfig
Способ 2: Использование класса с конструктором
Если вам нужна не только структура, но и поведение (методы), используйте класс:
class UserConfigClass {
name: string;
age: number;
isActive: boolean;
email: string;
constructor(data?: Partial<UserConfigClass>) {
this.name = data?.name ?? "Гость";
this.age = data?.age ?? 18;
this.isActive = data?.isActive ?? true;
this.email = data?.email ?? "";
}
}
const config = new UserConfigClass({ name: "Мария" });
console.log(config);
// UserConfigClass { name: "Мария", age: 18, isActive: true, email: "" }
В этом случае сам класс служит и интерфейсом, и реализацией. TypeScript автоматически выводит тип из класса.
Способ 3: Утилитарный тип с объединением
Если вы не хотите писать функцию, можно создать тип, который объединяет обязательные поля с необязательными значениями по умолчанию:
interface UserConfigRequired {
name: string;
age: number;
}
interface UserConfigDefaults {
isActive: boolean;
email: string;
}
type UserConfig = UserConfigRequired & Partial<UserConfigDefaults>;
// Но значения по умолчанию всё равно нужно задавать вручную:
function applyDefaults(config: UserConfig): UserConfigRequired & UserConfigDefaults {
return {
...config,
isActive: config.isActive ?? true,
email: config.email ?? ""
};
}
Важные замечания
- Не пытайтесь использовать
??(nullish coalescing) прямо в интерфейсе — это синтаксически неверно - Для простых случаев используйте фабричную функцию — это наиболее идиоматичный подход в TypeScript
- Если полей много (10+), рассмотрите библиотеки вроде
ts-patternилиzodдля валидации с дефолтами
Выберите способ, который лучше всего подходит под вашу архитектуру. Для большинства проектов достаточно первого варианта с Partial и spread-оператором.
Альтернативный подход: деструктуризация с значениями по умолчанию
Можно задать значения по умолчанию прямо при деструктуризации объекта. Это удобно, если вы работаете с данными локально, а не создаёте новые объекты:
interface UserConfig {
name: string;
age: number;
isActive?: boolean;
email?: string;
}
function processUser(data: UserConfig) {
const {
name,
age,
isActive = true,
email = ""
} = data;
// Теперь isActive и email гарантированно имеют значения
console.log(`Пользователь ${name}, возраст ${age}, активен: ${isActive}, email: ${email}`);
}
// Использование:
processUser({ name: "Анна", age: 30 });
// Выведет: Пользователь Анна, возраст 30, активен: true, email:
Плюсы: не нужно писать отдельную функцию, код компактный.
Минусы: значения по умолчанию применяются только внутри функции, а не на уровне хранения. Если вам нужно сохранить объект с дефолтами, этот способ не подойдёт.
Также можно комбинировать с фабричной функцией для создания объектов:
function createConfig({
name,
age,
isActive = true,
email = ""
}: Partial<UserConfig>): UserConfig {
return { name: name ?? "Гость", age: age ?? 18, isActive, email };
}
Этот вариант особенно полезен, когда у вас много необязательных полей с разными дефолтами.