- Lab
- Core Tech

Guided: Using TypeScript with React
Many developers know React and TypeScript separately but struggle to use them together effectively. In this guided lab, you’ll learn how to type React components, manage state safely, handle events, and work with hooks and context—all with TypeScript. By the end, you’ll be able to write more maintainable, type-safe React applications confidently.

Path Info
Table of Contents
-
Challenge
Introduction
In this guided lab, you will migrate a JavaScript React app to TypeScript. Along the way, you'll learn how to type different aspects of a React app and see it in action.
What to Expect
You should have foundational experience with React and have built a full application before.
info> The React Foundations lab series will teach you the fundamentals and uses the same Helpdesk demo application.
You should also be familiar with TypeScript fundamentals such as adding type annotations, interfaces, type aliases, and unions.
info> Stuck on a task? Find solution files in the
solutions/
folder, organized by step. Solution files look like{step.task}-{file path}
, like3.2-components~TicketPager.tsx
which corresponds to Step 3, Task 2 in thesrc/components
folder andTicketPager.tsx
file. Copying and pasting the contents of that file to the equivalent file in the Filetree will make the check pass and you can examine it for details. Some tasks require you to update multiple files. The final outcome of the lab is in thefinal
folder you can download.Getting Started
To make sure the lab environment is set up and working, you'll start by running the Globomantics Helpdesk demo application. Once set up, you can visit {{localhost:5173}} to view the app in your browser, or in the Web Browser tab in the editor to the right.
info> Web browser not working? Sometimes while reading through the lab, the dev server will shut down and you'll have to reconnect to the Terminal. Just run
npm run dev
again to start the server back up. Other times, the web browser may not always reflect the latest code due to the auto-save feature of the lab. Just click the refresh button next to the address bar in the tab to force-refresh the page.How is the app hosted?
You may wonder what "VITE" means and how the app is being hosted on the above URL. This comes from the JavaScript development tool Vite.js.Vite is a frontend tool that bundles and builds JavaScript applications, including React. There are other options available depending to build and host React applications depending on what you need, such as Next.js.
Vite is versatile and supports many other frameworks and libraries, including plain HTML and CSS with very little configuration.
A Simple Helpdesk App
The application is a simple helpdesk where you can submit and view support tickets related to fictitious Globomantics products.
A quick tour around the codebase:
src/components
holds different components that are on the pagesrc/hooks
holds a custom hook to work with IndexedDB, the web-based storage engine and the idb packagesrc/contexts
hold a shared context to share state in the appApp.jsx
is the main component that renders the page- The
TicketForm
component encapsulates the form fields and handling form submission - The
TicketDisplay
component handles displaying ticket information to the user - The
TicketList
component handles displaying the list of tickets in the database - Additionally, the file tree has been organized into modules within the src folder so that it's easier to focus on the code you'll write throughout the lab.
Benefits of Using TypeScript with React
One of the major benefits TypeScript provides is adding more context to the code for readability. It helps you "explain" the system better.
You probably know that TypeScript can infer types based on how they are used. We humans do the exact same thing without thinking. One way to think about TypeScript is that it forces you to explicitly think about types instead of only relying on inference, which may be an incomplete picture of your system.
To emphasize this benefit, as you work through each example in the lab, it will not tell you what types to use. Instead, you will need to determine the correct typings based on how the code is used, which should be unfamiliar to you.
This should help you understand why TypeScript is useful -- it is not just about catching errors.
-
Challenge
Configure TypeScript
Currently the lab is set up with Vite and JavaScript for React development. On a new project, you can run the following command to initialize a template with TypeScript support:
npm create vite@latest my-react-app -- --template react-ts
This scaffolds a blank Vite project with TypeScript pre-configured with the latest React installed.
However, when migrating an existing app, you'll need to configure TypeScript manually.
Configuring TypeScript
TypeScript can be configured using a
tsconfig.json
file at the root of your project.A
tsconfig.json
follows a schema like this:{ "compilerOptions": { // config options here } }
Most compiler options will be set under the
compilerOptions
section.Here are the recommended options to set for TypeScript and React support when using Vite:
"target": "ESNext", "moduleResolution": "bundler", "moduleDetection": "force", "verbatimModuleSyntax": true, "useDefineForClassFields": true, "allowImportingTsExtensions": true, "jsx": "react-jsx", "lib": ["ESNext", "DOM"], "skipLibCheck": true, "noEmit": true
What do these options do?
Vite and several other build tools require TypeScript to be in "isolated modules" mode to enable proper JavaScript transpilation. This is configured with the
verbatimModuleSyntax
,useDefineForClassFields
,allowImportingTsExtensions
, andmoduleDetection
options.Since this is a React project, JSX needs to be configured and TypeScript supports the
react-jsx
option out-of-the-box.The
noEmit
flag ensures no actual TypeScript code is output when running the compiler, since the build tool will take care of bunding for production using themoduleResolution
mode ofbundler
.The
target
andlib
fields dictate what syntax is supported and which typings are included. Since this is a browser-based app,dom
needs to be included andesnext
is recommended for supporting the latest ECMAscript features.skipLibCheck
ignores type errors if they come from your npm packages, which limits errors to "just your code."info> If you are using a different bundler/build tool, you will want to reference their documentation on the compiler options to use. These are the options Vite will set for you when using the Vanilla React TS template. ## Adding React Typings
The official React npm packages do not come with TypeScript declarations. They are provided separately using
@types
packages and you will need to install them manually in your project.The lab has the packages pre-installed but in your own project you would install them using your package manager:
npm install @types/react @types/react-dom --save
The versions of the packages correspond to the major React versions (
17.x
,18.x
,19.x
, etc.) ## Language ServerFrontend build tools like Vite do not perform type checking on your code since it can be a slow process, and typically your IDE provides that capability through the TypeScript Language Server.
The TypeScript language server is provided through the official Microsoft TypeScript Compiler (
tsc
), which can compile your code and runs type checking.The lab has the compiler installed through the
typescript
npm package, but the file editor does not feature TypeScript language server integration.You will need a way run type checking to ensure everything works as expected at runtime.
To do that, you can run the native TypeScript compiler (
tsc
). By default, it will use the nearesttsconfig.json
for configuration which includes thenoEmit
option. When using a bundler like Vite, this prevents the compiler from emitting outputs and only runs the type check. It is common to run the type check in a CICD environment like GitHub Actions but you can also run it manually before committing code.You can run the type check anytime during the lab to see what errors you have left to fix using:
npm run check ``` You should see an error: > error TS18003: No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were `'["src"]'` and 'exclude' paths were `'["checks"]'`. This is expected since right now the project only contains JavaScript files and `tsc` expects to find TypeScript files. Time to start the migration process! info> From now on the lab will run a type check when verifying your tasks to make sure your code is valid TypeScript.
-
Challenge
Typing Components and Props
To begin the migration to TypeScript, you will first need to change file extensions. Currently the project uses the extensions
.js
and.jsx
so you'll need to rename them to.ts
and.tsx
respectively.info> TypeScript can support JavaScript extensions through the
allowJs
config flag but it is recommended to use language-specific file extensions to be clearer. However, you may want to use that option when migrating a big project to TypeScript to track your progress. info> After running the script, theApp.jsx
file tab may disappear. You can expand the filetree and open the new files as you work through the lab.Strict vs. Non-strict Mode
If you run the type check, there should not be any compiler errors.
By default, TypeScript will infer types as best it can when there are no type annotations. When it cannot infer a type, it falls back to the
any
type.This implicit typing may be desirable for a migration project but it will not catch many classes of errors. If you enable the
strict: true
config option, this will disable implicit type inference and catch more errors but the trade-off is that it requires you to be more explicit.info> To ease the lab migration process, strict mode is turned off but it is on by default when creating a new Vite React/TypeScript project.
Common Prop Types
In React, you can pass data to components through props, including primitive types like
string
,number
, andboolean
.It's also common to pass callback functions like
onSelected
. In TypeScript, you can type callbacks using the function notation:interface Props { name: string; isActive: boolean; onSelected: (index: number) => void; }
Functional components accept props as the first argument and can be typed inline or separated into an
interface
ortype
alias.Typing Inline Props
The simplest method of typing React component props is to inline the type annotation on the
props
object.For example, this component accepts two props:
function UserAvatar({ imageUrl, title }: { imageUrl: string; title: string; }) { return ( <img src={imageUrl} alt={title} /> ) }
Based on the how each prop is used, you can infer they are
string
types since bothimg.src
andimg.alt
are both typed asstring
.The type annotation is added inline after the destructuring expression.
This approach is good for simple components that don't have a lot of props. ## Typing Prop Interfaces
For components with more than a few props, it can be easier to read if you declare an
interface
or atype
alias.What's the difference between an interface and type alias?
Practically, there's not much of a difference. However, you will usually get better error messages when using interfaces. Interfaces are preferred for most cases but type aliases can be useful when doing "mapped types" which will be covered soon.
Extracting the inline props in the example above might look like this:
interface UserAvatarProps { imageUrl: string; title: string; } function UserAvatar({ imageUrl, title }: UserAvatarProps) { return ( <img src={imageUrl} alt={title} /> ) }
In many React projects, you can declare prop interfaces or types next to the component in the same module and they do not usually need to be exported (unless referenced directly). ## Typing Optional or Null Props
Sometimes props can be optional when using a component, and you can declare that with the
?
optional operator:interface Props { name?: string; }
In this case,
name
will be typed as a union ofstring | undefined
.Optional props are different than allowing a prop to be
null
. In that case, you can explicitly allownull
values in addition to the regular type:interface Props { name: string | null; }
name
must be passed when using the component, but its value can benull
(but neverundefined
). ## Importing React TypingsThe React typings packages have utility types that may be helpful for your project.
When importing types only, in TypeScript you can use the
type
import keyword, which is sometimes required by your build tooling:import type { ReactNode } from 'react'
This signals to your bundler/transpiler that its safe to strip those imports from the production code.
info> You may receive a compiler or build error if you forget to use the
type
keyword for type-only imports. Some tools also require you to use the full.ts
extension when importing from TypeScript ES modules.Global Typings
React typings are global, so an alternative is that you can also reference utility types using the
React
global, like this:interface Props { heading: React.ReactNode; }
Typing Children
In React, all components can accept a
children
prop that represents other React components or JSX elements underneath them:<Layout> <Header /> <Body /> <Footer /> </Layout>
React children can be multiple types: an array of elements, components, a single component, a string literal, and more.
React provides a type you can import named
ReactNode
which represents the valid children a component can accept.import type { ReactNode } from "react"; interface Props { name: string; children: ReactNode; } function MyComponent(props: Props) { /* code */ }
This is an explicit way to type
children
on your own props type.Optional Children Prop
React also provides a helper you can use, the
PropsWithChildren<T>
generic type. This will perform a type intersection and add thechildren
prop to your typing automatically.You can use it without passing a props type to it, or pass your own prop interface/type alias:
import type { PropsWithChildren } from "react"; interface Props { name: string; } function MyComponent(props: PropsWithChildren<Props>) { /* props will contain `name` and `children` */ }
However, the children prop will be optional. This can be desirable in most situations but you may want to use the explicit method when you require children to be passed.
-
Challenge
Typing Async Hooks, Data, and APIs
The app stores and fetches tickets asynchronously using IndexedDB, a native browser storage engine. The package it uses is idb which provides a nicer Promise-based wrapper around the IndexedDB web API.
Start With the Data
When migrating a JavaScript project to TypeScript, it is helpful to begin at the data or API layer and work your way down. This is because often data from APIs are passed down to components, and understanding the data layer gives you more insight into how the app works.
The code to interface with IndexedDB is contained in the
useTicketDb
custom hook, which provides a set of async APIs components can call to create, get, or list tickets.Creating Data Types
You will often need to create custom types that represent the business "domain objects" being sent to or received from an API. To understand what the types are, you will need to inspect how the code interacts with an API.
It's common to use TypeScript interfaces to model the domain typings. For example, if you wanted to store and manage users, you may have a domain model like this:
// api-types.ts export interface User { id?: number; username: string; email: string; hobbies: Hobby[]; } export interface Hobby { id?: string; name: string; }
You can put these types anywhere in your React app, often in a
types
folder or module and then export them for shared usage.info> In IndexedDB, IDs may be optional on the typings because they can be auto-generated during creation. You can denote this with the
?
optional typing operator. ## Typing Third-Party APIsUnlike React, idb provides TypeScript typings as part of the package so there's no additional
@types
package to install.The main interface representing a database is
IIDBPDatabase<T>
whereT
represents your database schema typing.Continuing with the example of managing users, a fully typed IndexedDB API might look like this:
import { openDB, type DBSchema, type IDBPDatabase } from "idb"; import type { User, Hobby } from './api-types'; interface AppDB extends DBSchema { users: { key: number; value: User } hobbies: { key: string; value: Hobby; } } let db: IDBPDatabase<AppDB>; async function openDatabase() { if (db) return db; db = await openDB<AppDB>("app-db", 1, { upgrade(db) { db.createObjectStore("users", { keyPath: 'id', autoIncrement: true, }); db.createObjectStore("hobbies", { keyPath: 'id' }); } ); return db; }
Here the example defines two IndexedDB object stores,
users
andhobbies
. Every IndexedDB collection has a key to retrieve objects, which can be a string or auto-incrementing number. The typing indicatesUser
objects are stored with a numbered ID, but hobbies are stored with string keys.Module-scope initialization
It is best to manage a single IndexedDB instance at once, sometimes called the singleton pattern. The approach varies depending on the runtime environment. Here,
db
can be initialized once in the module-scope so thatopenDB
is only called once per browser session.When loading the initial list of tickets, you will see a loading message. This is provided through React Suspense and the use API in React 19.
The
use
API is not a hook, but instead can use a resource value like aPromise
orContext
. When used withSuspense
andErrorBoundary
, React will show the Suspense fallback when a Promise is pending, or the error boundary fallback if it's rejected.Typing the use API
This means you can pass a Promise from a top-level component down to a child within a Suspense boundary and it can show loading messages.
You will need to type the Promise using the generic
Promise<T>
type whereT
represents what the Promise value is:import { use } from "react"; import type { User } from "./api-types"; function Users({ loadUsersPromise }: { loadUsersPromise: Promise<User[]> }) { const users = use(loadUsersPromise); /* render with users data */ }
Here
users
will be of typeUser[]
as that's what the Promise value is wrapping. You do not need to explicitly typeusers
sinceuse
will unwrap the Promise type for you and it will be inferred. -
Challenge
Typing Events and Callbacks
Synthetic Events
React events are "synthetic" meaning they wrap native browser events, so there are some special cases to handle when it comes to
onClick
,onChange
, and other event handlers withuseCallback
.If you use an inline callback, you do not need any typings:
function Component() { const [name, setName] = useState(''); return ( <input type="text" onChange={e => setName(e.target.value)} /> ) }
In this case, the
onChange
prop type will flow into the anonymous function so thate
is typed as the correct React event type for the handler.However, for performance reasons you may decide to use the
useCallback
hook to memoize handlers, in which case you'll need to type them properly to strongly type the event object.There two ways to type events: by just typing the event argument or by typing the entire handler.
React Event Typing Conventions
Event handler typings follow a convention where the event type has an associated handler typing:
ChangeEvent
andChangeEventHandler
foronChange
FocusEvent
andFocusEventHandler
foronBlur
MouseEvent
andMouseEventHandler
foronClick
Depending on the event prop, the typing may be different.
Typing Event Arguments
For forms, the
onChange
prop is a common handler you'll need to type.The
onChange
prop invokes a callback and passes an event argument of typeReact.ChangeEvent<TElement>
whereTElement
is the HTML element type (likeHTMLInputElement
) defined by the global DOM library typings (lib.dom
).You can import this type helper to explicitly type event arguments in callbacks:
import { useCallback } from "react"; import type { ChangeEvent } from "react"; function Component() { const [name, setName] = useState(''); const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => { setName(e.target.value); }), []); return ( <input type="text" onChange={handleChange} /> ) } ``` Usually typing just the event argument is enough to satisfy TypeScript's inference when passing callbacks. However, there are other times where it may be necessary to explicitly type the handler instead of relying on inference. ## Typing Event Handlers In that case, you can import the `Handler` type and use it to annotate the callback variable declaration: ```tsx import { useCallback } from "react"; import type { ChangeEvent } from "react"; function Component() { const [name, setName] = useState(''); const handleChange: ChangeEventHandler<HTMLInputElement> = useCallback(e => { setName(e.target.value); }), []); return ( <input type="text" onChange={handleChange} /> ) }
In this case, you do not need to explicitly type the
e
event argument as that is provided by the higher-order handler typing. -
Challenge
Typing State, Context, and Providers
Typing useState Hooks
The React
useState<S>
hook accepts an optional generic type parameter representing the state value type. By default, the hook will infer the type based on what you pass as initial state:import { useState } from "react"; const [selected, setSelected] = useState(false); // selected: boolean const [selected, setSelected] = useState([]); // selected: any[] const [selected, setSelected] = useState<number[]>([]); // selected: number[]
Here you are defining the same state variable (
selected
) but there could be multiple meanings depending on how its used. For array state, it is common to explicitly pass the type in the generic so the state variable is typed properly.Typing null or undefined
If state can be null, then you can pass
null
as union type:import { useState } from "react"; const [selected, setSelected] = useState<boolean | null>(false); // selected: boolean | null
If you do not pass an initial value or generic type, the state variable will be typed as
undefined
only, so you can use a type union if you want to allowundefined
or another type. ## Typing Contexts and ProvidersA context is created using the React
createContext
API and it allows passing a generic type representing the context value type. The context value type will flow down into theContext.Provider
and throughuse
:import { createContext, use, type ReactNode } from "react"; type AppTheme = 'light' | 'dark' | null; interface AppContextValue { theme: AppTheme; } const AppContext = createContext<AppContextValue>({ theme: 'light' }); export function AppContextProvider({ defaultTheme = 'light', children }: { defaultTheme?: AppTheme; children: ReactNode }) { return ( <AppContext.Provider value={{ theme: defaultTheme }}> {children} </AppContext.Provider> ) } export const useAppContext = () => use(AppContext);
The object passed to the Context provider
value
prop will be typed asAppContextValue
. ## Typing useMemoYou can pass it inline as shown above or declare it as an object within the component and pass it down. It is common to memoize the context value using the
useMemo
API to reduce re-renders.Similar to
useState
, theuseMemo
API will infer the type based on the return type of the callback you pass but you can also explicitly pass a type in its generic parameter:export function AppContextProvider({ defaultTheme, children }: { defaultTheme?: AppTheme; children: ReactNode }) { const [theme, setTheme] = useState(defaultTheme ?? 'light'); const value = useMemo<AppContextValue>(() => ({ theme, setTheme }), [theme]); return ( <AppContext.Provider value={value}> {children} </AppContext.Provider> ) }
Why is
setTheme
being passed to the app context value, and how is it typed?Typing State Actions
It's not that common to pass set-state actions as props but in this demo app they are used to keep things simple.
A set-state action is formally typed as
React.Dispatch<React.SetStateAction<S>>
, a nested generic type.The
S
generic type parameter is the sameS
asuseState<S>
and you can use this built-in React helper type to type set-state actions:import type { Dispatch, SetStateAction } from "react"; interface AppContextValue { theme: AppTheme; setTheme: Dispatch<SetStateAction<AppTheme>>; }
Simpler typings for state actions
If you do not need to support the overload of set-state that passes the previous state, you can use a simpler type annotation like a regular callback function annotation, `(value: S) => void`. This is allowed by TypeScript since it matches the Dispatch interface.Why is typing the
useMemo
hook withinuseContextValue
optional?TypeScript uses a "structural" typing system, sometimes called "duck" typing. In other words, if it looks like a duck and quacks like a duck, TypeScript believes it's a duck.
useMemo
callback returns an object that matches your context interface and this is passed as the return type of the function, so TypeScript considers the type "assignable" and does not throw a compiler error.info> Try adding a new property in the
useMemo
callback and run the task check above again. Observe whether it fails or passes. Then, try changing one of the existing properties to a different value. Does it behave the same? Can you explain why or why not?Implicit types allow for flexibility. If you prefer strictness over flexibility, you can be explicit, and either pass a type to the
useMemo<T>
generic parameter or explicitly annotate the return type of the function. -
Challenge
Typing Refs and the DOM
Typing DOM Props
All JSX elements support common HTML properties, like
className
,onClick
, etc.React provides several type helpers to represent these depending on whether you want to include a
ref
prop or not:React.ComponentPropsWithoutRef<T>
React.ComponentPropsWithRef<T>
The
T
generic type is the component type, which in the case of HTML elements would be the literal string tag name:interface AutocompleteProps { htmlProps?: React.ComponentPropsWithoutRef<"div">; } export function Autocomplete({ htmlProps }: AutocompleteProps) { return ( <div {...htmlProps} className={`autocomplete ${htmlProps?.className ?? ''}` > </div> ) }
In general, it's usually better to hide HTML from consuming components through a prop but occasionally it makes sense to allow passing in any HTML prop to the underlying JSX.
You just want to be careful not to allow consumers to override HTML props you set yourself. To do that, you can spread the HTML props and then override specific ones based on the component behavior. The example above ensures the
autocomplete
CSS class is always present, in addition to whatever the caller passed (if any). When a new ticket is created, the app displays a popover using the native browser Popover API.The Popover API is available only on native DOM elements. Since React is an abstraction over the native DOM, the only way to access the native Popover API is through a Ref and the
useRef
hook.DOM Typings
A ref holds a reference to a value. This can be any value but in the case of DOM refs, the reference is to a native DOM element. DOM element typings are available globally in all TypeScript apps that use the
lib.dom.d.ts
typings (the default for browser-based apps).You used these elements previously when typing event callbacks, for example:
HTMLDivElement
HTMLInputElement
HTMLSelectElement
Each HTML element has an associated typing, and
HTMLElement
andElement
are the base interfaces each one inherits. This is how the Popover API is made strongly-typed through TypeScript:HTMLElement.showPopover()
HTMLElement.hidePopover()
HTMLElement.togglePopover()
Typing the useRef Hook
The
useRef<T>
hook accepts a generic type parameter representing the reference type you are holding, in this case a DOM element reference:const inputRef = useRef<HTMLInputElement>(null); return ( <input ref={inputRef} {...props} /> )
By default, DOM refs are initialized to null because they are populated when the component is rendered. ## Typing CSS Styles
If you only want to expose the
style
prop to consumers, React provides a simpler utility type,React.CSSProperties
meant for that purpose:interface AutocompleteProps { containerStyle?: React.CSSProperties; } export function Autocomplete({ containerStyle }: AutocompleteProps) { return ( <div style={containerStyle} className="autocomplete" > </div> ) }
-
Challenge
Typing Forms and Actions
HTML forms in React 19 allow you to pass a "form action" callback in their
action
prop.const handleFormAction = (formData) => { const myData = { name: formData.get("name"), uploadedFile: formData.get("phone-number") }; }; return ( <form action={handleFormAction}> {/* form code */} </form> )
Form actions can be void functions or they can return a Promise:
const handleFormAction = async (formData) => { /* await some API call */ };
Additionally, form actions may be marked as server functions with the
"use server";
directive in supported frameworks like Next.js which willPOST
data back to the server and re-render the component. However, for this lab the code only runs on the client browser.Typing useActionState
React also provides a
useActionState
hook that can create a form action whose pending state can be tracked for async or server-based rendering.There is no special typing required as long as you type the form action callback itself (the types will be inferred properly by TypeScript):
function Component() { const handleFormAction = async (previousState: string, formData: FormData) => { const myData = { name: formData.get("name"), uploadedFile: formData.get("phone-number") }; const req = await doSomething(myData); return req.message; }; const [message, formAction, isPending] = useActionState(handleFormAction, null); return ( <form action={formAction}> {/* form code */} </form> ) }
Here
message
is a typestring
presumably returned from an APIdoSomething(...)
.info> Note that the previous state is always returned as the first argument to the action callback, which is different when not using
useActionState
.Typing Form Data
The
FormData
class has aget
method that retrieves the form values for the given field name, which could be null if the field doesn't exist, a single string or even aFile
object (such as from a file input). The typing ofFormData.get
is a union between these different types. TheFormData.getAll
method is the same, except it could be an array of those values.If you want to specify a certain type for a form field, you may need to perform a type cast to cast the form data value to your expected type, like this:
const myData = { name: formData.get("name") as string, hobbies: formData.getAll("hobby") as string[] };
Narrowing vs. casting
Instead of type casting, you can narrow the type through a type guard. In that case, you would need a conditional
typeof
check or a helper function that returns the correct type if you pass in the return value offormData.get/getAll
. However, this can be more verbose and it is easier to cast directly in most cases. -
Challenge
Recap and What's Next
Using TypeScript with React seems fairly straightforward from the outside but once you get into the details, that's when things start to require more thought. In a real-world project, the hardest part is understanding how data flows through the application tree from the API layer down into components.
What You Learned
Congratulations on successfully completing this lab. Here are your takeaways:
- How hard was it to type code you never saw before? Did you need to rely on hints and solutions/check files? Now imagine onboarding onto a 10k line project, or a 1MM line project. TypeScript helps make big systems more explainable.
- Start with the data when typing a project using TypeScript. That often leads to an understanding of the domain you're working with.
- Implicit type inference is more flexible than being explicit, but being explicit catches more potential errors before runtime.
- "Let the types flow" to lessen the need for explicit typings. When you add typings at a higher-level, they can flow down so you aren't required to type as much lower-level code.
Try the Lab Again on Hard Mode
If you run:
npm run check
There should be no compiler errors. That's because the compiler is not strict by default, allowing the
any
type which disables type checking.For production projects, it's recommended to enable strict mode. If you want to stretch your TypeScript muscles, you check if there any strict errors by running:
npm run check -- --strict
There should be around 12 errors to fix. Can you solve them all?
info> You can also set the
"strict": true
compiler option in thetsconfig.json
file.
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.