@rootnative/inertia 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to `@rootnative/inertia` are documented here. The format fol
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.0.9] - 2026-08-22
8
+
9
+ ### Changed
10
+
11
+ - **`useInterpolatedStyle` types its return against the map it was given, so a `style` array no longer needs a cast.** The hook returned `ReturnType<typeof useAnimatedStyle>`, which resolves to Reanimated's `DefaultStyle` — the union `ViewStyle | ImageStyle | TextStyle`. Assigning that union to a single style works, but a **style array** checks every member, and `TextStyle` is not assignable to `ViewStyle` (`cursor` is `string` there and `CursorValue` here). So the documented, dominant call shape — a transform/opacity fragment spread into `style={[base, fragment]}` — was a type error, and consumers cast it away.
12
+
13
+ The return is now `InterpolatedStyle<K>`, computed from the map's own keys: view keys satisfy `StyleProp<ViewStyle>`, text-metric keys (`fontSize`, `lineHeight`, `letterSpacing`) satisfy `StyleProp<TextStyle>`, `tintColor` satisfies `StyleProp<ImageStyle>`, and transform keys collapse into the single `transform` array the worklet emits. The narrowing is exact rather than permissive — a text-only fragment is still rejected from a `ViewStyle` slot, pinned in both directions by `__type-tests__/interpolated-style.test-d.tsx`.
14
+
15
+ Types only: no runtime change, and no bundle-size change. Removing a now-unnecessary `as ViewStyle` is safe; the cast remains harmless if left in place. `InterpolatedStyle` is exported from the root barrel.
16
+
17
+ Surfaced by the `reelist` validation consumer, and it is the independent-application half of the loop reporting its first library defect.
18
+
7
19
  ## [0.0.8] - 2026-08-16
8
20
 
9
21
  ### Added
@@ -273,7 +285,8 @@ Initial alpha publish. The full initial surface is in place; APIs are still subj
273
285
  - SVG path morphing, gradient interpolation, and shared-element transitions across screens are out of scope until `0.2.x` / `1.x` per the roadmap.
274
286
  - `react-native-gesture-handler` integration (drag, pan, swipe sub-states) lands in `0.2` via the optional `@rootnative/inertia-gestures` adapter.
275
287
 
