Found while reviewing the @tanstack/ai-octane port (#1000), which mirrored the same signature. Fixed there; filing so the other adapters can catch up.
Problem
useAudioRecorder's transforming overload is:
export function useAudioRecorder<
TOnComplete extends (recording: AudioRecording) => unknown,
>(
options: UseAudioRecorderOptions<TOnComplete>,
): UseAudioRecorderReturn<InferAudioRecordingOutput<TOnComplete>>
onComplete is optional in UseAudioRecorderOptions, so an options object carrying only unrelated keys still matches this overload. TOnComplete then infers as unknown, and both recording and the resolved value of stop() collapse to unknown — so passing any option at all silently costs you the AudioRecording type.
Reproduction
Against packages/ai-react/src/use-audio-recorder.ts on main, this type-checks clean — i.e. the collapse is real:
import { expectTypeOf } from 'vitest'
import { useAudioRecorder } from './src/use-audio-recorder'
const { recording, stop } = useAudioRecorder({ onError: (_e: Error) => {} })
expectTypeOf(recording).toBeUnknown() // passes
expectTypeOf(stop).returns.toEqualTypeOf<Promise<unknown>>() // passes
useAudioRecorder() (no argument) and useAudioRecorder({ onComplete }) are both fine — it's specifically "options, but no onComplete" that breaks.
Fix
Require onComplete on the transforming overload so those calls fall through to the untransformed one:
export function useAudioRecorder<
TOnComplete extends (recording: AudioRecording) => unknown,
>(
- options: UseAudioRecorderOptions<TOnComplete>,
+ options: UseAudioRecorderOptions<TOnComplete> & { onComplete: TOnComplete },
): UseAudioRecorderReturn<InferAudioRecordingOutput<TOnComplete>>
No valid call site breaks — it only stops previously-broken inference. See packages/ai-octane/src/use-audio-recorder.tsrx and the type tests in packages/ai-octane/typetests/use-generation.test-d.ts.
Affected
(@tanstack/ai-octane already fixed in #1000.)
Found while reviewing the
@tanstack/ai-octaneport (#1000), which mirrored the same signature. Fixed there; filing so the other adapters can catch up.Problem
useAudioRecorder's transforming overload is:onCompleteis optional inUseAudioRecorderOptions, so an options object carrying only unrelated keys still matches this overload.TOnCompletethen infers asunknown, and bothrecordingand the resolved value ofstop()collapse tounknown— so passing any option at all silently costs you theAudioRecordingtype.Reproduction
Against
packages/ai-react/src/use-audio-recorder.tsonmain, this type-checks clean — i.e. the collapse is real:useAudioRecorder()(no argument) anduseAudioRecorder({ onComplete })are both fine — it's specifically "options, but noonComplete" that breaks.Fix
Require
onCompleteon the transforming overload so those calls fall through to the untransformed one:No valid call site breaks — it only stops previously-broken inference. See
packages/ai-octane/src/use-audio-recorder.tsrxand the type tests inpackages/ai-octane/typetests/use-generation.test-d.ts.Affected
@tanstack/ai-react@tanstack/ai-vue@tanstack/ai-solid@tanstack/ai-svelte@tanstack/ai-angular(
@tanstack/ai-octanealready fixed in #1000.)