diff --git a/.changeset/soft-lizards-flash.md b/.changeset/soft-lizards-flash.md new file mode 100644 index 00000000000..a524268fc9d --- /dev/null +++ b/.changeset/soft-lizards-flash.md @@ -0,0 +1,5 @@ +--- +"@firebase/data-connect": patch +--- + +Fixed type erasure of the `Data` type diff --git a/common/api-review/data-connect.api.md b/common/api-review/data-connect.api.md index 1a698c229b4..383d1ae33e1 100644 --- a/common/api-review/data-connect.api.md +++ b/common/api-review/data-connect.api.md @@ -83,6 +83,9 @@ export function executeMutation(mutationRef: MutationRef(queryRef: QueryRef): QueryPromise; +// @public (undocumented) +export const fdcSymbol: unique symbol; + // @public export function getDataConnect(options: ConnectorConfig): DataConnect; @@ -124,7 +127,9 @@ export type OnErrorSubscription = (err?: FirebaseError) => void; export type OnResultSubscription = (res: QueryResult) => void; // @public (undocumented) -export interface OperationRef<_Data, Variables> { +export interface OperationRef { + // (undocumented) + [fdcSymbol]?: Data; // (undocumented) dataConnect: DataConnect; // (undocumented) diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index acbe1a6ab22..d5a3006599e 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -68,7 +68,7 @@ "bugs": { "url": "https://github.com/firebase/firebase-js-sdk/issues" }, - "typings": "dist/src/index.d.ts", + "typings": "./dist/public.d.ts", "nyc": { "extension": [ ".ts" diff --git a/packages/data-connect/src/api/Reference.ts b/packages/data-connect/src/api/Reference.ts index f9d7687dd18..62538a9b204 100644 --- a/packages/data-connect/src/api/Reference.ts +++ b/packages/data-connect/src/api/Reference.ts @@ -30,11 +30,14 @@ export interface OpResult { fetchTime: string; } -export interface OperationRef<_Data, Variables> { +export const fdcSymbol = Symbol(); + +export interface OperationRef { name: string; variables: Variables; refType: ReferenceType; dataConnect: DataConnect; + [fdcSymbol]?: Data; // Never used, just here to ensure that the Data type doesn't get erased. } export interface DataConnectResult extends OpResult { diff --git a/packages/data-connect/test/unit/typings.test.ts b/packages/data-connect/test/unit/typings.test.ts new file mode 100644 index 00000000000..d3937e98c65 --- /dev/null +++ b/packages/data-connect/test/unit/typings.test.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { deleteApp, initializeApp } from '@firebase/app'; +import { DataConnect, getDataConnect, QueryRef, queryRef } from '../../src/api'; +describe('Typings', () => { + it('should properly infer the type', async () => { + interface MyData { + extraField: boolean; + } + const app = initializeApp({ projectId: 'a' }, 'test'); + const extendedType = Object.assign( + queryRef( + getDataConnect({ connector: 'c', location: 'l', service: 'n' }), + '', + undefined + ), + { + __abc: true + } + ); + function myFn( + queryRef: QueryRef + ): { data: Data } { + const data = {} as Data; + return { data }; + } + myFn(extendedType).data.extraField; + await deleteApp(app); + }); +});