TypeScript 模式(TypeScript Schemas)
TypeScript 模式需要新的 React Native 运行时,即 @rive-app/react-native 0.5.0 或更高版本。升级请参见迁移指南。
你可以为每个 .riv 文件生成一份 TypeScript 模式。有了它,画板、状态机、视图模型、属性和枚举名称会在编译期受到检查,编辑器也会自动补全它们。拼写错误会变成编译错误,而不是运行时失败。
模式是放在 .riv 文件旁边的 .riv.d.ts 文件。它列出该文件的画板、状态机、视图模型和枚举。它只包含类型,因此打包结果不会改变。
类型只在你用 import 加载 Rive 文件时生效。TypeScript 会自动找到 .riv.d.ts 文件,无需修改 tsconfig。require(...) 返回 any,因此即使存在模式也仍然无类型。这让你可以逐个文件采用模式。
import rewardsRiv from './assets/rive/rewards.riv'; // typed by rewards.riv.d.ts
const untypedRiv = require('./assets/rive/rewards.riv'); // untyped
快速开始(Quick start)
- 安装 @rive-app/canvas
npm install --save-dev @rive-app/canvas生成器用它读取
.riv文件。它只是开发依赖,不会打包进你的应用。 - 生成模式
npx rive-gen-types assets/rive/rewards.riv # writes rewards.riv.d.ts
npx rive-gen-types --all assets/rive # every .riv in the directory提交生成的文件。输出是稳定的,因此对未更改的文件重新生成不会产生 diff。
- 导入 Rive 文件
导入
.riv文件需要把riv加入 Metro 的assetExts。参见加载 Rive 文件。import { useRiveFile } from '@rive-app/react-native';
import rewardsRiv from './assets/rive/rewards.riv';
const { riveFile } = useRiveFile(rewardsRiv);
哪些内容会有类型(What gets typed)
生成的模式如下所示:
// Generated by rive-gen-types — do not edit manually. @generated
/* eslint-disable */
// Source: rewards.riv
import type { RiveFileSource } from '@rive-app/react-native';
declare const asset: RiveFileSource<{
schemaVersion: 1;
artboards: 'Main' | 'Chest' | 'Button' | 'Item';
defaultArtboard: 'Main';
stateMachines: {
'Main': 'State Machine 1';
// ...one entry per artboard
};
enums: {
Item_Selection: 'Coin' | 'Gem';
};
viewModels: {
Rewards: {
Price_Value: 'number';
Coin: 'viewModel:Item_Icon_Value';
};
Item: {
Item_Selection: 'enum:Item_Selection';
};
// ...
};
referencedAssets: {};
}>;
export default asset;
类型从导入流入,因此你不需要额外标注。RiveView 上的画板和状态机名称会受到检查,且状态机必须属于所选画板:
<RiveView
file={riveFile}
artboardName="Main" // "Mian" is an error
stateMachineName="State Machine 1"
/>
视图模型名称、属性路径和枚举值也会受到检查。每个 hook 只接受其类型对应的路径,包括嵌套属性路径:
const { instance } = useViewModelInstance(riveFile, {
viewModelName: 'Rewards', // 'Rewrads' is an error
async: true,
});
useRiveNumber('Price_Value', instance); // ✓
useRiveNumber('Coin/Item_Value', instance); // ✓ nested
useRiveTrigger('Price_Value', instance); // ✗ not a trigger
const { setValue } = useRiveEnum('Item_Selection/Item_Selection', instance);
setValue('Gem'); // ✓
setValue('Diamond'); // ✗ not a value of Item_Selection
枚举值会对照 Rive 文件中定义的值名称进行检查。要在你自己的类型中复用某个枚举的值,使用 EnumValues:
import { type EnumValues } from '@rive-app/react-native';
type ItemKind = EnumValues<typeof rewardsRiv, 'Item_Selection'>; // 'Coin' | 'Gem'
引用的资源(Referenced assets)
带外资源列在 referencedAssets 中,键是从 Rive 编辑器导出的资源名称和唯一标识。useRiveFile 的 referencedAssets 选项会对照该列表检查:未知的键或错误的 type 是编译错误。
// Schema contains: referencedAssets: { 'Inter-594377': 'font' }
const { riveFile } = useRiveFile(outOfBandRiv, {
referencedAssets: {
'Inter-594377': { source: require('./assets/Inter.ttf'), type: 'font' },
},
});
加载带外资源的方式见加载资源。
给你自己的组件加类型(Typing your own components)
要把带类型的 Rive 文件或视图模型实例传给另一个组件,使用 TypedRiveFile 和 TypedViewModelOf:
import {
type TypedRiveFile,
type TypedViewModelOf,
} from '@rive-app/react-native';
import rewardsRiv from './assets/rive/rewards.riv';
function RewardsScreen({ file }: { file: TypedRiveFile<typeof rewardsRiv> }) {
// ...
}
function PricePanel({
instance,
}: {
instance: TypedViewModelOf<typeof rewardsRiv, 'Rewards'>;
}) {
const { value } = useRiveNumber('Price_Value', instance);
// ...
}
从 URL 加载的文件(Files loaded from a URL)
.riv.d.ts 文件只为本地导入提供类型。对于 URL,用 --out 生成模式类型,并把它作为泛型传入:
npx rive-gen-types https://example.com/rewards.riv --out src/rive/rewards-schema.ts
import { RiveFileFactory, useRiveFile } from '@rive-app/react-native';
import type { RewardsSchema } from './rive/rewards-schema';
const url = 'https://example.com/rewards.riv';
const { riveFile } = useRiveFile<RewardsSchema>({ uri: url });
// Or, outside a component:
const file = await RiveFileFactory.fromURL<RewardsSchema>(url, undefined);
类型以文件命名(rewards.riv → RewardsSchema)。用 --name 可以选择另一个名称。
保持模式最新(Keeping schemas up to date)
编辑 .riv 文件后重新生成,然后修复 TypeScript 标出的问题。重命名的画板、被移除的属性以及更改过的枚举值,会在使用处显示为错误。
要在 CI 中发现过期的模式,重新生成并在有任何变化时失败:
- name: Check .riv schemas
run: |
npx rive-gen-types --all assets/rive
test -z "$(git status --porcelain -- 'assets/rive/*.riv.d.ts')"
限制(Limits)
- 嵌套属性路径的类型最多到两层(
'a/b/leaf')。更深的路径在运行时可用,但需要类型转换。 - 路径必须是字符串字面量。要在运行时拼接路径,请使用没有模式的视图模型实例。
- 列表项没有类型,因为模式无法知道每一项使用哪个视图模型。
- 嵌套视图模型只有在文件中有默认实例时才会有类型。否则它的路径没有类型。
排查(Troubleshooting)
名称没有受到检查
用 import 而不是 require(...) 加载 Rive 文件。确认同名的 .riv.d.ts 文件位于 .riv 文件旁边。
导入报告「This .riv.d.ts is out of date」
该模式是为另一个版本的 @rive-app/react-native 生成的。再次运行 npx rive-gen-types。