You’ve added a type field to distinguish Users from Admins and extracted type checking logic into separate functions. However, TypeScript doesn’t understand that these functions narrow the type.
export function isAdmin(person: Person) { return person.type === 'admin';}export function isUser(person: Person) { return person.type === 'user';}export function logPerson(person: Person) { let additionalInformation: string = ''; if (isAdmin(person)) { additionalInformation = person.role; // ❌ Error } if (isUser(person)) { additionalInformation = person.occupation; // ❌ Error } console.log(`${person.name}, ${person.age}, ${additionalInformation}`);}
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; // ✅ person is Admin } if (isUser(person)) { additionalInformation = person.occupation; // ✅ person is User } console.log(`${person.name}, ${person.age}, ${additionalInformation}`);}
function isAdmin(person: Person): person is Admin { return person.type === 'admin';}// Can be used anywhereif (isAdmin(person)) { ... }persons.filter(isAdmin)
Use type predicates when you need reusable type guards, especially with array methods like filter(), find(), or every().
TypeScript can verify you’ve handled all union cases:
function handlePerson(person: Person) { if (isAdmin(person)) { return `Admin: ${person.role}`; } else if (isUser(person)) { return `User: ${person.occupation}`; } // If you add a new Person type, TypeScript will error here const _exhaustive: never = person; return _exhaustive;}