chore: Migrate with* type tests to TSTyche#9562
Conversation
The __typetests__ suites assert types through tsc failures and ~220 `@ts-expect-error` directives, which match any error on the following line and give no per-test reporting. Start migrating them to TSTyche, which runs on the project's own TypeScript and produces named, isolated assertions. - Add the `tstyche` dev dependency and a `type:check:tstyche` script. - Add tstyche.config.json scoped to packages/*/__typetests__. - Convert FeatureFlagTest.tsx to FeatureFlagTest.tst.ts as the first test. - Make scripts/test-ts.sh skip *.tst.* so the old and new harnesses run side by side and files can be migrated one at a time. Builds on the TSTyche proof of concept from #7727.
Co-authored-by: Tom Mrazauskas <tom@mrazauskas.de>
The 7.2.1 bump could not be installed: the repo's 8-day npm age gate quarantines it (published 2026-05-27), and the bump left yarn.lock pinned to 4.1.0, which breaks `yarn install --immutable`. - Add tstyche to npmPreapprovedPackages in .yarnrc.yml. - Update yarn.lock to TSTyche 7.2.1.
TSTyche v7 changed the default configuration file name from tstyche.config.json to tstyche.json. Without the rename v7 auto-renames it at runtime and prints a warning.
CI runs type tests only through each package's `yarn type:check` -> `type:check:tests`, so the standalone `type:check:tstyche` script left the .tst.* tests unenforced now that the old harness skips them. Call `type:check:tstyche`, scoped to the package, from `type:check:tests` so CI keeps checking them. Scoped by package path to avoid cross-package build coupling; worklets stays unwired until it has its first .tst.* test.
Convert the withDecay/withTiming/withSpring __typetests__ from `@ts-expect-error` assertions to TSTyche: - withDecay: DecayConfig validation becomes toBeCallableWith / not.toBeCallableWith. - withTiming / withSpring: assert the generic `=> T` contract via toBeAssignableTo, plus a callback-acceptance check. Remove the old .tsx files; the new .tst.ts run through type:check:tests.
Rewrite the withTiming and withSpring tests to follow the original __typetests__ more closely: keep the `useAnimatedStyle` worklet with the width/backgroundColor style assignments and assert the call type-checks via `expect(useAnimatedStyle).type.toBeCallableWith(...)`, rather than checking the animation's return type in isolation.
6095d7a to
c252dc0
Compare
| expect(useAnimatedStyle).type.toBeCallableWith(() => ({ | ||
| backgroundColor: withSpring('rgba(255,105,180,0)', {}, (_finished) => {}), | ||
| })); |
There was a problem hiding this comment.
Nice to see the structure in tests.
I only noticed that in these (and in withTiming) tests useAnimatedStyle is passed to expect although the intent is to test the withSpring function. What about something like:
| expect(useAnimatedStyle).type.toBeCallableWith(() => ({ | |
| backgroundColor: withSpring('rgba(255,105,180,0)', {}, (_finished) => {}), | |
| })); | |
| expect(withSpring('rgba(255,105,180,0)', {}, (_finished) => {})).type.toBe<'rgba(255,105,180,0)'>() |
By the way, the original test looks like an integration story. Perhaps it is worth asserting the return type and keeping the rest too:
describe('withSpring', () => {
test('animates backgroundColor with a color string toValue', () => {
const backgroundColor = withSpring('rgba(255,105,180,0)', {}, (_finished) => {})
expect(backgroundColor).type.toBe<'rgba(255,105,180,0)'>()
const style = useAnimatedStyle(() => {
return { backgroundColor };
});
<View>
<Animated.View style={style} />
</View>
});
});There was a problem hiding this comment.
Hey! Thanks for your suggestion but I'd actually prefer to test the contract between useAnimatedStyle and the callback function that it is passed instead of testing the withSpring return type directly. Another problem here is that Reanimated uses fake cast of the type returned by withSpring to the same type as the input value, which is not 100% valid as the returned value is, in fact, an object representing an animation. Reanimated needs a huge type refactor in this area so that types represent the actual values but this is something we don't have time for now.
Because of that, I'd rather not test the return type of withSpring directly and keep the compatibility (integration) tests for now, until we refine the internal types.
There was a problem hiding this comment.
Ah.. This makes sense. Thanks for the details.
Summary
Continues the TSTyche migration started in #9557 by converting the
with*animation__typetests__and wiring the runner into CI.withDecay,withTiming,withSpringfrom@ts-expect-errorassertions to TSTyche (.tst.ts):withDecay:DecayConfigvalidation becomestoBeCallableWith/not.toBeCallableWith.withTiming/withSpring: assert the generic=> Tcontract viatoBeAssignableTo, plus a callback-acceptance check.type:check:tstyche(scoped to the package) from reanimated'stype:check:tests, so the.tst.tsrun in CI through the existing command instead of a separate step.workletsstays unwired until it has its first.tst.*test.Stacked on #9557.
Test plan
yarn workspace react-native-reanimated type:check:testspasses (old harness + TSTyche).yarn type:check:tstychepasses.