[BUG] `source` TS type rejects React Native's `ImageRequireSource` (number) for bundled assets
#4,901 创建于 2026年5月19日
仓库指标
- Star
- (7,677 star)
- PR 合并指标
- (平均合并 2天 3小时) (30 天内合并 11 个 PR)
描述
What happened?
Affects the TypeScript type-checker only — all runtime targets work correctly — when using manually declared typing for *.mp4 files.
react-native-video accepts numeric Metro asset ids at two positions at runtime (Video.tsx:205-212):
const isLocalAssetFile =
typeof _source === 'number' || // legacy `source={require('./X.mp4')}` form
('uri' in _source && typeof _source.uri === 'number') || // wrapped `source={{ uri: require('./X.mp4') }}` form
('uri' in _source &&
typeof _source.uri === 'string' &&
(_source.uri.startsWith('file://') ||
_source.uri.startsWith('content://') ||
_source.uri.startsWith('.')));
Neither numeric shape is allowed by the TS type. ReactVideoSource is currently object-only with string | NodeRequire for uri (video.ts):
export type ReactVideoSource = Readonly<Omit<ReactVideoSourceProperties, 'uri'> & {
uri?: string | NodeRequire;
}>;
A TS project that types *.mp4 requires using ImageRequireSource (since that is the runtime type returned by Metro), breaks usage of react-native-video. Even the README's own example fails to compile:
// Project-level asset declaration (RN-conventional):
declare module '*.mp4' {
const url: ImageRequireSource; // resolves to `number`
export = url;
}
const sintel = require('./sintel.mp4'); // ImageRequireSource (= number)
<Video source={sintel} /> // TS2322: number not assignable to ReactVideoSource (object type)
<Video source={{ uri: sintel }} /> // TS2322: number not assignable to string | NodeRequire
Suggested fix
Widen ReactVideoSource at both positions to match the runtime:
export type ReactVideoSource =
| ImageRequireSource
| Readonly<Omit<ReactVideoSourceProperties, 'uri'> & {
uri?: string | ImageRequireSource;
}>;
ImageRequireSource from react-native is the documented type produced by require('./X.png') / require('./X.mp4') etc. and is also what the runtime path expects. NodeRequire can stay in the uri union for backward compatibility, but as noted it's @deprecated in newer @types/node.
Why this matters
The README explicitly recommends source={{ uri: require('./video.mp4') }} as the v6.0.0-beta.6+ canonical form. In codebases following React Native's standard asset-typing convention, this canonical example doesn't compile against the v6 types. Consumers are instead forced to cast at the call site, widen workspace asset declarations to NodeRequire (which carries its own deprecation), or skip type-checking with // @ts-expect-error.
The runtime cleanly supports numeric ids at both source positions. Widening the type closes the documented "happy path" gap without changing any runtime behaviour.
Steps to reproduce
- Declare
*.mp4modules asImageRequireSource(RN-conventional asset typing). - Use
<Video source={require('./x.mp4')} />or<Video source={{ uri: require('./x.mp4') }} />. - Run
tsc- both fail withTS2322(number not assignable toReactVideoSource).
Reproduction repository
No response
react-native-video version
react-native-video: ^6.19.2 (applies to the v6 stable line — the type was tightened around v6.0.0-beta.6 per the README's deprecation note about the legacy bare-require source form)
react-native version
No response
react-native-nitro-modules version
No response
Platforms
No response
OS version
No response
Device
No response
Architecture
No response
Expo
No response
Last working version
No response
Media / source type
No response