Skip to content

Commit 675514b

Browse files
authored
chore: Make ValueProcessor inputs non-mutable (#8317)
## Summary This PR changes the type of the `ValueProcessor` input to disallow mutating it in place (e.g. changing array/object elements). We don't want to allow that as we don't want to mutate the user-provided style inputs passed in props.
1 parent 14eca5b commit 675514b

File tree

4 files changed

+21
-10
lines changed

4 files changed

+21
-10
lines changed

packages/react-native-reanimated/src/common/processors/transformOrigin.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,26 +51,30 @@ function getAllowedValues(axis: Axis, isArray: boolean): string {
5151
}
5252

5353
export const ERROR_MESSAGES = {
54-
invalidTransformOrigin: (value: TransformOrigin) =>
54+
invalidTransformOrigin: (value: Readonly<TransformOrigin>) =>
5555
`Invalid transformOrigin: ${JSON.stringify(value)}. Expected 1-3 values.`,
5656
invalidValue: (
5757
value: string | number,
5858
axis: Axis,
59-
origin: TransformOrigin,
59+
origin: Readonly<TransformOrigin>,
6060
isArray: boolean
6161
) =>
6262
`Invalid value "${value}" for the ${axis}-axis in transformOrigin ${JSON.stringify(
6363
origin
6464
)}. Allowed values: ${getAllowedValues(axis, isArray)}.`,
6565
};
6666

67-
function maybeSwapComponents(components: (string | number)[]) {
67+
function maybeSwapComponents(components: ReadonlyArray<string | number>) {
6868
if (
6969
components[0] in VERTICAL_CONVERSIONS &&
7070
(components[1] === undefined || components[1] in HORIZONTAL_CONVERSIONS)
7171
) {
72-
[components[0], components[1]] = [components[1], components[0]];
72+
const copy = [...components];
73+
[copy[0], copy[1]] = [copy[1], copy[0]];
74+
return copy;
7375
}
76+
77+
return components;
7478
}
7579

7680
function parseValue(
@@ -118,14 +122,14 @@ export const processTransformOrigin: ValueProcessor<TransformOrigin> = (
118122
value
119123
) => {
120124
const isArray = Array.isArray(value);
121-
const components = isArray ? value : value.split(/\s+/);
125+
let components = typeof value === 'string' ? value.split(/\s+/) : value;
122126
const customParse = isArray ? () => null : parsePx;
123127

124128
if (components.length < 1 || components.length > 3) {
125129
throw new ReanimatedError(ERROR_MESSAGES.invalidTransformOrigin(value));
126130
}
127131

128-
maybeSwapComponents(components);
132+
components = maybeSwapComponents(components);
129133

130134
return [
131135
parseValue(

packages/react-native-reanimated/src/common/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
'use strict';
22
export type Maybe<T> = T | null | undefined;
33

4+
/**
5+
* Makes only mutable types (objects, arrays) readonly while leaving primitive
6+
* types unchanged. This prevents type issues caused by making other types
7+
* readonly, like Readonly<string> which isn't the same as string.
8+
*/
9+
export type NonMutable<T> = T extends object ? Readonly<T> : T;
10+
411
export type ValueProcessor<V, R = V> = (
5-
value: V
12+
value: NonMutable<V>
613
) => Maybe<R> | Record<string, R>;
714

815
export type TransformOrigin = string | Array<string | number>;

packages/react-native-reanimated/src/css/native/style/processors/colors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '../../../../common';
99

1010
export const ERROR_MESSAGES = {
11-
invalidColor: (color: Maybe<ColorValue | number>) =>
11+
invalidColor: (color: Readonly<ColorValue | number>) =>
1212
`Invalid color value: ${String(color)}`,
1313
};
1414

packages/react-native-reanimated/src/css/web/style/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
2-
import type { Maybe } from '../../../common';
2+
import type { Maybe, NonMutable } from '../../../common';
33
import type { AnyRecord } from '../../types';
44

55
export type ValueProcessor<V> = (
6-
value: V
6+
value: NonMutable<V>
77
) => Maybe<string> | Record<string, string>;
88

99
type ProcessedProps<P extends AnyRecord> = {

0 commit comments

Comments
 (0)