Fala Samurai, blz?

Nesse artigo vamos introduzir uma ferramenta que permite adicionar tipagem e alguns recursos adicionais a linguagem JavaScript possibilitando melhorar a estrutura dos projetos em React Native e deixar o código escalável para facilitar a manutenção futura.

Quando iniciamos nossos projetos em React Native é comum seguir codificando sem preocupações como “qual o tipo da variável X” ou “oque a função Y vai retornar” ou se não vai retornar nada e só vai fazer alguma coisa, etc. E é natural, afinal, o Javascript permite que nossos programas sejam escritos com tipagem dinâmica, isso quer dizer que as variáveis e funções podem assumir tipos diferentes no decorrer da execução do código.

E isso pode parecer favorecer na produtividade, ou seja, na medida em que vamos escrevendo o código sem nos preocuparmos com a tipagem perdemos menos tempo.

Porém, existem situações em que essa possibilidade de escrever o código de forma dinâmica pode gerar outros problemas e essa aparência de produtividade acaba caindo por água abaixo e então vai valer a pena pensar em trabalhar com tipagem estática e é sobre isso que vamos falar nesse artigo ao introduzir o uso de Typescript com React Native.

O que é TypeScript e por que utilizar?

O TypeScript não é uma linguagem de programação e sim uma ferramenta criada pela Microsoft com o intuito de resolver alguns problemas relacionados a tipagem naturalmente dinâmica da linguagem JavaScript.

Imagine um projeto escrito em React Native em que você não mexe já faz algum tempo e precisa realizar uma manutenção ou ajuste no código.

Estando ele escrito com tipagem dinâmica, você irá precisar entender o que as funções estão retornando ou se só estão fazendo alguma coisa e de que tipo são as variáveis do código, com que valor elas iniciaram e depois se assumiram algum outro valor, etc.

Também pode acontecer que outra pessoa da equipe vá mexer no seu código e por algum motivo não tenha a mesma linha de raciocínio que você teve ao nomear as variáveis e pode pensar que a variável X que estava trabalhando com números agora possa receber um texto ou simplesmente faz isso porque foi mais fácil ali na hora de mexer e a linguagem permite.

No primeiro caso isso pode te custar mais tempo e no segundo caso pode causar erros no futuro ou confusão e uma estrutura ruim de código.

Nos dois casos temos um problema que pode ser resolvido com padronização de código e utilização do TypeScript.

Tipagem

Quando falamos de tipagem no TypeScript queremos dizer que ao declarar uma variável ou função definimos um tipo para ela e que ela vai assumir esse tipo durante todo o ciclo de vida em tempo de execução do código, ex de declaração:

let decimal: number = 6;
let color: string = "blue";

E vai acusar um erro ao tentar definir um valor diferente de number para a variável decimal ou um valor diferente de string para a variável color .

Os tipos possíveis são:

  • Boolean;
  • Number;
  • String;
  • Array;
  • Tuple;
  • Enum;
  • Any;
  • Void;
  • Null and Undefined;
  • Never;
  • Object;

Usando TypeScript no React Native

Para usar o TypeScript com React Native você pode começar um novo projeto já pré configurado com o comando:

npx react-native init MyTSProject --template [email protected]

Ou pode utilizar em um projeto já existente fazendo algumas instalações e modificações necessárias.

Para o nosso exemplo, iremos utilizar um projeto já existente que pode ser baixado nesse repositório do github da Dev Samurai:

Link para o SamuraiCam no Github

Se quiser saber como criar esse projeto do zero, acesse nosso tutorial do SamuraiCam:

React Native Tutorials: Usando a Câmera do Celular

Bom, com o projeto em mãos, a primeira coisa a fazer depois de rodar o yarn é instalar as dependências necessárias para utilizar o Typescript:

yarn add typescript @types/jest @types/react @types/react-native @types/react-test-renderer

