Skip to content

Strict TypeScript API (RN 0.87 default, 0.80+ opt-in): interface augmentation of react-native cannot merge with the generated type aliases #651

Description

@jucaco18

What happened?

Uniwind types className by augmenting react-native with declare module interfaces (ViewProps, TextProps, ScrollViewProps, … in types.d.ts, pulled in via the generated d.ts's /// <reference types="uniwind/types" />).

Under React Native's Strict TypeScript API — opt-in via customConditions: ["react-native", "react-native-strict-api"] on 0.80–0.86, the default from RN 0.87 — those same props are generated type aliases, and TypeScript cannot merge an interface with a type alias:

  • With skipLibCheck: false: hard TS6200 — "Definitions of the following identifiers conflict with those in another file" — across 18 prop types (ViewProps, TextProps, PressableProps, TextInputProps, FlatListProps, ScrollViewProps, SwitchProps, …).
  • With skipLibCheck: true (the common config): the interface silently wins — root ViewProps/TextProps collapse to className-only, losing children, accessibilityLabel, everything — and it cascades into any library building on them (@shopify/flash-list, @shopify/react-native-skia, react-native-reorderable-list). 243 type errors in our app.

Two workarounds we verified do not work, to save triage time:

  • tsconfig paths stubbing cannot intercept the triple-slash type reference route (error count unchanged).
  • The generated per-app d.ts is regenerated by the CLI, so editing it is not durable.

What we ship instead (works, but is scaffolding every strict-mode app would need to reinvent): strip the /// <reference> line from the generated d.ts post-generation, and type className locally via intersection — ComponentProps<typeof View> & { className?: string } — which the Strict API supports where augmentation cannot. The runtime is unaffected throughout: the Metro nativeResolver rewrite of react-native → uniwind/components carries className regardless of types.

Ask: strict-compatible typing — e.g. a parallel types entry keyed on the react-native-strict-api custom condition, or intersection-based component typing instead of global interface augmentation. This becomes non-optional for every Uniwind app when RN 0.87 makes the Strict API the default.

Steps to Reproduce

  1. Expo SDK 57 app, RN 0.86.3, uniwind@1.11.0 wired per docs (withUniwindConfig, generated d.ts included in tsconfig).
  2. Add "customConditions": ["react-native", "react-native-strict-api"] to compilerOptions (with "moduleResolution": "bundler").
  3. tsc --noEmit → mass prop-type errors (or TS6200 with skipLibCheck: false).

Snack or Repository Link

Private repo; the measurement table and the full analysis are reproducible from the steps above (numbers: 243 errors with the augmentation loaded; 3 without it — the 3 being className itself; 0 after the intersection-typing workaround).

Uniwind version

1.11.0

React Native Version

0.86.3

Platforms

iOS, Android

Expo

Yes — SDK 57

Additional information

Filed ahead of moving this app to Uniwind Pro. Happy to test a fix build — the strict opt-in is enabled in our CI, so a compatible release gets verified within a day.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidThis doesn't seem right

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions