-
1.1
props.children
import type ChildrenProps from '@src/types/utils/ChildrenProps'; type WrapperComponentProps = ChildrenProps & { ... }; function WrapperComponent({ children }: WrapperComponentProps) { return <View>{children}</View>; } function App() { return ( <WrapperComponent> <View /> </WrapperComponent> ); }
-
1.2
forwardRef
// CustomTextInput.tsx import { forwardRef, useRef, ReactNode, ForwardedRef } from "react"; import { TextInput, View } from "react-native"; export type CustomTextInputProps = { label: string; children?: ReactNode; }; function CustomTextInput(props: CustomTextInputProps, ref: ForwardedRef<TextInput>) { return ( <View> <TextInput ref={ref} /> {props.children} </View> ); }; export default forwardRef(CustomTextInput); // ParentComponent.tsx function ParentComponent() { const ref = useRef<TextInput>(); return <CustomTextInput ref={ref} label="Press me" />; }
-
1.3 Style Props
Use
StyleProp<T>
to type style props. For pass-through style props, use types exported fromreact-native
for the type parameter (e.g.ViewStyle
).import { StyleProp, ViewStyle, TextStyle, ImageStyle } from "react-native"; type MyComponentProps = { containerStyle?: StyleProp<ViewStyle>; textStyle?: StyleProp<TextStyle>; imageStyle?: StyleProp<ImageStyle>; }; function MyComponent({ containerStyle, textStyle, imageStyle }: MyComponentProps) = { <View style={containerStyle}> <Text style={textStyle}>Sample Image</Text> <Image style={imageStyle} src={'https://sample.com/image.png'} /> </View> }
- 1.4 Animated styles
The recommended approach to creating animations is by using the react-native-reanimated
library,
as it offers greater efficiency and convenience compared to using the Animated
API directly from
React Native.
import React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import Animated, { useAnimatedStyle, useSharedValue, withTiming, SharedValue, WithTimingConfig } from 'react-native-reanimated';
type MyComponentProps = {
opacity: Animated.SharedValue<number>;
};
const MyComponent = ({ opacity }: MyComponentProps) => {
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
return (
<Animated.View style={[styles.box, animatedStyle]} />
);
};
const App = () => {
const opacity = useSharedValue(0);
const startAnimation = () => {
opacity.value = withTiming(1, {
duration: 1000,
easing: Easing.inOut(Easing.quad),
});
};
return (
<View style={styles.container}>
<MyComponent opacity={opacity} />
<Button title="Animate" onPress={startAnimation} />
</View>
);
};
-
1.5 Render Prop
type ParentComponentProps = { children: (label: string) => React.ReactNode; }; function ParentComponent({ children }: ParentComponentProps) { return children("String being injected"); } function App() { return ( <ParentComponent> {(label) => ( <View> <Text>{label}</Text> </View> )} </ParentComponent> ); }
-
1.6 Type Narrowing Narrow types down using
typeof
, discriminated unions, or custom type guards. Refer to this guide for more information on when to use discriminated unions and custom type guards.type Manager = { role: "manager"; team: string; }; type Engineer = { role: "engineer"; language: "ts" | "js" | "php"; }; function introduce(employee: Manager | Engineer) { console.log(employee.team); // TypeScript errors: Property 'team' does not exist on type 'Manager | Engineer'. if (employee.role === "manager") { console.log(`I manage ${employee.team}`); // employee: Manager } else { console.log(`I write ${employee.language}`); // employee: Engineer } }
In the above code, type narrowing is used to determine whether an employee object is a Manager or an Engineer based on the role property, allowing safe access to the
team
property for managers and thelanguage
property for engineers.We can also create a custom type guard function.
function isManager(employee: Manager | Engineer): employee is Manager { return employee.role === "manager"; } function introduce(employee: Manager | Engineer) { if (isManager(employee)) { console.log(`I manage ${employee.team}`); // employee: Manager } }
In the above code,
employee is Manager
is a type predicate. It means that the return type ofisManager
is aboolean
that indicates whether a value passed to the function is of a certain type (e.g.Manager
).
-
1.7 Error in Try-Catch Clauses
Errors in try/catch clauses are inferred as
unknown
. If the error data needs to be accessed, the type of the error needs to be checked and narrowed down.try { .... } catch (e) { // `e` is `unknown`. if (e instanceof Error) { // you can access properties on Error console.error(e.message); } }
-
1.8 Use const assertions for rigorous typing
Use
as const
when you want to ensure that the types and values are as exact as possible and prevent unwanted mutations.const greeting1 = "hello"; // type: string const greeting2 = "goodbye" as const; // type: "goodbye" const person1 = { name: "Alice", age: 20 }; // type: { name: string, age: number } const person2 = { name: "Bob", age: 30 } as const; // type: { readonly name: "Bob", readonly age: 30 } const array1 = ["hello", 1]; // type: (string | number)[] const array2 = ["goodbye", 2] as const; // type: readonly ["goodbye", 2]
-
1.9 Higher Order Components
Typing HOCs is hard. Refer to this article for detailed guideline on typing HOCs for different usages of HOCs.
-
1.10 Function Overloading
Use function overloads to provide more type information for functions. For the following types of functions, function overloading can be beneficial.
- The return type depends on the input type
- When function accepts different number of parameters
- There are type dependencies between parameters
Refer to this guide to learn how to use functional overloads for each situation.