Review: Total TypeScript Beginner’s TypeScript Tutorial

Cecelia Martinez - Jan 24 '23 - - Dev Community

I’ve used TypeScript in production and side projects with frameworks like React and Vue, but have never taking an actual course. Instead, I learned on-the-job using the codebase, my experience with types from C#, and documentation and online resources. When I saw that Matt Pocock was releasing a TypeScript course, I decided to check it out. I knew of Matt from his work with the Stately.ai team and open source projects.

I completed the free Beginner’s TypeScript Tutorial. There is also a free Zod Tutorial with 10 exercises I plan to do next. The website contains tips and articles about TypeScript, and Matt creates videos on YouTube and Twitter. There’s also a Discord community to interact with other’s taking the course. Matt even built a VS Code extension to help with TypeScript errors! There is a lot here to be excited about.

About the Tutorial

The Beginner’s TypeScript tutorial contains 18 lessons, all with problems and solutions presented in an IDE with accompanying video explanations. The goal is to make both the Vitest tests and the TypeScript checker happy with your own solutions. Topics covered include:

  • Type Annotations
  • Named Types & Interfaces
  • Function Parameter and Return Types
  • Union Types
  • Generics & Type Variables
  • Utility Types
  • Typing Async Requests, Dynamic Keys, Errors, and Callbacks

This provides a strong foundation for many situations you’ll encounter with TypeScript in a codebase. I was able to complete the course in an afternoon with my existing TypeScript experience. It may take true beginners longer to find solutions and spend time absorbing the material.

My Review

I missed the pre-sale window for the Pro Workshops, and after completing the Beginner’s TypeScript Tutorial I am kicking myself for that. I found the Beginner’s Tutorial a great way to add context to my existing TypeScript knowledge in a structured way.

I enjoyed the hands-on, testing-based format of the tutorial and appreciated that Matt encourages you to find the answers yourself before revealing the solution.

I liked that the examples used mirrored real-world code samples, and didn’t rely on random variables like a or foo. I also liked that he reviewed the multiple potential solutions, and provided guidance on why you’d choose one solution over another.

Sometimes it was difficult to know what to look for, and I ended up using Google vs the official TypeScript docs to get to a solution or find the correct terminology. For example, I knew you could use & to combine types, but didn’t know it was called an “Intersection Type” until I found a blog post that used the term. I did realize that the file names in the IDE have hints (for example, the file is 15-intersection.problem.ts even though the lesson name is “Combining Types to Create New Types”). So, looking at the file name may help if you get stuck. Ultimately, having to investigate on my own was a realistic learning experience that mirrored a real-world developer workflow.

Even though I have experience with TypeScript, I definitely learned a few new things and will be signing up for the advanced Pro Workshops once available.

Here are five new fun things I learned about TypeScript while working through the tutorial:

  1. If you set a parameter as optional using a union type such as string | undefined, you still have to pass the parameter as undefined to avoid an error.
  2. Using the as keyword is called "casting" or "type assertion", and is typically less type-safe than assigning a type.
  3. How to use the Index Signature syntax for typing dynamic keys in an object.
  4. You can use instanceof to narrow a type, such as for error handling.
  5. There are 20+ Utility Types for combining, restricting, or otherwise altering existing types.

Tutorial Process & Notes

I completed the course entirely in the site’s built-in IDE. I did pull down the GitHub codebase initially but found it was easier and more efficient to use the course UI. My process when completing the course was:

  • Review the problem
  • Try to solve based on my existing knowledge of TypeScript
  • Look up documentation or Google for resources as needed
  • Complete problem
  • Review solution
  • Write down the concept demonstrated by the solution, including links to any docs
  • Create a new example of the concept in my notes to confirm understanding

Here are the notes I created during the course:

Type Annotations

  • Syntax for assigning a type to a variable
const variable: type
Enter fullscreen mode Exit fullscreen mode

Object Types

  • Syntax for assigning types to the properties in an Object
const variable: {key: type, key: type}
Enter fullscreen mode Exit fullscreen mode

Named Type

  • Using the type keyword to define a type to reference elsewhere
  • Can also be exported
type TypeName = {

key: type,
key: type
}
Enter fullscreen mode Exit fullscreen mode

Interface

  • Alternative to type keyword, used to represent Objects
interface InterfaceName = {
key: type,
key: type
}
Enter fullscreen mode Exit fullscreen mode

Optional Properties and Parameters

  • use ? following key to designate a property as not required
const variable: {key: type, key?: type}

const myFunction = (param: type, param?: type)
/// required parameters must go first
Enter fullscreen mode Exit fullscreen mode

Union Types

  • type indicating that a value can be one or more types
  • can be a primitive type, named type, or string literal

type Id = number | string
type EventCategory = 'conference' | 'meetup' | 'stream' | 'workshop'
Enter fullscreen mode Exit fullscreen mode

Arrays

  • use square brackets following type
  • can be primitive or named type
// primitve type array
const namesArray: string[] = ['john', 'amanda', 'jane']

// array of named Event type

interface Event {
id: string,
title: string,
description: string,
url: string,
date: string,
location: string,
}

