diff --git a/components/common/api/create_interface_api.test.tsx b/components/common/api/create_interface_api.test.tsx index f95c4df6e53..a0cf0e2fedc 100644 --- a/components/common/api/create_interface_api.test.tsx +++ b/components/common/api/create_interface_api.test.tsx @@ -476,6 +476,49 @@ describe('createInterfaceApi', () => { expect(mutationFn).toHaveBeenCalledTimes(1) }) + it('accepts mutations defined via endpointsFor under strict function types', async () => { + // Regression: previously, the `RawEndpoints` index signature used + // `EndpointDef`, which made tuple-typed mutations like + // `MutationEndpointDefinition<[Uint8Array, string], …>` fail assignability + // under `strictFunctionTypes` because of contravariant `onMutate(variables: P)` + // callbacks. The signature now uses `EndpointDef` to side-step + // that. + type UploadedFile = { name: string } + type MyInterface = { + processImageFile: ( + fileData: number[], + filename: string, + ) => Promise<{ processedFile: UploadedFile | null }> + processPdfFile: ( + fileData: number[], + filename: string, + ) => Promise<{ processedFile: UploadedFile | null }> + } + const impl: MyInterface = { + processImageFile: (_d, n) => + Promise.resolve({ processedFile: { name: n } }), + processPdfFile: (_d, n) => + Promise.resolve({ processedFile: { name: n } }), + } + + const api = createInterfaceApi({ + actions: {}, + endpoints: { + ...endpointsFor(impl, { + processImageFile: { + mutationResponse: (result) => result.processedFile, + }, + processPdfFile: { + mutationResponse: (result) => result.processedFile, + }, + }), + }, + }) + + const result = await api.processImageFile([[1, 2, 3], 'image.png']) + expect(result).toEqual({ name: 'image.png' }) + }) + it('can create void mutations that have a parameter', async () => { const mutationFn = jest.fn((hi: string) => { return Promise.resolve() diff --git a/components/common/api/create_interface_api.ts b/components/common/api/create_interface_api.ts index db9c34cae53..03c00f3931e 100644 --- a/components/common/api/create_interface_api.ts +++ b/components/common/api/create_interface_api.ts @@ -180,7 +180,14 @@ export function createInterfaceApi< string, Function | Record >, - const RawEndpoints extends Record>, + // Use `EndpointDef` (not `any[]`) so that specific endpoint + // definitions remain assignable under `strictFunctionTypes`. With `any[]`, + // mutation callbacks like `onMutate: (variables: [a, b]) => …` fail + // contravariance checks against `(variables: any[]) => …` because tuples + // are narrower than `any[]`. Switching to `any` makes the parameter slot + // bivariant and side-steps the variance issue without losing inference on + // the concrete `RawEndpoints` type below. + const RawEndpoints extends Record>, EventDefinitions extends Record> = {}, >(config: { /**