Back

Converting Javascript to TypeScript with React Native

Jennifer  Watanabe
Jennifer Watanabe
September 2, 2021
Converting Javascript to TypeScript with React Native

Two people high-fiving each other

Photo by krakenimages on Unsplash

If you’re like me, you learned Javascript first and want to start using TypeScript instead. Good news: TypeScript can be used anywhere Javascript can be used and it looks very similar to Javascript so it’s easy to learn!

Example #1: Interfaces

Interfaces will check the shape of our values. We define them outside the component by using the keyword interface and are conventionally Pascal cased.

interface DogBreed { averageHeight: number coatType: string countryOfOrigin: string isGoodDoggo: boolean name: string }

After defining an interface we can use it to define a value’s shape. In this example we’re saying that the value of variable maltese and russellTerrier will have all the keys and properties of the interface DogBreed.

const maltese: DogBreed = { // <- Define this value's shape as DogBreed averageHeight: 8, coatType: 'very long', countryOfOrigin: 'Malta? No one knows!', isGoodDoggo: true, name: 'Maltese', } const russellTerrier: DogBreed = { // <- Same thing here averageHeight: 11, coatType: 'mostly wiry', countryOfOrigin: 'England', isGoodDoggo: true, name: 'Russell Terrier', }

Doesn’t quite make sense?

Example #2: Unions (“Or”)

Unions are represented by a single pipe (or vertical line) symbol. They describe a value that can be of more than one type. In this example we’re saying that the countryOfOrigin can be a string type or undefined type.

interface DogBreed { averageHeight: number coatType: string countryOfOrigin: string | undefined // <- Use the union here isGoodDoggo: boolean name: string }

Now that we changed the accepted types for countryOfOrigin, we can set that value to be undefined and TypeScript will be ok with that. What TypeScript is doing is called type checking and is a large part of what makes TypeScript great.

const maltese: DogBreed = { averageHeight: 8, coatType: 'very long', countryOfOrigin: undefined, // <- Now this can be undefined isGoodDoggo: true, name: 'Maltese', }

If we tried to assign anything other than a string or undefined to countryOfOrigin TypeScript will fail to compile.

countryOfOrigin: 'Americas' // ✅ TypeScript accepts a string or undefined and this is a string countryOfOrigin: undefined // ✅ TypeScript accepts a string or undefined and this is undefined countryOfOrigin: 10 // ❌ TypeScript accepts a string or undefined and this is a number

Example #3: Enums

Enums are a set of constants that are used to tell other developers exactly what a value should be. They are defined outside the component using the keyword enum. Conventionally they are Pascal cased and the properties are written in caps.

enum CoatType { CURLY = 'curly', NO_COAT = 'hairless', SILKY = 'silky', SMOOTH = 'smooth', WIRY = 'wiry', } interface DogBreed { averageHeight: number coatType: CoatType // <- Now coatType can only be what we defined in CoatType countryOfOrigin: string | undefined isGoodDoggo: boolean name: string }

Now we can change our dog objects to match the new enum. This way we can clearly define what values are acceptable for this property. It’s also easy for another developer to see our intention.

const maltese: DogBreed = { averageHeight: 8, coatType: CoatType.SILKY, // <- Now TypeScript will give an error if we type in a wrong value countryOfOrigin: undefined, isGoodDoggo: true, name: 'Maltese', }

Want more enum examples?

Example #4: useState

We can check the values passed to the useState hook by using angled brackets. (This is also called Generics. Check out the links at the end of this blog for more information!)

const [dog, setDog] = useState<DogBreed | null>(null)

Because the default state is null, we have to add null to the angled brackets so that TypeScript knows to accept null. The other type we are accepting is the interface DogBreed. Any object that doesn't match the keys and properties of DogBreed will not be accepted.

Example #5: Functions

Function arguments and the function’s return type can be checked with TypeScript.

// An onomatopoeia is the formation of a word from a sound like "cuckoo" or "sizzle" const onPress = (selectedDog: DogBreed, onomatopoeia: string): void => { alert(`${selectedDog.name} says: ${onomatopoeia}!`) } onPress(maltese, 'bark bark') // ✅ Both arguments match what TypeScript accepts onPress(russellTerrier, 100) // ❌ The second argument is not a string onPress(tabbyCat, 'meow') // ❌ The first argument does not match DogBreed

onPress takes one argument called selectedDog which has to match all the keys and properties of DogBreed. The second argument must be a string. After all the function arguments are listed, we add the return type of the function. Because onPress does not return anything the return type is void.

Example #6: Arrays

Arrays can be declared in TypeScript using square or angle brackets. If the array accepts more than one type use the angled brackets version.

number[] // Accepts an array of numbers: [1, 2, 3] Array<number> // Accepts an array of numbers: [1, 2, 3] Array<number | string> // Accepts an array of numbers or strings: [1, 2, 3, 'hi', 'hello'] number | string[] // Accepts a single number or an array of strings: 1 OR ['hi', 'hello']

We can add this to the interface DogBreed so TypeScript will only accept numbers for averageHeight

interface DogBreed { averageHeight: number[] // <- Only an array of numbers will be accepted now coatType: CoatType countryOfOrigin: string | undefined isGoodDoggo: boolean name: string } const maltese: DogBreed = { averageHeight: [7, 8, 9], // <- This must match the accepted type of number[] now coatType: CoatType.SILKY, countryOfOrigin: undefined, isGoodDoggo: true, name: 'Maltese', }

Need to see how this works in action?

Example #7: Objects

Objects in TypeScript are written similarly to interfaces. If the object keys aren’t known, we can still define its type.

{ [key: string]: string | number // The object's key will be a string and its value will be a string or a number }

Example #8: Optionals

Optionals are written using a question mark. When values are optional, they receive the undefined type automatically. Optionals can be added to function arguments too. Optionals are useful when you want TypeScript to check a value only if the value is provided.

interface DogBreed { averageHeight: number[] coatType: CoatType countryOfOrigin?: string // <- TypeScript will not complain if this property is not given a value isGoodDoggo: boolean name: string }

Example #9: Final Code

import React, { useState } from 'react' import { Text, TouchableOpacity, View } from 'react-native' import { calculateAverageFromArray } from './utils' // Helper function was imported for brevity enum CoatType { // Could even be moved to an external file CURLY = 'curly', NO_COAT = 'hairless', SILKY = 'silky', SMOOTH = 'smooth', WIRY = 'wiry', } interface DogBreed { // Could also be moved to an external file if the codebase gets bigger averageHeight: number[] // Accepts an array of numbers coatType: CoatType // Must match a property of enum CoatType countryOfOrigin?: string // Accepts a string or undefined isGoodDoggo: boolean // Accepts a boolean name: string // Accepts a string } const maltese: DogBreed = { // This object will match the interface DogBreed averageHeight: [7, 8, 9], coatType: CoatType.SILKY, // Uses enum CoatType // countryOfOrigin no longer needs to be declared as it is optional isGoodDoggo: true, name: 'Maltese', } const russellTerrier: DogBreed = { // This object will match the interface DogBreed averageHeight: [10, 11, 12], coatType: CoatType.WIRY, // Uses enum CoatType countryOfOrigin: 'England', isGoodDoggo: true, name: 'Russell Terrier', } /** * Displays information on certain dog breeds. */ export const DogInformation = () => { const [dog, setDog] = useState<DogBreed | null>(null) // setDog only accepts objects matching DogBreed or null // This function accepts 2 arguments: // The first argument must match the interface DogBreed // The second argument must be a string // Finally, this function does not return anything so its return type is void // An onomatopoeia is the formation of a word from a sound like "cuckoo" or "sizzle" const onPress = (selectedDog: DogBreed, onomatopoeia: string): void => { alert(`${selectedDog.name} says: ${onomatopoeia}!`) setDog(selectedDog) } return ( <View> {dog && ( {/* Once a DogBreed is set in state, its information will be displayed here */} <Text> {` You're viewing information on: ${dog.name} Country of Origin: ${dog.countryOfOrigin} Average Height: ${calculateAverageFromArray(dog.averageHeight)} Deserves Treats? ${dog.isGoodDoggo} `} </Text> )} {/* These buttons invoke our onPress function and pass it a DogBreed object */} <TouchableOpacity onPress={() => onPress(maltese, 'BARK')}> <Text> Click here to see information on Maltese </Text> </TouchableOpacity> <TouchableOpacity onPress={() => onPress(russellTerrier, 'WOOF')}> <Text> Click here to see information on Russell Terriers </Text> </TouchableOpacity> </View> ) }

Conclusion

Now you’ve learned some common TypeScript and have a good place to start!

Making TypeScript happy is a full-time job.

— David K. 🎹 (@DavidKPiano) August 9, 2021

For more beginning TypeScript resources, check out the following:

For more intermediate or advanced TypeScript resources, check out the following:

Share this post

Interested in working with us?

Give us some details about your project, and our team will be in touch within a day or two.