276
- [unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.8...HEAD
288
+ [unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.9...HEAD
289
+ [0.0.9]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.9
277
290
  [0.0.8]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.8
278
291
  [0.0.7]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.7
279
292
  [0.0.6]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.6
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  Declarative animation primitives for React Native, built as a thin wrapper around [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/). Inspired by Framer Motion (web) and react-spring (cross-platform).
14
14
 
15
- > **Status:** `0.0.8` — stable. Pre-`1.0.0` minor versions may break — see the root [README](https://github.com/rootnative/inertia#versioning--release).
15
+ > **Status:** `0.0.9` — stable. Pre-`1.0.0` minor versions may break — see the root [README](https://github.com/rootnative/inertia#versioning--release).
16
16
 
17
17
  ## Install
18
18
 
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ReactNode } from 'react';
3
3
  import * as react_native from 'react-native';
4
- import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
4
+ import { TextStyle, ImageStyle, ViewStyle, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
5
5
  import { M as MotionComponent, V as VariantsMap, a as MotionProps, N as NamedTransitions, b as TransitionInput, T as TransitionConfig, A as AnimatableValue, E as EasingInput, S as SpringTransition, c as TransitionName, d as VariantController } from './types-DyJpG64F.mjs';
6
6
  export { e as AnimateStyle, f as AnimationCallbackInfo, B as BoxShadowInput, D as DecayTransition, g as EasingFunction, h as EasingFunctionFactory, i as GestureSubStates, j as NoAnimationTransition, P as PerPropertyTransition, R as RegisteredTransitions, k as RepeatConfig, l as SequenceStep, m as TimingTransition, n as Transition } from './types-DyJpG64F.mjs';
7
7
  import { SharedValue, useAnimatedStyle } from 'react-native-reanimated';
@@ -689,6 +689,22 @@ type InterpolatedStyleMap = {
689
689
  } & {
690
690
  [K in ColorStyleKey]?: readonly string[];
691
691
  };
692
+ /**
693
+ * The style shape a map of `K` produces. Transform keys collapse into the
694
+ * single `transform` array the worklet emits; every other key survives under
695
+ * its own name, typed from the RN style family it belongs to.
696
+ *
697
+ * This is what lets the return narrow to `ViewStyle` at a call site whose map
698
+ * holds only view keys. Reanimated's own `useAnimatedStyle` resolves to
699
+ * `DefaultStyle` (`ViewStyle | ImageStyle | TextStyle`), and that union is
700
+ * rejected inside a `StyleProp<ViewStyle>` array — `TextStyle` fails on
701
+ * `cursor` — so a consumer had to cast.
702
+ */
703
+ type InterpolatedStyle<K extends keyof InterpolatedStyleMap> = {
704
+ [P in Exclude<K, TransformKey>]: P extends keyof TextStyle ? TextStyle[P] : P extends keyof ImageStyle ? ImageStyle[P] : P extends keyof ViewStyle ? ViewStyle[P] : never;
705
+ } & (Extract<K, TransformKey> extends never ? unknown : {
706
+ transform: NonNullable<ViewStyle['transform']>;
707
+ });
692
708
  interface UseInterpolatedStyleOptions {
693
709
  /**
694
710
  * Input range mapped onto every key's output range. Defaults to `[0, 1]`
@@ -746,7 +762,7 @@ interface UseInterpolatedStyleOptions {
746
762
  * hook stays fully declarative and hashable so unchanged maps produce zero
747
763
  * new UI-thread closures.
748
764
  */
749
- declare function useInterpolatedStyle(progress: SharedValue<number>, map: InterpolatedStyleMap, options?: UseInterpolatedStyleOptions): ReturnType<typeof useAnimatedStyle>;
765
+ declare function useInterpolatedStyle<K extends keyof InterpolatedStyleMap>(progress: SharedValue<number>, map: Pick<InterpolatedStyleMap, K>, options?: UseInterpolatedStyleOptions): InterpolatedStyle<K>;
750
766
 
751
767
  /**
752
768
  * Create an animatable value owned by JS but readable from worklets.
@@ -976,4 +992,4 @@ declare function useShadow({ from, to, progress, }: UseShadowOptions): ReturnTyp
976
992
  */
977
993
  declare function useVariants<V extends Readonly<Record<string, object>>>(variants: V, initial?: keyof V & string): VariantController<keyof V & string>;
978
994
 
979
- export { AnimatableValue, type AnimationCallback, type Animator, type BoxShadowLayer, type ColorCascadeLayer, type ColorStyleKey, EasingInput, type ExtrapolationMode, type InterpolatedStyleMap, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, MotionProps, NamedTransitions, type NumericStyleKey, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, Stagger, type StaggerProps, TRANSPARENT, type TransformKey, TransitionConfig, TransitionInput, TransitionName, type UseColorCascadeOptions, type UseColorTransitionOptions, type UseInterpolatedStyleOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, VariantsMap, applyDelay, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useAnimator, useBooleanSpring, useColorCascade, useColorTransition, useInterpolatedStyle, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useStaggerDelay, useTransform, useVariants };
995
+ export { AnimatableValue, type AnimationCallback, type Animator, type BoxShadowLayer, type ColorCascadeLayer, type ColorStyleKey, EasingInput, type ExtrapolationMode, type InterpolatedStyle, type InterpolatedStyleMap, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, MotionProps, NamedTransitions, type NumericStyleKey, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, Stagger, type StaggerProps, TRANSPARENT, type TransformKey, TransitionConfig, TransitionInput, TransitionName, type UseColorCascadeOptions, type UseColorTransitionOptions, type UseInterpolatedStyleOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, VariantsMap, applyDelay, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useAnimator, useBooleanSpring, useColorCascade, useColorTransition, useInterpolatedStyle, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useStaggerDelay, useTransform, useVariants };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ReactNode } from 'react';
3
3
  import * as react_native from 'react-native';
4
- import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
4
+ import { TextStyle, ImageStyle, ViewStyle, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
5
5
  import { M as MotionComponent, V as VariantsMap, a as MotionProps, N as NamedTransitions, b as TransitionInput, T as TransitionConfig, A as AnimatableValue, E as EasingInput, S as SpringTransition, c as TransitionName, d as VariantController } from './types-DyJpG64F.js';
6
6
  export { e as AnimateStyle, f as AnimationCallbackInfo, B as BoxShadowInput, D as DecayTransition, g as EasingFunction, h as EasingFunctionFactory, i as GestureSubStates, j as NoAnimationTransition, P as PerPropertyTransition, R as RegisteredTransitions, k as RepeatConfig, l as SequenceStep, m as TimingTransition, n as Transition } from './types-DyJpG64F.js';
7
7
  import { SharedValue, useAnimatedStyle } from 'react-native-reanimated';
@@ -689,6 +689,22 @@ type InterpolatedStyleMap = {
689
689
  } & {
690
690
  [K in ColorStyleKey]?: readonly string[];
691
691
  };
692
+ /**
693
+ * The style shape a map of `K` produces. Transform keys collapse into the
694
+ * single `transform` array the worklet emits; every other key survives under
695
+ * its own name, typed from the RN style family it belongs to.
696
+ *
697
+ * This is what lets the return narrow to `ViewStyle` at a call site whose map
698
+ * holds only view keys. Reanimated's own `useAnimatedStyle` resolves to
699
+ * `DefaultStyle` (`ViewStyle | ImageStyle | TextStyle`), and that union is
700
+ * rejected inside a `StyleProp<ViewStyle>` array — `TextStyle` fails on
701
+ * `cursor` — so a consumer had to cast.
702
+ */
703
+ type InterpolatedStyle<K extends keyof InterpolatedStyleMap> = {
704
+ [P in Exclude<K, TransformKey>]: P extends keyof TextStyle ? TextStyle[P] : P extends keyof ImageStyle ? ImageStyle[P] : P extends keyof ViewStyle ? ViewStyle[P] : never;
705
+ } & (Extract<K, TransformKey> extends never ? unknown : {
706
+ transform: NonNullable<ViewStyle['transform']>;
707
+ });
692
708
  interface UseInterpolatedStyleOptions {
693
709
  /**
694
710
  * Input range mapped onto every key's output range. Defaults to `[0, 1]`
@@ -746,7 +762,7 @@ interface UseInterpolatedStyleOptions {
746
762
  * hook stays fully declarative and hashable so unchanged maps produce zero
747
763
  * new UI-thread closures.
748
764
  */
749
- declare function useInterpolatedStyle(progress: SharedValue<number>, map: InterpolatedStyleMap, options?: UseInterpolatedStyleOptions): ReturnType<typeof useAnimatedStyle>;
765
+ declare function useInterpolatedStyle<K extends keyof InterpolatedStyleMap>(progress: SharedValue<number>, map: Pick<InterpolatedStyleMap, K>, options?: UseInterpolatedStyleOptions): InterpolatedStyle<K>;
750
766
 
751
767
  /**
752
768
  * Create an animatable value owned by JS but readable from worklets.
@@ -976,4 +992,4 @@ declare function useShadow({ from, to, progress, }: UseShadowOptions): ReturnTyp
976
992
  */
977
993
  declare function useVariants<V extends Readonly<Record<string, object>>>(variants: V, initial?: keyof V & string): VariantController<keyof V & string>;
978
994
 
979
- export { AnimatableValue, type AnimationCallback, type Animator, type BoxShadowLayer, type ColorCascadeLayer, type ColorStyleKey, EasingInput, type ExtrapolationMode, type InterpolatedStyleMap, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, MotionProps, NamedTransitions, type NumericStyleKey, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, Stagger, type StaggerProps, TRANSPARENT, type TransformKey, TransitionConfig, TransitionInput, TransitionName, type UseColorCascadeOptions, type UseColorTransitionOptions, type UseInterpolatedStyleOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, VariantsMap, applyDelay, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useAnimator, useBooleanSpring, useColorCascade, useColorTransition, useInterpolatedStyle, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useStaggerDelay, useTransform, useVariants };
995
+ export { AnimatableValue, type AnimationCallback, type Animator, type BoxShadowLayer, type ColorCascadeLayer, type ColorStyleKey, EasingInput, type ExtrapolationMode, type InterpolatedStyle, type InterpolatedStyleMap, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, MotionProps, NamedTransitions, type NumericStyleKey, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, Stagger, type StaggerProps, TRANSPARENT, type TransformKey, TransitionConfig, TransitionInput, TransitionName, type UseColorCascadeOptions, type UseColorTransitionOptions, type UseInterpolatedStyleOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, VariantsMap, applyDelay, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useAnimator, useBooleanSpring, useColorCascade, useColorTransition, useInterpolatedStyle, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useStaggerDelay, useTransform, useVariants };
package/dist/index.js CHANGED
@@ -249,7 +249,11 @@ function buildEntries(map, options) {
249
249
  function useInterpolatedStyle(progress, map, options) {
250
250
  const extrapolate = mapExtrapolation(options?.extrapolate);
251
251
  const sig = buildSignature(map, options);
252
- const entries = react.useMemo(() => buildEntries(map, options), [sig]);
252
+ const entries = react.useMemo(
253
+ () => buildEntries(map, options),
254
+ // eslint-disable-next-line react-hooks/exhaustive-deps
255
+ [sig]
256
+ );
253
257
  return reactNativeReanimated.useAnimatedStyle(() => {
254
258
  "worklet";
255
259
  const out = {};
package/dist/index.mjs CHANGED
@@ -256,7 +256,11 @@ function buildEntries(map, options) {
256
256
  function useInterpolatedStyle(progress, map, options) {
257
257
  const extrapolate = mapExtrapolation(options?.extrapolate);
258
258
  const sig = buildSignature(map, options);
259
- const entries = useMemo(() => buildEntries(map, options), [sig]);
259
+ const entries = useMemo(
260
+ () => buildEntries(map, options),
261
+ // eslint-disable-next-line react-hooks/exhaustive-deps
262
+ [sig]
263
+ );
260
264
  return useAnimatedStyle(() => {
261
265
  "worklet";
262
266
  const out = {};
package/llms.txt CHANGED
@@ -80,7 +80,7 @@ import { MotionFlatList } from '@rootnative/inertia/flat-list'
80
80
  - `useShadow({ from, to, progress })` — pure value-layer interpolator between two `ShadowConfig`s (`shadowOpacity` / `shadowRadius` / `shadowOffset` / `elevation` / `shadowColor`, plus `boxShadow` as a CSS string or `BoxShadowLayer[]` — the shadow surface on web and RN 0.76+ new-arch; multi-layer, CSS-transition padding semantics, malformed strings throw at setup) driven by a `SharedValue<number>` (0→1). Returns an animated style fragment to spread onto `style`; only emits keys present on either side, absent sides default to natural zero. The hook does not animate on its own — drive `progress` with a spring, a scroll-derived `useTransform`, or any other shared value source.
81
81
  - `useColorTransition(progress, [from, to], options?)` — pure value-layer interpolator for a single color channel, driven by a `SharedValue<number>` (0→1). Returns an animated style fragment with one color key (default `backgroundColor`; configurable via `options.key` to `color` / `borderColor` / `tintColor` / `shadowColor` / per-side border colors). For raw `SharedValue<string>` output, use `useTransform(progress, [0, 1], [from, to])` instead.
82
82
  - `useColorCascade(rest, layers, options?)` — pure value-layer interpolator compositing a **priority-ordered** stack of color layers over a base `rest` color; each `layers` entry is `{ progress: SharedValue<number>, color: string }`, later entries win as their progress rises (equivalent to the nested `focus(error(hover(rest)))` `interpolateColor` chain). `options.key` reuses `ColorStyleKey` (default `backgroundColor`). Returns a spreadable animated style fragment; memoized on a colors+key signature. Color-only by design — cascade a numeric key separately via `useInterpolatedStyle` + `useTransform` max. `useColorTransition` stays the single-layer fast path.
83
- - `useInterpolatedStyle(progress, map, options?)` — pure value-layer interpolator mapping one `SharedValue<number>` onto **N** style props at once, returned as a spreadable animated style fragment (the multi-key, style-fragment counterpart to `useTransform`'s output-range form). Numeric keys (`opacity` / `height` / `fontSize` / …) route through `interpolate`; transform keys (`translateX` / `scale` / `rotate` / …) lift into a `transform` array in map key-order (`rotate*` emit `'<n>deg'`); color keys (`backgroundColor` / `borderColor` / …) route through `interpolateColor` (a multi-stop `useColorTransition`). Stop types are compile-checked. `options.inputRange` (default `[0,1]` / evenly-spaced) and `options.extrapolate` (default `'clamp'`) apply to every key. Memoized on an order-preserving signature. For function-valued or multi-source composition, drop to `useTransform`'s worklet form or a hand-rolled `useAnimatedStyle`.
83
+ - `useInterpolatedStyle(progress, map, options?)` — pure value-layer interpolator mapping one `SharedValue<number>` onto **N** style props at once, returned as a spreadable animated style fragment (the multi-key, style-fragment counterpart to `useTransform`'s output-range form). Numeric keys (`opacity` / `height` / `fontSize` / …) route through `interpolate`; transform keys (`translateX` / `scale` / `rotate` / …) lift into a `transform` array in map key-order (`rotate*` emit `'<n>deg'`); color keys (`backgroundColor` / `borderColor` / …) route through `interpolateColor` (a multi-stop `useColorTransition`). Stop types are compile-checked. **The return type is derived from the map's own keys (`InterpolatedStyle<K>`), so it narrows to the right style family and needs no cast in a `style` array** — view keys satisfy `StyleProp<ViewStyle>`, text-metric keys (`fontSize` / `lineHeight` / `letterSpacing`) satisfy `StyleProp<TextStyle>`, `tintColor` satisfies `StyleProp<ImageStyle>`, and transform keys collapse into the emitted `transform` array. (Reanimated's `useAnimatedStyle` returns the `DefaultStyle` union, whose `TextStyle` member fails against `ViewStyle` on `cursor` inside a style array.) `options.inputRange` (default `[0,1]` / evenly-spaced) and `options.extrapolate` (default `'clamp'`) apply to every key. Memoized on an order-preserving signature. For function-valued or multi-source composition, drop to `useTransform`'s worklet form or a hand-rolled `useAnimatedStyle`.
84
84
  - `useScroll()` — returns `{ scrollX, scrollY, onScroll }` for use with `Motion.ScrollView` or `Motion.FlatList`. Scroll events fire on the UI thread. Set `scrollEventThrottle={16}` on a `Motion.ScrollView`; `Motion.FlatList` defaults it to 1.
85
85
  - `createMotionComponent<C>(C)` — wrap any component with the same Motion prop surface, inferring style from `C`.
86
86
  - `buildReleaseAnimation(transition, toValue)` — worklet-safe single-step animation builder (spring / timing / decay / no-animation). For assigning Inertia-resolved animations to shared values from inside a gesture worklet. Used internally by `@rootnative/inertia-gestures`'s `useDrag({ onRelease })`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootnative/inertia",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Declarative animation primitives for React Native, built on react-native-reanimated.",
5
5
  "license": "MIT",
6
6
  "author": "RootNative",
package/src/index.ts CHANGED
@@ -74,6 +74,7 @@ export type {
74
74
  ColorCascadeLayer,
75
75
  ColorStyleKey,
76
76
  ExtrapolationMode,
77
+ InterpolatedStyle,
77
78
  InterpolatedStyleMap,
78
79
  NumericStyleKey,
79
80
  ShadowConfig,
@@ -18,6 +18,7 @@ export {
18
18
  } from './useGesture'
19
19
  export {
20
20
  useInterpolatedStyle,
21
+ type InterpolatedStyle,
21
22
  type InterpolatedStyleMap,
22
23
  type NumericStyleKey,
23
24
  type TransformKey,
@@ -1,4 +1,5 @@
1
1
  import { useMemo } from 'react'
2
+ import type { ImageStyle, TextStyle, ViewStyle } from 'react-native'
2
3
  import {
3
4
  Extrapolation,
4
5
  interpolate,
@@ -83,6 +84,29 @@ export type InterpolatedStyleMap = {
83
84
  [K in ColorStyleKey]?: readonly string[]
84
85
  }
85
86
 
87
+ /**
88
+ * The style shape a map of `K` produces. Transform keys collapse into the
89
+ * single `transform` array the worklet emits; every other key survives under
90
+ * its own name, typed from the RN style family it belongs to.
91
+ *
92
+ * This is what lets the return narrow to `ViewStyle` at a call site whose map
93
+ * holds only view keys. Reanimated's own `useAnimatedStyle` resolves to
94
+ * `DefaultStyle` (`ViewStyle | ImageStyle | TextStyle`), and that union is
95
+ * rejected inside a `StyleProp<ViewStyle>` array — `TextStyle` fails on
96
+ * `cursor` — so a consumer had to cast.
97
+ */
98
+ export type InterpolatedStyle<K extends keyof InterpolatedStyleMap> = {
99
+ [P in Exclude<K, TransformKey>]: P extends keyof TextStyle
100
+ ? TextStyle[P]
101
+ : P extends keyof ImageStyle
102
+ ? ImageStyle[P]
103
+ : P extends keyof ViewStyle
104
+ ? ViewStyle[P]
105
+ : never
106
+ } & (Extract<K, TransformKey> extends never
107
+ ? unknown
108
+ : { transform: NonNullable<ViewStyle['transform']> })
109
+
86
110
  export interface UseInterpolatedStyleOptions {
87
111
  /**
88
112
  * Input range mapped onto every key's output range. Defaults to `[0, 1]`
@@ -262,17 +286,17 @@ function buildEntries(
262
286
  * hook stays fully declarative and hashable so unchanged maps produce zero
263
287
  * new UI-thread closures.
264
288
  */
265
- export function useInterpolatedStyle(
289
+ export function useInterpolatedStyle<K extends keyof InterpolatedStyleMap>(
266
290
  progress: SharedValue<number>,
267
- map: InterpolatedStyleMap,
291
+ map: Pick<InterpolatedStyleMap, K>,
268
292
  options?: UseInterpolatedStyleOptions,
269
- ): ReturnType<typeof useAnimatedStyle> {
293
+ ): InterpolatedStyle<K> {
270
294
  const extrapolate = mapExtrapolation(options?.extrapolate)
271
295
 
272
296
  // Order-preserving signature: the map's key order is load-bearing (transform
273
297
  // lifting emits axes in author order), so `stableSig` (which sorts keys) is
274
298
  // wrong here — sign the ordered key/output pairs plus the options directly.
275
- const sig = buildSignature(map, options)
299
+ const sig = buildSignature(map as InterpolatedStyleMap, options)
276
300
 
277
301
  // Resolve every key's plan once on the JS thread so the worklet body only
278
302
  // consumes flat arrays — consistent with the JS-thread resolver principle
@@ -280,9 +304,15 @@ export function useInterpolatedStyle(
280
304
  // principle 8). Memoized on `sig` so a fresh-but-equal map literal each
281
305
  // render yields the same `entries` reference — Reanimated then sees an
282
306
  // unchanged closure dependency and does not rebuild the UI-thread worklet.
283
- // eslint-disable-next-line react-hooks/exhaustive-deps
284
- const entries = useMemo<Entry[]>(() => buildEntries(map, options), [sig])
307
+ const entries = useMemo<Entry[]>(
308
+ () => buildEntries(map as InterpolatedStyleMap, options),
309
+ // eslint-disable-next-line react-hooks/exhaustive-deps
310
+ [sig],
311
+ )
285
312
 
313
+ // The worklet builds a `Record<string, unknown>` by design — the emitted keys
314
+ // are only known from `entries` at run time. `InterpolatedStyle<K>` is the
315
+ // static statement of that same shape, so the cast is where the two meet.
286
316
  return useAnimatedStyle(() => {
287
317
  'worklet'
288
318
  const out: Record<string, unknown> = {}
@@ -313,7 +343,7 @@ export function useInterpolatedStyle(
313
343
  }
314
344
  if (transform.length > 0) out.transform = transform
315
345
  return out
316
- })
346
+ }) as InterpolatedStyle<K>
317
347
  }
318
348
 
319
349
  declare const __DEV__: boolean