Depois precisamos criar 2 arquivos na raiz do projeto para configurar as dependências do TypeScript.

Um deles é tsconfig.json:

{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["es6"],
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"target": "esnext"
},
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js"
]
}

O outro é jest.config.js:

module.exports = {
preset: “react-native”,
moduleFileExtensions: [“ts”, “tsx”, “js”, “jsx”, “json”, “node”]
};

Beleza, agora todo arquivo do projeto que desejamos utilizar o TypeScript precisa ser renomeado para a extensão .tsx e no nosso exemplo só temos o App.js, então vamos renomear para App.tsx.

Precisamos agora adaptar o código para que seja um arquivo TypeScript válido.

Se rodar o comando yarn tsc ele vai verificar os arquivos .tsx e irá apontar os erros encontrados.

Nosso código vai retornar 7 erros. No console você verá os erros encontrados, os locais dos arquivos e no final o total que deve estar assim Found 7 errors.

A ideia é corrigir esses erros para que o código fique padronizado com o TypeScript.

Os primeiros erros notados são em dois imports no começo do arquivo:

import Icon from 'react-native-vector-icons/MaterialIcons'; e import logo from './assets/logo.png';

Dizendo que esses módulos não foram encontrados, então nós vamos criar um arquivo chamado declarations.d.ts na raiz do projeto em que podemos centralizar a declaração dos módulos necessários:

declare module 'react-native-vector-icons/MaterialIcons';
declare module '*.png';

Outro erro que aparece são com as props do componente Camera , pois, não temos uma tipagem definida para aquelas props .

E como temos 3 tipos de props , podemos criar uma interface para definir os tipos das propriedades recebidas pelo Camera .

No arquivo declarations.d.ts adicione:

interface CameraProps {
isVisible: boolean;
onChangePhoto(uri: string): void;
onCloseCamera(): void;
}

Ou seja, criamos uma interface chamada CameraProps que possuí 3 valores:

  • isVisible é uma variável que deve receber um valor booleano;
  • onChangePhoto é uma função que deve receber sempre um parâmetro uri do tipo string e ela não retorna nenhum valor ( void );
  • onCloseCamera é uma função que não recebe nenhum parâmetro e não retorna nada ( void );

Agora la no App.tsx vamos definir que as props de Camera são do tipo CameraProps :

const Camera = ({isVisible, onChangePhoto, onCloseCamera}: CameraProps) => {...}

Depois, no componente App temos a função onChangePhoto que recebe um parâmetro newPhoto e ele deve ser tipado como string, pois, definimos lá no CameraProps que o método onChangePhoto deve receber um parâmetro string e é essa função que estamos enviando nas props do componente Camera .

const [photo, setPhoto] = useState();
const onChangePhoto = (newPhoto: string) => {
setPhoto(newPhoto);
setIsCameraVisible(false);
};

No final nosso App.tsx deve estar assim:

