如何在React Native+TypeScript中定义带组件属性的函数组件?
在React Native TypeScript中实现带Animated复合组件的CharText组件
已知在TypeScript中可以通过定义包含函数签名和属性的类型,给函数添加额外属性,示例如下:
// 定义函数类型,包含函数签名和usedBy属性 type VerifyAgeFunc = { (age: number): boolean; usedBy: string; }; // 实现函数并断言类型 let verifyAge = <VerifyAgeFunc>((age: number) => age > 18); // 添加属性 verifyAge.usedBy = "Admin";
但在React Native的JSX环境中,要实现类似逻辑:定义CharText组件,并为其添加CharText.Animated复合组件(类似官方组件的复合写法),之前的两种尝试都存在问题:
第一种尝试:直接类型断言与JSX冲突
interface CharTextFunc { (props: CharTextProps): ReactNode; Animated: (props: CharTextProps) => ReactNode; } const CharText = <CharTextFunc,>({ mapStyle, style, children, ...restProps }: CharTextProps) => ( <View style={styles.container} {...restProps}> {[...children].map((chr, index) => ( <Text key={`${chr}-${index}`} style={[styles.char, style, mapStyle && mapStyle(index, chr)]}> {chr} </Text> ))} </View> );
问题:为区分JSX语法添加逗号后仍无法生效,且CharTextFunc出现“已定义但未使用”的警告,类型断言与JSX语法直接冲突。
第二种尝试:高阶组件方案属性可选
interface CharTextFunc { (props: CharTextProps): ReactNode; Animated?: (props: CharTextProps) => ReactNode; } type TextComponent = typeof Animated.Text | typeof Text; const makeCharText = (Text: TextComponent): CharTextFunc => { return ({ mapStyle, style, children, ...restProps }: CharTextProps) => ( <View style={styles.container} {...restProps}> {[...children].map((chr, index) => ( <Text key={`${chr}-${index}`} style={[styles.char, style, mapStyle && mapStyle(index, chr)]}> {chr} </Text> ))} </View> ); }; const CharText = makeCharText(Text); CharText.Animated = makeCharText(Animated.Text);
问题:Animated属性设为可选时,调用CharText.Animated会触发类型警告;设为必填则回到类型断言与JSX冲突的问题。
正确实现方案
1. 定义Props和组件类型
import React, { ReactNode } from 'react'; import { View, Text, Animated, StyleSheet } from 'react-native'; // 定义CharText的Props类型 interface CharTextProps { mapStyle?: (index: number, char: string) => object; style?: object; children: string; [key: string]: any; // 支持View组件的其他属性 } // 定义CharText组件的完整类型:既是函数组件,又包含必填的Animated复合组件 type CharTextComponent = ((props: CharTextProps) => ReactNode) & { Animated: (props: CharTextProps) => ReactNode; };
2. 分别实现基础组件和Animated组件
// 基础CharText组件实现 const CharTextBase = ({ mapStyle, style, children, ...restProps }: CharTextProps) => ( <View style={styles.container} {...restProps}> {[...children].map((chr, index) => ( <Text key={`${chr}-${index}`} style={[styles.char, style, mapStyle && mapStyle(index, chr)]} > {chr} </Text> ))} </View> ); // Animated版本的CharText组件 const CharTextAnimated = ({ mapStyle, style, children, ...restProps }: CharTextProps) => ( <View style={styles.container} {...restProps}> {[...children].map((chr, index) => ( <Animated.Text key={`${chr}-${index}`} style={[styles.char, style, mapStyle && mapStyle(index, chr)]} > {chr} </Animated.Text> ))} </View> );
3. 组合组件并添加复合属性
// 将基础组件断言为CharTextComponent类型,允许添加Animated属性 const CharText = CharTextBase as CharTextComponent; // 挂载Animated组件到基础组件上 CharText.Animated = CharTextAnimated; // 基础样式定义 const styles = StyleSheet.create({ container: { flexDirection: 'row', flexWrap: 'wrap', }, char: { marginHorizontal: 2, // 可添加其他基础字符样式 }, }); export default CharText;
4. 使用方式
// 使用基础CharText组件 <CharText style={{ fontSize: 24, color: '#333' }}>Hello World</CharText> // 使用Animated版本 const animatedStyle = useAnimatedStyle(() => ({ opacity: 1, transform: [{ translateY: 0 }], })); <CharText.Animated style={animatedStyle}>Hello Animated</CharText>
内容的提问来源于stack exchange,提问作者jcubic
相关产品推荐
相关产品推荐

