What is Typescript?
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript…
You have no doubt heard of Typescript, or have seen it on a job description if you have spent any time programming in JavaScript. However, even experienced JavaScript developers have not looked into this programming language created by Microsoft in 2012.
Although Typescript is defined as a language on Wikipedia, it’s better defined as a superset on the site (see quote above). A superset is a larger set containing all the elements of the smaller set and probably a few more. That can be a difficult definition to grasp, so let me give an example.
// x is a superset of y let x = [4, 5, 6, 9, 2, 10] let y = [4, 5, 6, 9]
x
contains all the elements of y
plus a few more. If we translate this example to Typescript’s relation with JavaScript we can say that Typescript contains all the features of JavaScript plus a few more.
Furthermore, it’s typed. This means that functions, parameters, and props have corresponding data types that can be declared by a developer, like React.FC
(type for React functional components) or a built-in type like string
.
Why Use Typescript?
Some programming languages have type declaration built into the language. I don’t develop with Java or C++ but I think that statement holds for both. If you are a programmer coming from a language that demands the structure of declarative types for arguments and parameters then you may think that JavaScript is sloppy (and you could make a very strong argument in favor of that position). Typing can help with code maintenance and reduce accidental bugs.
Despite the lack of typing in JavaScript, it’s still the language of the web. Therefore, Typescript was developed to do everything that JavaScript does but with types. I think it’s safe to say that once someone understands Typescript, it’s difficult for them to go back to their old ways because there is a clear benefit.
If you are programming and try to pass a string to a value that is declared as a boolean, the application will not compile.
Difference Between Typescript and PropTypes
PropTypes
PropTypes is a softer way to check property types in React that allows the application to compile and will only display warnings in the browser’s development console during development.
To use prop types, you need to install the library prop-types. An example of usage could be,
import React from 'react' import PropTypes from 'prop-types'; const FunctionalComponentName = (props) => { return ( <h1>Hello, {props.name}</h1> ) } FunctionalComponentName.propTypes = { name: PropTypes.string };
If this component received a boolean instead of a string (i.e <FunctionalComponentName name={true} />
) the browser would display a warning similar to the image below.
Despite the warning, the application still compiles. In Typescript, the application would fail to compile.
Typescript
To convert this into a typescript component you would need to;
- Set up a Typescript project (easier to use the create-react-app boilerplate)
- Change the file extension from
.jsx
to.tsx
- Install the library @types/react that has the type definitions for React
- Declare the property types for the component
The Typescript version could look like,
import React from 'react' interface Props { name: string; } const FunctionalComponentName: React.FC<Props> = (props) => { return ( <h1>Hello, {props.name}</h1> ) }
Notice that we are declaring the interface Props
that defines the type the component expects. Don’t worry if this is confusing at the moment because we are going to cover type declarations in the next section.
Furthermore, we are using the React.FC
type for the component (functional component) and passing it the Props
object in between the angle brackets. React.FC
is a type that we get from the @types/react library. The @types/react library contains declaration files for the many types that React uses.
Now, if we were to pass the component a boolean prop value, like <FunctionalComponentName name={true} />
, the application immediately tells us that something is wrong.
Declaring Types in Typescript
In the previous section, we declared a type using an interface, but types can be declared in different ways. Furthermore, it’s important to understand your options for types. Below is a list of the basic-types in Typescript:
- Boolean
- Number
- String
- Array
- Tuple
- Enum
- Any
- Void
- Null and Undefined
- Never
- Object
We don’t have enough time to cover the usage of them all but you can learn more about any of them on typescriptlang.org.
Type Annotations
Type annotations are a quick and simple way to make a declaration.
If you have a function, let’s say,
function addTwoNumbers = (firstNumber, secondNumber) => { return firstNumber + secondNumber }
You can declare the parameter types by placing a colon after the argument name followed by the type. The function now looks like,
function addTwoNumbers = (firstNumber: number, secondNumber: number) => { return firstNumber + secondNumber }
This annotation can be used in multiple ways. Another example is to declare the type for a variable.
let name: string = 'John'
Or, declare that the variable can have multiple types.
let name: string | undefined = ''
Interfaces
Declaring interfaces is a common technique when using Typescript for React apps. Interfaces are useful when the arguments that are being passed are an object. The props
parameter that is commonly passed to components is an object, therefore, it’s common to define props for a component using an interface.
interface Props { name: string; email?: string; subscribed: boolean; }
Notice that the email declaration has a question mark after email
. This is how you can declare an optional parameter.
Interfaces are how we defined the props in our component in the previous section and it’s what we will use in the example application later.
Declaration Files
Declaration files are more complex than the declarations above and are commonly installed separately from the library that utilizes them. For example, there is the React JavaScript library, then there is a @types/react declaration file library for React.
This library can be inspected once installed, and the picture below shows what parts of this library may look like.
In the above image, I am using Visual Studio Code, and I have navigated into the node_modules/@types/react
folder and opened index.js
. This is a declaration file for React types. As you can see, it can be a bit much when just starting to learn Typescript!
However, you can notice that the image shows the declaration for the React.FC
type that we used above with the interface. Thankfully, we are not writing our own React types and can leverage the ones already defined!
Further Learning
The above introduction to Typescript taught you enough to understand and follow along with the example application below. However, it is only a small introduction to Typescript and there is much more that can be learned!
For documentation, tutorials, and community visit the official Typescript website.
How To Create a React App Using Typescript
Prerequisites
- You’ll need to have Node >= 8.10 and npm >= 5.6 on your machine.
- Familiarity with React components, CSS, and HTML.
- Internet connection.
- A text editor (I am using Visual Studio Code).
- Understanding of how to open a terminal, or command-line on your machine
Text Editor
It’s hard to imagine using Typescript without a text editor that has autocomplete or tooltip capabilities. I recommend using Visual Studio Code because it can notify you in the editor if a type is wrong. Furthermore, you can hover over the underlined error and see the types that you need to correct the mistake.
Here’s an example catching a—possibly obvious—mistake in a Typescript project.
Also, it can notify you of an incorrect type, even if the type is in a different file.
On that note, I hope you are ready to get started!
1. Set Up Project with Create-React-App
Because TypeScript is a superset of JavaScript, it doesn’t have a default template – there would be too many. Instead, other projects have their own TypeScript bootstrap templates with their own context. These projects provide templates which include TypeScript support.
We are going to utilize create-react-app (CRA) to get our project set up quickly. If you are wondering what other React templates are available, visit the documentation page to explore other options.
Open up a new command prompt and execute the command below.
npx create-react-app rapidapi-react-typescript --template typescript
The above command creates a new React app using CRA. Furthermore, we specify that we want to use the typescript
template. This handles a lot of the type installations configuration, and file set-up for us! However, it takes a few minutes to install.
Next, change directories in rapidapi-react-typescript
and install the axios library to use for API calls later.
$ cd rapidapi-react-typescript/ $ npm install --save axios
Open up the project in your text editor.
Notice that the tsconfig.json
is filled with default values. In the future, you may want to customize the project and you can read more about that process by following this link.
Inside of App.tsx
, remove the JSX that is in between the <header>
tag and replace it with,
<h1>Email Validation</h1> <h2>Creating a React App with <span style={{ fontFamily:'cursive', color: "rgb(169, 254, 129)" }}>Typescript</span></h2>
Next, underneath the <header>
tag add the <main>
tag with a component named <EmailValidator />
. Import this component at the top of the page. The final code in App.tsx
is;
import React from 'react'; import './App.css'; import EmailValidator from './EmailValidator' function App() { return ( <div className="App"> <header className="App-header"> <h1>Email Validation</h1> <h2>Creating a React App with <span style={{ fontFamily:'cursive', color: "rgb(169, 254, 129)" }}>Typescript</span></h2> </header> <main> <EmailValidator /> </main> </div> ); } export default App;
Create Email Validator File
This application is going to validate an email address using an API on RapidAPI, but for now, we just need the component to not throw an error when we start the app.
Create the file EmailValidator.tsx
in the src
directory and add the below code to it.
import React from 'react' const EmailValidator: React.FC = () => { return ( <div> {/* Add form and display API response */} </div> ) } export default EmailValidator
Start the app by running npm start
in the project’s root directory. Finally, navigate to http://localhost:3000 to see the application.
2. Sign Up For a Free Account on RapidAPI
In the next step, we are going to set up an API call. The response from the API call determines what the props will be for a future component. To inspect the response and follow along with the API call, you will need an account on RapidAPI.
Visit RapidAPI to get signed up if you haven’t already!
3. Subscribe to the Email Verifier API
Search for Email Verifier API on RapidAPI or follow this link to the subscription page. Select the basic subscription.
Notice I have already subscribed to the Basic plan. Therefore, I have a link to Manage and View Usage that takes me to the developer dashboard.
You can track your API usage on the dashboard in case you have concerns about approaching your quota for any of the APIs that you subscribe to.
We have a rate limit of 20 requests per day, then $0.02 for every request after that.
4. Add API Call to Email Verifier API
Navigate to the endpoints page for the Email Verifier API.
There is only one endpoint for the API and it returns a small response object. We want two values from this JSON response object.
- status
- reason
Both of these values are strings. Furthermore, we can double-check that assumption by clicking on the Schema tab in the dashboard.
What we want the app to do is accept a user email as a string, then return whether the email is valid or invalid. Also, we can provide an option to see the "reason"
parameter for invalid emails. To accept a string value for the API we need a state variable to hold the string. Furthermore, we need string state variables to hold the "reason"
and "status"
parameters from the response object. The component’s state will be handled using React hooks.
After adding the form, state variables, and API call, EmailValidator.tsx
will look like,
import React from 'react' import axios from 'axios' const EmailValidator: React.FC = () => { let [email, setEmail] = React.useState<string>('') let [emailStatus, setEmailStatus] = React.useState<string>('') let [validationReason, setValidationReason] = React.useState<string>('') let [showReason, setShowReason] = React.useState<string>('Yes') const validateEmail = (e: React.FormEvent<HTMLFormElement>): void => { e.preventDefault(); axios({ "method": "GET", "url": "https://email-checker.p.rapidapi.com/verify/v1", "headers": { "content-type": "application/octet-stream", "x-rapidapi-host": "email-checker.p.rapidapi.com", "x-rapidapi-key": process.env.REACT_APP_RAPIDAPI_KEY }, "params": { "email": encodeURI(email) } }) .then(({data}) => { setEmailStatus(data.status) setValidationReason(data.reason) }) .catch((error) => { console.log(error) }) } return ( <div> <form onSubmit={validateEmail}> <legend></legend> <fieldset> <label>Email to Validate: </label> <input type='text' value={email} required onChange={(e) => setEmail(e.target.value)} /> <div> <p>Display the reason for validation classification?</p> <input type="radio" id="yes-show" name="reason" value='Yes' onChange={(e) => setShowReason(e.target.value)} /> <label htmlFor="yes-show">Yes</label><br /> <input type="radio" id="no-show" name="reason" value='No' onChange={(e) => setShowReason(e.target.value)} /> <label htmlFor="no-show">No</label><br /> </div> <button type='submit'>Validate</button> </fieldset> </form> </div> ) } export default EmailValidator
A few things to note about this code:
- types for the state variables are declared inline as strings
- the JavaScript click
event
listener is declared as aReact.FormEvent<HTMLFormElement>
type for thevalidateEmail
response function
Using an interface
You do not need to change the code to use an interface. However, if you wanted the response to be saved to the component in an object form you could use an interface. The component could look like this,
import React from 'react' import axios from 'axios' import DisplayValidationResponse from './DisplayValidationResponse' interface ResponseObj { // declare expected values status: string; reason: string; } const EmailValidator: React.FC = () => { let [email, setEmail] = React.useState<string>('') let [showReason, setShowReason] = React.useState<string>('Yes') let [response, setResponse] = React.useState<ResponseObj>({ // pass ResponseObj type into state function status: '', reason: '' }) const validateEmail = (e: React.FormEvent<HTMLFormElement>): void => { e.preventDefault(); axios({ "method": "GET", "url": "https://email-checker.p.rapidapi.com/verify/v1", "headers": { "content-type": "application/octet-stream", "x-rapidapi-host": "email-checker.p.rapidapi.com", "x-rapidapi-key": process.env.REACT_APP_RAPIDAPI_KEY }, "params": { "email": encodeURI(email) } }) .then(({data}) => { setResponse(data) // set data as object }) .catch((error) => { console.log(error) }) } return ( <div> <form onSubmit={validateEmail}> <legend></legend> <fieldset> <label>Email to Validate: </label> <input type='text' value={email} required onChange={(e) => setEmail(e.target.value)} /> <div> <p>Display the reason for validation classification?</p> <input type="radio" id="yes-show" name="reason" value='Yes' onChange={(e) => setShowReason(e.target.value)} /> <label htmlFor="yes-show">Yes</label><br /> <input type="radio" id="no-show" name="reason" value='No' onChange={(e) => setShowReason(e.target.value)} /> <label htmlFor="no-show">No</label><br /> </div> <button type='submit'>Validate</button> </fieldset> </form> </div> ) } export default EmailValidator
Check back with your application at http://localhost:3000 and notice that we have a form, but it won’t work because no API-key has been provided and there is no component to display the data!
Add API Key
WARNING: This method does not secure your API key in production. It is only used to simulate data fetching in an application and hide the API key for public repositories.
Create the file .env
in the root of the project.
In .gitignore
, add the line (anywhere in the file),
.env
and inside of .env
add,
REACT_APP_RAPIDAPI_KEY=yourapikey
You can find your API key on the Email Verifier dashboard. I pointed it out in one of the dashboard pictures above.
Restart the application to load in the API key.
5. Create Component to Display API Response Data
In theory, our API call works, but we do not have a component to display it!
Create the file DisplayValidation.tsx
file and add the below code.
import React from 'react' interface Props { status: string; reason: string; displayReason: boolean; } const DisplayValidationResponse: React.FC<Props> = (props) => { return ( <div> {props.status === 'valid' ? <p className="validation valid">Valid!</p> :<p className="validation invalid">Invalid</p>} {props.displayReason ? props.reason : null} </div> ) } export default DisplayValidationResponse
Here you can see that the component expects three properties with the types defined in the interface Props
. Consequently, when we add this component to EmailValidator.tsx
, it needs to receive properties that have these types.
Open up EmailValidator.tsx
and import DisplayComponents.tsx
at the top.
import DisplayValidationResponse from './DisplayValidation'
Then, add the component underneath the <form>
tag.
... {emailStatus && <DisplayValidationResponse status={emailStatus} reason={validationReason} displayReason={showReason === 'Yes' ? true : false} />} ...
Now, when we add an email and click the validate button we have a response!
Add CSS
The app is missing some style and the data is displayed too far down on the page. This is inconvenient for the user, so let’s add some CSS. Replace all the code in App.css
with the below CSS code.
body { background-color: rgb(169, 254, 129); color: white; } .App { background-color: black; text-align: center; margin: 10%; box-shadow: 0px 0px 22px rgb(82, 82, 82); border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px; -ms-border-radius: 10px; -o-border-radius: 10px; padding: 2%; } .validation { padding: .3rem 1rem; color: white; font-size: 2rem; width: 80%; margin: 1rem auto; } .invalid { background-color: red; } .valid { background-color: green; } form { padding: 1.3rem; margin: 1rem auto; background-color: white; color: black; border-radius: 5px; width: 80%; -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; } button { cursor: pointer; font-size: 1.3rem; padding: 0.5rem .9rem; margin: 20px; border: 1px solid black; color: white; background: black; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; } button:hover { color: black; background: rgb(169, 254, 129); } fieldset { border: none; } input { font-size: 1.1rem; padding: .5rem; border: 1px solid #CCC; box-shadow: 0px 0px 10px #EEE; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; } h1 { font-weight: 200; border-bottom: 4px solid rgb(169, 254, 129); } h2 { font-weight: 200; } .App-header { color: white; min-height: 20vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); }
A little bit of styling can go a long way!
You can now enter emails into the form to validate their authenticity! Don’t forget about your quota limits!
The practical use cases for an interactive email verifier are low, but the point is to see Typescript in action across React components. Great job!
Conclusion
The more applications that you work on, and the more complicated, the more the advantages of Typescript begin to show up. It can be difficult to remember all the props for different components if you have to orchestrate many components at once. Having something double check your arguments can reduce bugs and help with maintenance.
Furthermore, it can be useful to know the schema types for API response objects. We used the RapidAPI dashboard to double-check the schema in this small application, and being able to access the schema-type for APIs can help the planning and development process in Typescript.
Whether you plan to use CRA, Gatsby.js, or Next.js to build your Typescript app there are templates to help get you started that will save you time in development. Thanks for working through the example and if you have questions or comments please leave a comment below!
Leave a Reply