Skip to content

getChanges typings: upsertionChanges uses HealthConnectRecord instead of HealthConnectRecordResult #237

@shelley-xyris

Description

@shelley-xyris

Describe the bug
The TypeScript type for getChanges is incorrect.
GetChangesResults.upsertionChanges[i].record is typed as HealthConnectRecord, but at runtime it has the shape of HealthConnectRecordResult (including metadata, recordType, etc.). This causes type errors when using the result as a WeightRecord.

To Reproduce
Steps to reproduce the behavior:

  1. Call getChanges with a Weight record type:
    const changes = await getChanges({
      changesToken,
      recordTypes: ['Weight'],
    });
    
    samples.push(
      ...changes.upsertionChanges
        .map(({ record }) => record)
        .filter(
          (record): record is WeightRecord => record.recordType === 'Weight',
        )
        .map((record) => this.normalizeWeight(record)),
    );
  2. normalizeWeight expects a WeightRecord:
    private normalizeWeight(record: WeightRecord): BodyMassSample {
      const timestamp = new Date(record.time).toISOString();
      const kilograms = this.toKilograms(record.weight);
      const value = Number(kilograms.toFixed(3));
      const hash = buildSampleHash(timestamp, value);
      // ...
    }
  3. TypeScript reports a type error when passing record into normalizeWeight, because record is typed as HealthConnectRecord.
  4. Log changes.upsertionChanges[0].record and see the actual runtime shape:
    {
      metadata: {
        clientRecordVersion: 0,
        dataOrigin: 'com.google.android.apps.fitness',
        id: '',
        lastModifiedTime: '2025-11-26T05:06:23.219Z',
        recordingMethod: 3,
      },
      recordType: 'Weight',
      time: '2025-11-15T05:06:00Z',
      weight: {
        inGrams: 67099.9984741211,
        inKilograms: 67.0999984741211,
        inMicrograms: 67099998474.12109,
        inMilligrams: 67099998.474121094,
        inOunces: 2366.8830538972475,
        inPounds: 147.9301745620657,
      },
    }

Expected behavior
getChanges typings should match the runtime data.
GetChangesResults.upsertionChanges[i].record should be typed as HealthConnectRecordResult so that WeightRecord (and other specific record result types) can be used without unsafe casts.

Proposed change
Update the interface as follows:

// Current
export interface GetChangesResults {
  upsertionChanges: Array<{ record: HealthConnectRecord }>;
  deletionChanges: Array<{ recordId: string }>;
  nextChangesToken: string;
  changesTokenExpired: boolean;
  hasMore: boolean;
}

// Suggested
export interface GetChangesResults {
  upsertionChanges: Array<{ record: HealthConnectRecordResult }>;
  deletionChanges: Array<{ recordId: string }>;
  nextChangesToken: string;
  changesTokenExpired: boolean;
  hasMore: boolean;
}

Happy to open a PR with this change if desired.

Minimal Reproducible
The code snippets above should reproduce the issue in a fresh project that calls getChanges for Weight records.

Environment:

  • Health Connect Version: 3.5.0
  • React Native Version: 0.81.5
  • New architecture enabled: Yes
  • Using Expo
  • Android API Level: 36

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions