Skip to content

Latest commit

 

History

History
266 lines (198 loc) · 8.05 KB

TS_CHEATSHEET.md

File metadata and controls

266 lines (198 loc) · 8.05 KB

Expensify TypeScript React Native CheatSheet

Table of Contents

CheatSheet

  • 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 from react-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 the language 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 of isManager is a boolean 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.