Typing Components and Props in React Native with TypeScript
Typing Components and Props
~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Now we'll migrate ProductCard – EXACTLY as in "React for Professionals" chapter 41, just with React Native's own event types instead of web DOM event types.
ProductCard.js to ProductCard.tsx
import { memo } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { Image } from 'expo-image';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
const BLURHASH = '|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[';
interface ProductCardProps {
name: string;
price: number;
imageUrl: string;
onPress: () => void;
isFavorite: boolean;
onToggleFavorite: () => void;
}
function ProductCard({
name,
price,
imageUrl,
onPress,
isFavorite,
onToggleFavorite,
}: ProductCardProps) {
const scale = useSharedValue(1);
const heartStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
function handleToggleFavorite() {
scale.value = withSpring(1.4, { damping: 4 }, () => {
scale.value = withSpring(1);
});
onToggleFavorite();
}
return (
<TouchableOpacity style={styles.card} onPress={onPress}>
<Image
source={imageUrl}
style={styles.image}
placeholder={{ blurhash: BLURHASH }}
contentFit="cover"
transition={200}
cachePolicy="memory-disk"
/>
<View style={styles.info}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.price}>${price.toFixed(2)}</Text>
</View>
<TouchableOpacity style={styles.favoriteButton} onPress={handleToggleFavorite}>
<Animated.Text style={[styles.favoriteIcon, heartStyle]}>
{isFavorite ? '♥' : '♡'}
</Animated.Text>
</TouchableOpacity>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
card: {
flexDirection: 'row',
padding: 12,
marginBottom: 8,
backgroundColor: '#f9fafb',
borderRadius: 8,
alignItems: 'center',
},
image: { width: 60, height: 60, borderRadius: 4 },
info: { marginLeft: 12, flex: 1 },
name: { fontSize: 16, fontWeight: '600' },
price: { fontSize: 14, color: '#6b7280' },
favoriteButton: { padding: 8 },
favoriteIcon: { fontSize: 20, color: '#ef4444' },
});
export default memo(ProductCard);Bonus: StyleSheet.create is already typed automatically
A pleasant difference from React web: StyleSheet.create({{...}}) is GENERICALLY typed – if you accidentally type an invalid style name (e.g. colr instead of color), TypeScript reports the error IMMEDIATELY, without you having to define a style type yourself. These types come directly from @types/react-native, which Expo installs automatically.
Passing style objects through as props
For components that expose their own style prop to the outside (e.g. a future, more configurable version of ProductCard), there's a matching React Native type:
import { StyleProp, ViewStyle } from 'react-native';
interface CardProps {
style?: StyleProp<ViewStyle>;
}StyleProp<ViewStyle> covers ALL valid shapes React Native accepts for styles: a single style object, an array of styles (as we use with [styles.favoriteIcon, heartStyle] in the example above), or false/null/undefined (for conditional styles like condition && styles.active).
children and generic touchable props
import { ReactNode } from 'react';
import { TouchableOpacityProps } from 'react-native';
// ReactNode works identically to "React for Professionals" chapter 41 (web):
interface WrapperProps {
children: ReactNode;
}
// Handy for inheriting ALL standard props of a native component,
// plus your own additions - saves listing every standard prop individually:
interface CustomButtonProps extends TouchableOpacityProps {
label: string;
}extends TouchableOpacityProps is a React Native-specific pattern with no direct equivalent in "React for Professionals": it automatically inherits ALL of TouchableOpacity's standard props (onPress, disabled, style, activeOpacity, ...), so your own wrapper component doesn't have to redeclare them individually.
Tipp: Rule of thumb for your own, reusable RN components: extends <NativeComponent>Props is almost ALWAYS a better starting point than a props interface written entirely from scratch – you automatically inherit all the standard functionality and only need to add the TRULY new, custom props.