Published on

TypeScript Exercise - Solving Exercise 4

Authors
  • avatar
    Name
    hwahyeon
    Twitter

Today, I solved Exercise 4.

interface User {
  type: 'user'
  name: string
  age: number
  occupation: string
}

interface Admin {
  type: 'admin'
  name: string
  age: number
  role: string
}

export type Person = User | Admin

export const persons: Person[] = [
  { type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
  { type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
  { type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
  { type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' },
]

export function isAdmin(person: Person): person is Admin {
  return person.type === 'admin'
}

export function isUser(person: Person): person is User {
  return person.type === 'user'
}

export function logPerson(person: Person) {
  let additionalInformation: string = ''
  if (isAdmin(person)) {
    additionalInformation = person.role
  }
  if (isUser(person)) {
    additionalInformation = person.occupation
  }
  console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`)
}

console.log('Admins:')
persons.filter(isAdmin).forEach(logPerson)

console.log()

console.log('Users:')
persons.filter(isUser).forEach(logPerson)
export function isAdmin(person: Person) {
  return person.type === 'admin'
}

export function isUser(person: Person) {
  return person.type === 'user'
}

The functions in the code specify the parameter person as the union type Person, which includes both User and Admin. However, without an explicitly declared return type, TypeScript does not narrow the type after the condition, so person is still considered a Person type even after the condition is evaluated. As a result, while properties like person.name and person.age, which are common to both User and Admin, can be accessed, attempting to access person.role, which exists only on Admin, will result in a type error because TypeScript still considers person to be of type Person. Consequently, it cannot safely allow access to role.

To resolve this, you must explicitly declare the return type of the type guard function. By specifying the return type, TypeScript can narrow the type of person in the conditional block, allowing safe access to properties that belong only to the specific type.

export function isAdmin(person: Person): person is Admin {
  return person.type === 'admin'
}

export function isUser(person: Person): person is User {
  return person.type === 'user'
}

By declaring return types such as person is Admin or person is User, TypeScript can infer the type of person after the condition is evaluated, allowing safe access to the corresponding properties.

Without a return type declaration

export function isAdmin(person: Person) {
  return person.type === 'admin'
}

if (isAdmin(person)) {
  person.role // TypeScript cannot determine if person is Admin, so this results in an error.
}

With a return type declaration

export function isAdmin(person: Person): person is Admin {
  return person.type === 'admin'
}

if (isAdmin(person)) {
  person.role // TypeScript infers that person is Admin, so no error occurs.
}