From 3c7a6d8278d3744e52329cd3c72770fd426c7fd0 Mon Sep 17 00:00:00 2001 From: Tyler Dixon Date: Fri, 17 Jul 2026 13:07:06 -0700 Subject: [PATCH 1/2] fix: tighten ReactFireOptions generic types, remove T | any widening Removes the `| any` escape hatch from `initialData` and `startWithValue` in ReactFireOptions, so passing a mismatched type is now a compile error instead of silently accepted. Also tightens the return types of `checkIdField` and `checkinitialData`, and adds a cast in `useObservable` to handle the internal mismatch between the hook's T and the raw Firebase observable type. Closes #383 --- src/firestore.tsx | 6 +++--- src/index.ts | 12 ++++++------ src/useObservable.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/firestore.tsx b/src/firestore.tsx index 136d34e0..fa566cfd 100644 --- a/src/firestore.tsx +++ b/src/firestore.tsx @@ -63,7 +63,7 @@ export function useFirestoreDocData(ref: DocumentReference, opti const idField = options ? checkIdField(options) : 'NO_ID_FIELD'; const observableId = `firestore:docData:${ref.firestore.app.name}:${ref.path}:idField=${idField}`; - const observable = docData(ref, { idField }); + const observable = docData(ref, { idField: idField as keyof T }); return useObservable(observableId, observable, options) as ObservableStatus; } @@ -75,7 +75,7 @@ export function useFirestoreDocDataOnce(ref: DocumentReference, const idField = options ? checkIdField(options) : 'NO_ID_FIELD'; const observableId = `firestore:docDataOnce:${ref.firestore.app.name}:${ref.path}:idField=${idField}`; - const observable$ = docData(ref, { idField }).pipe(first()); + const observable$ = docData(ref, { idField: idField as keyof T }).pipe(first()); return useObservable(observableId, observable$, options) as ObservableStatus; } @@ -96,7 +96,7 @@ export function useFirestoreCollection(query: FirestoreQuery(query: FirestoreQuery, options?: ReactFireOptions): ObservableStatus { const idField = options ? checkIdField(options) : 'NO_ID_FIELD'; const observableId = `firestore:collectionData:${getUniqueIdForFirestoreQuery(query)}:idField=${idField}`; - const observable$ = collectionData(query, { idField }); + const observable$ = collectionData(query, { idField: idField as (string & keyof T) }); return useObservable(observableId, observable$, options); } diff --git a/src/index.ts b/src/index.ts index ab60a1fe..df5ce614 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,11 +23,11 @@ export class ReactFireError extends Error { export interface ReactFireOptions { idField?: string; - initialData?: T | any; + initialData?: T; /** * @deprecated use initialData instead */ - startWithValue?: T | any; + startWithValue?: T; suspense?: boolean; } @@ -40,12 +40,12 @@ export function checkOptions(options: ReactFireOptions, field: string) { throw new Error(`Field "${field}" is not a valid key in ReactFireOptions`); } -export function checkinitialData(options: ReactFireOptions) { - return checkOptions(options, 'initialData'); +export function checkinitialData(options: ReactFireOptions): T | undefined { + return checkOptions(options, 'initialData') as T | undefined; } -export function checkIdField(options: ReactFireOptions) { - return checkOptions(options, 'idField'); +export function checkIdField(options: ReactFireOptions): string | undefined { + return checkOptions(options, 'idField') as string | undefined; } export * from './auth'; diff --git a/src/useObservable.ts b/src/useObservable.ts index 08e8c748..ed5bcfc4 100644 --- a/src/useObservable.ts +++ b/src/useObservable.ts @@ -127,7 +127,7 @@ export function useObservable(observableId: string, source: Observa // modify the value if initialData exists if (!observable.hasValue && hasData) { - update.data = config?.initialData ?? config?.startWithValue; + update.data = (config?.initialData ?? config?.startWithValue) as T | undefined; update.status = 'success'; update.hasEmitted = true; } From b32f51a7e002980e097890e8868da444acf7687c Mon Sep 17 00:00:00 2001 From: Tyler Dixon Date: Fri, 17 Jul 2026 13:27:48 -0700 Subject: [PATCH 2/2] refactor: simplify checkIdField, remove unused checkinitialData checkIdField now reads options.idField directly (already string | undefined) instead of routing through checkOptions and casting. checkinitialData had no call sites and is removed. --- src/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index df5ce614..c6bf3af0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,12 +40,8 @@ export function checkOptions(options: ReactFireOptions, field: string) { throw new Error(`Field "${field}" is not a valid key in ReactFireOptions`); } -export function checkinitialData(options: ReactFireOptions): T | undefined { - return checkOptions(options, 'initialData') as T | undefined; -} - export function checkIdField(options: ReactFireOptions): string | undefined { - return checkOptions(options, 'idField') as string | undefined; + return options?.idField; } export * from './auth';