Skip to content

Commit dae84b0

Browse files
committed
test(hooks): migrate deprecation pins to tsd, matching PR #294's wiring
Replace the session-invented eslint typetest convention with the tsd infrastructure PR #294 introduces, copied as closely as possible so whichever PR merges second reconciles trivially: tsd ^0.33.0, the 'typetest' script (tsd --typings src/index.tsx), the package.json tsd block (directory src/__tests__, paths/moduleResolution/jsx options), jest testPathIgnorePatterns for *.test-d.ts, eslint + root-tsconfig excludes for *.test-d.ts. The pins live in src/__tests__/useViewModelInstance.test-d.ts as explicit assertions: expectDeprecated for every call that must resolve to a deprecated overload (bare, async: false, widened boolean async, exported param types), expectNotDeprecated for the literal async: true forms including the { ...params, async: true } re-pin, expectType for the required-narrowing behavior, and expectError for invalid param combinations. Verified that tsd resolves deprecation per-overload for call expressions (failure messages name the resolved signature). One deliberate divergence from #294: CI gates 'yarn typetest' in the lint job (theirs wires the script without a CI step) — keep the step when the branches meet.
1 parent 8b8c6f2 commit dae84b0

7 files changed

Lines changed: 357 additions & 128 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ jobs:
5757
- name: Typecheck files
5858
run: yarn typecheck
5959

60+
# Overload/deprecation pins (src/__tests__/*.test-d.ts). PR #294 adds
61+
# the same tsd wiring without gating it; keep this step when merging.
62+
- name: Type tests
63+
run: yarn typetest
64+
6065
test:
6166
runs-on: ubuntu-latest
6267
steps:

