The Event interface declares neither author nor createdAt, but addEvent writes both to Firestore and the read paths add createdAt back on the way out. The mismatch is currently hidden by three as unknown as Event double casts, which silence the compiler without making the type correct, and it surfaces as a hard error in searchService, which builds its event objects without a cast.
Location
src/lib/types.ts:19-30 — the Event interface
src/lib/services/eventService.ts:26-32 — where author and createdAt are written
src/lib/services/eventService.ts:51, :70, :90 — the three double casts
src/lib/services/searchService.ts:163 — the resulting type error
The write stores both fields:
const docRef = await addDoc(collection(db, 'events'), {
...eventData,
author,
dateTime: dateTime.toISOString(),
createdAt: serverTimestamp(),
rsvpCount: 0,
});
The reads add createdAt and then cast the shape away, three times over:
return {
...data,
id: docSnap.id,
createdAt: data.createdAt?.toDate ? data.createdAt.toDate().toISOString() : new Date().toISOString(),
dateTime: typeof data.dateTime === 'string' ? data.dateTime : (...),
} as unknown as Event;
searchService does the same mapping without a cast and fails:
src/lib/services/searchService.ts(163,9): error TS2353: Object literal may only specify known properties, and 'createdAt' does not exist in type 'Event'.
The file already acknowledges both fields — EventDataForFirestore at eventService.ts:8-12 and the addEvent parameter at :16 both Omit 'createdAt' and 'author' from Event. Omit does not require the keys to exist, so those lines compile while documenting fields the type never declared.
Why this matters
as unknown as Event turns off checking for the whole object, not just the two extra fields. Every consumer of getEvents(), getEventById(), and getEventsByCommunity() is trusting a shape the compiler was told not to verify — so a genuinely malformed document reaches the render tree unflagged rather than failing at the boundary. A document created before author was written, or by a script that skips it, will pass straight through these casts and crash whichever surface dereferences event.author.
The casts also make author unreachable through the type: no consumer can display the event organiser without adding a cast of its own.
Suggested fix
- Add both fields to
Event in src/lib/types.ts, matching the shape Question already uses at :37-38. Make them optional only if documents genuinely predate the write; if so, prefer author?: ... plus a guard at the render sites over a cast.
- Replace the three casts at
eventService.ts:51,70,90 with a single narrowing helper, e.g. mapEventDoc(docSnap): Event, that validates the required fields and returns a properly typed object. All three call sites do identical mapping today.
- Confirm
searchService.ts:150-164 type-checks without change once createdAt is declared, and that TS2353 is gone from npm run typecheck.
- Decide what should happen for a document missing
author — skip it, or substitute a placeholder — and encode that in the helper rather than leaving it to whichever component dereferences it first.
Note: #23 proposes narrowing the stored author to uid/displayName/photoURL, so coordinate the field's type with that issue rather than adding a full UserProfile here.
The
Eventinterface declares neitherauthornorcreatedAt, butaddEventwrites both to Firestore and the read paths addcreatedAtback on the way out. The mismatch is currently hidden by threeas unknown as Eventdouble casts, which silence the compiler without making the type correct, and it surfaces as a hard error insearchService, which builds its event objects without a cast.Location
src/lib/types.ts:19-30— theEventinterfacesrc/lib/services/eventService.ts:26-32— whereauthorandcreatedAtare writtensrc/lib/services/eventService.ts:51,:70,:90— the three double castssrc/lib/services/searchService.ts:163— the resulting type errorThe write stores both fields:
The reads add
createdAtand then cast the shape away, three times over:searchServicedoes the same mapping without a cast and fails:The file already acknowledges both fields —
EventDataForFirestoreateventService.ts:8-12and theaddEventparameter at:16bothOmit'createdAt'and'author'fromEvent.Omitdoes not require the keys to exist, so those lines compile while documenting fields the type never declared.Why this matters
as unknown as Eventturns off checking for the whole object, not just the two extra fields. Every consumer ofgetEvents(),getEventById(), andgetEventsByCommunity()is trusting a shape the compiler was told not to verify — so a genuinely malformed document reaches the render tree unflagged rather than failing at the boundary. A document created beforeauthorwas written, or by a script that skips it, will pass straight through these casts and crash whichever surface dereferencesevent.author.The casts also make
authorunreachable through the type: no consumer can display the event organiser without adding a cast of its own.Suggested fix
Eventinsrc/lib/types.ts, matching the shapeQuestionalready uses at:37-38. Make them optional only if documents genuinely predate the write; if so, preferauthor?: ...plus a guard at the render sites over a cast.eventService.ts:51,70,90with a single narrowing helper, e.g.mapEventDoc(docSnap): Event, that validates the required fields and returns a properly typed object. All three call sites do identical mapping today.searchService.ts:150-164type-checks without change oncecreatedAtis declared, and thatTS2353is gone fromnpm run typecheck.author— skip it, or substitute a placeholder — and encode that in the helper rather than leaving it to whichever component dereferences it first.Note: #23 proposes narrowing the stored author to
uid/displayName/photoURL, so coordinate the field's type with that issue rather than adding a fullUserProfilehere.