Типизация forwarded refs в React 19: баги с ref как prop, инфер типов в HOC и render-pr...
Типизация forwarded refs в React 19: баги с ref как prop, инфер типов в HOC и render-props с useImperativeHandle React 19 сделал ref обычным prop, что на первый взгляд упрощает API. Однако на практике разработчики часто сталкиваются с неочевидными багами типов, особенно в production-коде с обёртками. Основная ошибка — игнорировать явную типизацию в угоду кажущейся простоте. 1. ref как prop без forwardRef — неочевидный баг Если передать ref как обычный prop без forwardRef, TypeScript не поймёт, что это forwarded ref: function MyInput({ ref, ...props }: { ref: React.Ref<HTMLInputElement> }) { return <input ref={ref} />; } TypeScript выдаст ошибку, так как не видит контекст Refs. Решение — оборачивать в forwardRef и явно указывать generic: const MyInput = forwardRef<HTMLInputElement, Props>((props, ref) => ...); Это гарантирует корректную инференцию типов и совместимость с React 19. 2. Инфер типов в HOC с forwarded refs — теряется связь Когда оборачиваешь компонент с ref в HOC, типы часто ломаются. Типичная ошибка: забыть явно пробросить generic для ref: function withLogger<T extends object>( Component: React.ForwardRefExoticComponent<T & { ref: React.Ref<HTMLDivElement> }> ) { return forwardRef<HTMLDivElement, T>((props, ref) => { console.log('Rendered'); return <Component {...props} ref={ref} />; }); } Без явного ForwardRefExoticComponent TypeScript будет гадать, выведя неправильный тип ref. Практический совет: всегда указывай generic для forwarded ref в HOC. 3. Render-props с useImperativeHandle — неправильный тип экземпляра useImperativeHandle в React 19 работает с forwarded refs, но типизация render-props проваливается, если не объявить интерфейс явно: interface ImperativeActions { focus: () => void; } const Input = forwardRef<ImperativeActions, {}>((_, ref) => { useImperativeHandle(ref, () => ({ focus: () => console.log('Focus') })); return <input />; }); function Outer({ children }: { children: (ref: React.RefObject<ImperativeActions>) => React.ReactNode }) { const ref = useRef<ImperativeActions>(null); return <>{children(ref)}</>; } Частая ошибка: TypeScript выводит ImperativeActions как any. Предупреждение: используй React.ElementRef<typeof Input> вместо ручного объявления, чтобы избежать рассинхронизации типов. Вывод: В React 19 явная типизация forwarded refs с generic-типами в каждом слое — не опция, а необходимость для production-кода, иначе баги с инференцией ломают компоненты с HOC и render-props.