eslint.config.mjs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,15 @@ export default defineConfig([
4949
'@typescript-eslint/no-deprecated': 'error',
5050
},
5151
},
52-
{
53-
// Type-test files pin overload/deprecation resolution: a
54-
// no-deprecated disable there asserts "this call IS deprecated", so an
55-
// unused directive means the resolution regressed and must fail lint.
56-
files: ['**/*.typetest.{ts,tsx}'],
57-
linterOptions: {
58-
reportUnusedDisableDirectives: 'error',
59-
},
60-
},
6152
{
6253
ignores: [
6354
'node_modules/',
6455
'lib/',
6556
'**/.expo/',
6657
'**/.harness/',
58+
// tsd owns *.test-d.ts (yarn typetest) — deprecated calls there are
59+
// intentional expectDeprecated() pins, not violations.
60+
'**/*.test-d.ts',
6761
// Agent worktrees (.claude/worktrees/*) are gitignored full-repo copies;
6862
// linting them duplicates every finding and slows the run.
6963
'**/.claude/',

package.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"lint:swift": "./scripts/lint-swift.sh",
4848
"lint:kotlin": "./scripts/lint-kotlin.sh",
4949
"lint:fix:kotlin": "./scripts/lint-fix-kotlin.sh",
50-
"lint:native": "yarn lint:swift && yarn lint:kotlin"
50+
"lint:native": "yarn lint:swift && yarn lint:kotlin",
51+
"typetest": "tsd --typings src/index.tsx"
5152
},
5253
"keywords": [
5354
"react-native",
@@ -101,6 +102,7 @@
101102
"react-native-nitro-modules": "0.35.10",
102103
"react-test-renderer": "19.0.0",
103104
"release-it": "^17.10.0",
105+
"tsd": "^0.33.0",
104106
"turbo": "^1.10.7",
105107
"typescript": "^5.2.2"
106108
},
@@ -126,6 +128,10 @@
126128
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
127129
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
128130
],
131+
"testPathIgnorePatterns": [
132+
"/node_modules/",
133+
"\\.test-d\\.ts$"
134+
],
129135
"modulePathIgnorePatterns": [
130136
"<rootDir>/example/node_modules",
131137
"<rootDir>/lib/"
@@ -191,6 +197,20 @@
191197
]
192198
]
193199
},
200+
"tsd": {
201+
"typings": "src/index.tsx",
202+
"directory": "src/__tests__",
203+
"compilerOptions": {
204+
"paths": {
205+
"@rive-app/react-native": ["./src/index"]
206+
},
207+
"moduleResolution": "bundler",
208+
"jsx": "react-jsx",
209+
"lib": ["ESNext"],
210+
"strict": true,
211+
"allowArbitraryExtensions": true
212+
}
213+
},
194214
"create-react-native-library": {
195215
"languages": "kotlin-swift",
196216
"type": "nitro-module",
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* tsd pins for useViewModelInstance's overload resolution (`yarn typetest`).
3+
*
4+
* - expectDeprecated: the call must resolve to a @deprecated overload
5+
* (any call not provably `async: true` — bare, async: false, widened
6+
* params, exported param types).
7+
* - expectNotDeprecated: literal `async: true` calls must stay clean.
8+
* - expectError pins invalid param combinations.
9+
* - expectType pins the `required` narrowing behavior.
10+
*/
11+
import {
12+
expectDeprecated,
13+
expectNotDeprecated,
14+
expectError,
15+
expectType,
16+
expectAssignable,
17+
} from 'tsd';
18+
import { useViewModelInstance } from '../index';
19+
import type {
20+
UseViewModelInstanceRequiredResult,
21+
UseViewModelInstanceResult,
22+
} from '../index';
23+
import type { UseViewModelInstanceFileParams } from '../hooks/useViewModelInstance';
24+
import type { RiveFile } from '../specs/RiveFile.nitro';
25+
import type { ViewModel } from '../specs/ViewModel.nitro';
26+
import type { RiveViewRef } from '../index';
27+
28+
declare const file: RiveFile;
29+
declare const nullableFile: RiveFile | null | undefined;
30+
declare const vm: ViewModel;
31+
declare const ref: RiveViewRef;
32+
33+
// ── must resolve to a @deprecated overload ───────────────────────────
34+
expectDeprecated(useViewModelInstance(file));
35+
expectDeprecated(useViewModelInstance(nullableFile));
36+
expectDeprecated(useViewModelInstance(file, { instanceName: 'X' }));
37+
expectDeprecated(useViewModelInstance(file, { async: false }));
38+
expectDeprecated(useViewModelInstance(vm, { name: 'X' }));
39+
expectDeprecated(useViewModelInstance(ref));
40+
41+
// widened `async: boolean` params resolve to the deprecated overload by
42+
// design — the deprecation message tells callers to re-pin with
43+
// `{ ...params, async: true }`
44+
declare const widened: { async: boolean };
45+
expectDeprecated(useViewModelInstance(file, widened));
46+
47+
// values of the exported param types (async?: boolean) do too
48+
declare const viaExportedType: UseViewModelInstanceFileParams;
49+
expectDeprecated(useViewModelInstance(file, viaExportedType));
50+
51+
// ── must stay non-deprecated ─────────────────────────────────────────
52+
expectNotDeprecated(useViewModelInstance(file, { async: true }));
53+
expectNotDeprecated(useViewModelInstance(nullableFile, { async: true }));
54+
expectNotDeprecated(
55+
useViewModelInstance(file, { async: true, artboardName: 'Main' })
56+
);
57+
expectNotDeprecated(
58+
useViewModelInstance(file, { async: true, viewModelName: 'Settings' })
59+
);
60+
expectNotDeprecated(useViewModelInstance(vm, { async: true, useNew: true }));
61+
expectNotDeprecated(useViewModelInstance(ref, { async: true }));
62+
// the documented fix for widened params re-pins the literal at the call:
63+
expectNotDeprecated(
64+
useViewModelInstance(file, { ...viaExportedType, async: true })
65+
);
66+
67+
// ── result types ─────────────────────────────────────────────────────
68+
// required narrowing needs a literal async AND a non-nullable source
69+
expectType<UseViewModelInstanceRequiredResult>(
70+
useViewModelInstance(file, { async: true, required: true })
71+
);
72+
// nullable source falls back to the standard result (runtime still throws)
73+
expectType<UseViewModelInstanceResult>(
74+
useViewModelInstance(nullableFile, { async: true, required: true })
75+
);
76+
// widened params keep the standard result shape
77+
expectType<UseViewModelInstanceResult>(useViewModelInstance(file, widened));
78+
// the required result is a subset of the standard result
79+
expectAssignable<UseViewModelInstanceResult>(
80+
useViewModelInstance(file, { async: true, required: true })
81+
);
82+
83+
// ── invalid combinations must not compile ────────────────────────────
84+
// artboardName and viewModelName are mutually exclusive
85+
expectError(
86+
useViewModelInstance(file, {
87+
async: true,
88+
artboardName: 'A',
89+
viewModelName: 'B',
90+
})
91+
);
92+
// file-source params on a ViewModel source
93+
expectError(useViewModelInstance(vm, { async: true, artboardName: 'A' }));

src/hooks/__tests__/useViewModelInstance.deprecation.typetest.tsx

Lines changed: 0 additions & 109 deletions
This file was deleted.

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@
2626
"target": "ESNext",
2727
"verbatimModuleSyntax": true
2828
},
29-
"exclude": ["expo-example", "expo55-example", "expo57-example"]
29+
"exclude": ["expo-example", "expo55-example", "expo57-example", "**/*.test-d.ts"]
3030
}

0 commit comments

Comments
 (0)