import React, { useState } from "react";
import {
Alert,
Modal,
View,
Image,
ImageBackground,
TouchableOpacity,
StyleSheet
} from "react-native";
import Icon from "react-native-vector-icons/MaterialIcons";
import { RNCamera } from "react-native-camera";
import logo from "./assets/logo.png";
const Camera = ({ isVisible, onChangePhoto, onCloseCamera }: CameraProps) => {
const [camera, setCamera] = useState();
const onTakePicture = async () => {
try {
const { uri } = await camera.takePictureAsync({
quality: 0.5,
forceUpOrientation: true,
fixOrientation: true,
skipProcessing: true
});
onChangePhoto(uri);
} catch (error) {
Alert.alert("Erro", "Houve um erro ao tirar a foto.");
}
};
return (
<Modal animationType="slide" transparent={false} visible={isVisible}>
<RNCamera
ref={ref => setCamera(ref)}
style={{ flex: 1 }}
type={RNCamera.Constants.Type.back}
autoFocus={RNCamera.Constants.AutoFocus.on}
flashMode={RNCamera.Constants.FlashMode.off}
androidCameraPermissionOptions={{
title: "Permissão para usar a câmera",
message: "Precisamos da sua permissão para usar a câmera.",
buttonPositive: "Ok",
buttonNegative: "Cancelar"
}}
captureAudio={false}
>
<Icon
name="photo-camera"
size={40}
color={"#fff"}
onPress={onTakePicture}
style={styles.buttonTakePicture}
/>
<Icon
name="close"
size={50}
color={"#fff"}
onPress={onCloseCamera}
style={styles.buttonCloseCamera}
/>
</RNCamera>
</Modal>
);
};
const App = () => {
const [isCameraVisible, setIsCameraVisible] = useState(false);
const [photo, setPhoto] = useState();
const onChangePhoto = (newPhoto: string) => {
setPhoto(newPhoto);
setIsCameraVisible(false);
};
const onCloseCamera = () => {
setIsCameraVisible(false);
};
return (
<View style={styles.container}>
<Image source={logo} style={styles.logo} />
<View style={styles.photo}>
<ImageBackground
style={{ width: "100%", height: "100%" }}
source={{ uri: photo }}
/>
</View>
<View style={styles.buttons}>
<TouchableOpacity
style={styles.button}
onPress={() => {
setIsCameraVisible(true);
}}
>
<Icon name="camera-alt" size={40} color={"#f37272"} />
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => {
setPhoto(null);
}}
>
<Icon name="delete" size={40} color={"#f37272"} />
</TouchableOpacity>
</View>
<Camera
isVisible={isCameraVisible}
onChangePhoto={onChangePhoto}
onCloseCamera={onCloseCamera}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f37272"
},
logo: {
alignSelf: "center",
marginTop: 60
},
photo: {
width: 300,
height: 200,
backgroundColor: "#fff",
alignSelf: "center",
marginTop: 80
},
buttons: {
marginTop: 10,
flexDirection: "row",
justifyContent: "center"
},
button: {
backgroundColor: "#fff",
margin: 20,
borderRadius: 150,
width: 80,
height: 80,
alignItems: "center",
justifyContent: "center"
},
buttonTakePicture: {
flex: 0,
alignSelf: "center",
position: "absolute",
bottom: 20
},
buttonCloseCamera: {
flex: 0,
position: "absolute",
top: 20,
right: 20
}
});
export default App;

E ao rodar o comando yarn tsc novamente para verificar os erros a mensagem deve estar parecida com essa: Done in 4.28s.

Conclusão

Utilizar o TypeScript tem diversas vantagens que podem não parecer fazer sentido para quem não está acostumado, pois, tem uma média curva de aprendizado para entender as especificações e as formas corretas de se utilizar a tipagem.

Mas é interessante conhecer e utilizar, pois, em projetos que começam a crescer em escopo ou equipe, a padronização e alinhamento entre os devs acabada se tornando crucial e aplicar tipagem e os recursos de orientação a objetos oferecidos pelo TypeScript é uma excelente escolha.

Nesse artigo tivemos apenas uma introdução básica sobre o assunto mas pretendemos criar um projeto do zero em React Native utilizando TypeScript com as melhores práticas, por isso fica de olho nos anúncios da nossa comunidade la no Discord:

Comunidade Dev Samurai no Discord

O código de exemplo utilizado aqui veio do projeto SamuraiCam que está disponível para download nesse repositório:

Link do Github para o SamuraiCam

E também temos um tutorial bem legal, ensinado o passo a passo para criar esse projeto (SamuraiCam) do zero:

React Native Tutorials: Usando a Câmera do Celular

E para você não ficar de fora dos nossos artigos e tutoriais sobre programação, carreira e desenvolvimento de aplicativos, cadastre seu e-mail e receba os anúncios fresquinhos das novidades aqui do blog

Valeu Samurai por acompanhar esse artigo e até a próxima.