跳到主要内容

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)​

  1. 安装 @rive-app/canvas
    npm install --save-dev @rive-app/canvas

    生成器用它读取 .riv 文件。它只是开发依赖,不会打包进你的应用。

  2. 生成模式
    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。

  3. 导入 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。

看完还有疑问?进群交流下!
与众多 Rive 创作者、开发者一起交流探讨与答疑解惑。
加入交流群