TheWidlarzGroup/react-native-video

[BUG] `source` TS type rejects React Native's `ImageRequireSource` (number) for bundled assets

Open

#4,901 建立於 2026年5月19日

在 GitHub 查看
 (1 留言) (0 反應) (0 負責人)Kotlin (3,027 fork)batch import
Missing ReproV6bugdifficulty: easygood first issuepriority: mediumtriaged

倉庫指標

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

  1. Declare *.mp4 modules as ImageRequireSource (RN-conventional asset typing).
  2. Use <Video source={require('./x.mp4')} /> or <Video source={{ uri: require('./x.mp4') }} />.
  3. Run tsc - both fail with TS2322 (number not assignable to ReactVideoSource).

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

貢獻者指南