const eventsArray: Event[] = [
{
id: 1,
title: 'my event',
...
Enter fullscreen mode Exit fullscreen mode

Generics

  • Generics docs
  • Provides variables to types
  • Uses <> syntax following the type
  • Alternative to using [] for arrays
const events: Array<Event>
const ids: Array<number>
Enter fullscreen mode Exit fullscreen mode

Function Return Type

  • Add type after () in function declaration
const returnString = (): string => {

return 'string'
}
Enter fullscreen mode Exit fullscreen mode

Promises

  • Use the Promise type generic and pass in the expected return type after the promise resolves
const asyncGetUser = async (): Promise<User> => {

const data: User = await fetch('api/getUser').then((res) => {
return res.json()
})

return data
Enter fullscreen mode Exit fullscreen mode

Casting Type Assertions

  • Type Assertions docs
  • Overrides or changes the type of a variable
  • less type safe than assigning
  • Uses the as keyword or <> syntax
// Assigning type will throw type error because missing required User properties
const myUser: User = {}

// Casting will not throw error, less type safe
const myUser = {} as User

// Can use casting to type return value of a data fetch

const asyncGetUser = async (): Promise<User> => {

const data = await fetch('api/getUser').then((res) => {
return res.json()
})

return data as User
}
Enter fullscreen mode Exit fullscreen mode

More Generic Types with Variables

  • Like with arrays, pass type to a type
// Sets
const stringSet = new Set<string>()
stringSet.add('hello')

// Maps
const stringNumberMap = new Map<string, number>()
stringNumberMap.set('id', 1)
Enter fullscreen mode Exit fullscreen mode

Record Type

  • Record Type docs
  • Used to designate key and value types of an object
  • Helpful when we have dynamic keys in an object
const contactEmails: Record<string, string> = {
'jane': 'jane@address.com',
'adam': 'adam@address.com'
}

// can add new key-value pair with dynamic key at runtime type safely
contactEmails['sue'] = 'sue@address.com'
Enter fullscreen mode Exit fullscreen mode

Index Signature

  • Index Signature docs
  • Used to designate the type of a key in an object, alternative to Record Type
  • Can also be used inside an Interface
const contactEmails: {[name: string]: string} = {
'jane': 'jane@address.com',
'adam': 'adam@address.com'
}

interface ContactEmails {
[name: string]: string
}

const myContacts: ContactEmails = {}
Enter fullscreen mode Exit fullscreen mode

Handling Error Types

  • TypeScript will only let you catch errors with type unknown or any
  • We can use any or coerce with e as Error but safest option is to check for Error type before handling
  • instanceof narrowing docs
} catch (e) {
  if (e instanceof Error) {
    return e.message;
  }
}
Enter fullscreen mode Exit fullscreen mode

Extending Interfaces

  • Use the extends keyword to inherit properties from a base interface
  • Can only be used with Interfaces, not Types
interface User {
id: string,
name: string,
isAdmin: boolean
}

/// also includes id, name, and isAdmin
interface Admin extends User {
adminLevel: 'admin' | 'super-admin',
adminKey: string
adminKeyLastUse: Date
}
Enter fullscreen mode Exit fullscreen mode

Intersection Types

  • Intersection Types docs
  • Use an & to create a new type with all the properties of two different Object types
  • Can use {} and [] as needed to add structure to the new type
interface User {
id: string,
name: string,
isAdmin: boolean
}

interface AdminDetails {
adminLevel: 'admin' | 'super-admin',
adminKey: string
adminKeyLastUse: Date
}

/// creates type with User properties and AdminDetails
/// object as a property

type UserAndAdminDetails = User & {adminDetails: AdminDetails}

Enter fullscreen mode Exit fullscreen mode

Pick Type

  • Pick Type docs
  • Constructs a new type with only the selected properties of an existing interface
  • Properties are required, other utility types can make properties required or optional as needed
interface User {
id: string,
name: string,
isAdmin: boolean
}

type UserDetails = Pick<User, "id" | "name">
Enter fullscreen mode Exit fullscreen mode

Omit Type

  • Used to create a new type without the selected properties of an existing interface
  • Does not provide autocompletion
interface User {
id: string,
name: string,
isAdmin: boolean
}

type UserDetails = Omit<User, "isAdmin">
Enter fullscreen mode Exit fullscreen mode

Callback Function Types

  • Used to type a function that is passed as a parameter
  • Defines any argument types and the return type
  • Can be used inline or extracted as its own type
  • Callback Types docs
  • Can be used with Promise<> for async functions
  • Can be extracted into their own type
// inline callback function type
const changeAdminStatus = (adminId: string, newStatus: boolean, 
updateUser: (id: string, property: string, value: string | boolean) => void) {

updateUser(adminId, "isAdmin", newStatus)

}

// async callback function type inline and extracted example

type UpdateAdminStatus = (id: string, newStatus: boolean) => Promise<boolean>

const getThenToggleAdminStatus = async (adminId: string,
getAdminStatus: (id: string) => Promise<boolean>,
updateAdminStatus: UpdateAdminStatus
): Promise<boolean> => {
const currentAdminStatus: boolean = await getAdminStatus(adminId);

const updatedAdminStatus: boolean = await updateAdminStatus(currentAdminStatus);

return updatedAdminStatus
}
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .