Ошибка TS2532: Object is possibly 'undefined' в TypeScript — как исправить?
Причина ошибки и базовое решение
Ошибка TS2532 возникает, потому что TypeScript строго следит за возможными значениями undefined и null. В вашем случае метод snap.data() возвращает тип Firestore.DocumentData | undefined — это значит, что данных может не быть. TypeScript не позволяет обращаться к свойствам объекта, который может быть undefined, так как это приведет к ошибке во время выполнения.
Самый простой и надежный способ — использовать ранний выход (early return) или проверку с утверждением типа. Рассмотрим оба подхода.
Решение 1: Проверка с early return
export const processUserData = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
const data = snap.data();
// Если данных нет — выходим из функции
if (!data) {
console.log('No data found for this document');
return;
}
// Теперь TypeScript знает, что data — это точно объект
const userName = data.name;
const userAge = data.age;
console.log(`User ${userName} is ${userAge} years old.`);
});
После проверки if (!data) TypeScript "сужает" тип (type narrowing) и понимает, что внутри блока if data гарантированно не undefined. Это самый безопасный подход.
Решение 2: Использование оператора опциональной цепочки (?.)
export const processUserData = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
const data = snap.data();
// Опциональная цепочка и nullish coalescing
const userName = data?.name ?? 'Unknown';
const userAge = data?.age ?? 0;
console.log(`User ${userName} is ${userAge} years old.`);
});
Оператор ?. (optional chaining) проверяет, существует ли объект перед обращением к свойству. Если data — undefined, то data?.name вернет undefined. Оператор ?? (nullish coalescing) задает значение по умолчанию, если результат слева — null или undefined.
Решение 3: Type assertion (не рекомендуется)
const data = snap.data();
const userName = (data as Firestore.DocumentData).name;
const userAge = (data as Firestore.DocumentData).age;
Этот способ небезопасен — вы говорите TypeScript "замолчать", но если в рантайме data окажется undefined, вы получите ошибку. Используйте только если на 100% уверены, что данные существуют.
Лучшая практика для Firebase Firestore
- Всегда проверяйте
snap.exists— это свойство документа Firestore, которое точно говорит, существует ли документ. - Используйте
if (!snap.exists)для раннего выхода.
export const processUserData = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
if (!snap.exists) {
console.log('Document does not exist');
return;
}
const data = snap.data()!; // Non-null assertion — безопасно после проверки exists
const userName = data.name;
const userAge = data.age;
console.log(`User ${userName} is ${userAge} years old.`);
});
После проверки snap.exists можно использовать оператор ! (non-null assertion), так как вы гарантируете, что данные есть. Это делает код чище и безопаснее.
Альтернативный подход: деструктуризация с значениями по умолчанию
Если вы уверены, что документ существует (например, в триггере onCreate документ только что создан), можно использовать деструктуризацию с значениями по умолчанию в сочетании с оператором !.
export const processUserData = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
const { name = 'Anonymous', age = 0 } = snap.data()!;
console.log(`User ${name} is ${age} years old.`);
});
Оператор ! (non-null assertion) говорит TypeScript: "Я знаю, что это не undefined". Это безопасно в контексте onCreate, так как триггер срабатывает только при создании документа. Деструктуризация с значениями по умолчанию защищает от отсутствующих полей.
Важно: Этот подход подходит только когда вы абсолютно уверены в существовании объекта. Для общего случая используйте проверку if или optional chaining.
Дополнительно можно создать вспомогательную функцию для обработки данных:
function safeGetUserData(data: Firestore.DocumentData | undefined) {
if (!data) return null;
return {
name: data.name ?? 'Unknown',
age: data.age ?? 0
};
}
// Использование
const userData = safeGetUserData(snap.data());
if (userData) {
console.log(`User ${userData.name} is ${userData.age} years old.`);
}
Такой подход делает код переиспользуемым и улучшает читаемость.