Type guards are techniques that help TypeScript determine the specific type of a value at runtime. They are commonly used with union types to safely access properties and methods.
A type guard is a condition or function that helps TypeScript determine the type of a value.
function show(
value: string | number
) {
if (typeof value === "string") {
console.log(
value.toUpperCase()
);
}
}
The typeof check acts as a type guard.
Type guards are useful when a value can have multiple types.
function printValue(
value: string | number
) {
if (typeof value === "string") {
console.log(
value.length
);
} else {
console.log(
value.toFixed(2)
);
}
}
The guard allows each type to be handled safely.
The typeof operator is commonly used for
primitive types.
function showValue(
value: string | number
) {
if (typeof value === "string") {
console.log("String");
} else {
console.log("Number");
}
}
You can check whether a value is a string.
function show(
value: string | number
) {
if (typeof value === "string") {
console.log(
value.toUpperCase()
);
}
}
You can also check whether a value is a number.
function show(
value: string | number
) {
if (typeof value === "number") {
console.log(
value.toFixed(2)
);
}
}
function show(
value: string | boolean
) {
if (typeof value === "boolean") {
console.log(
value
);
}
}
The condition narrows value to boolean.
The instanceof operator can be used to check
whether an object is an instance of a class.
class Student {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Teacher {
subject: string;
constructor(subject: string) {
this.subject = subject;
}
}
function show(
person: Student | Teacher
) {
if (person instanceof Student) {
console.log(
person.name
);
}
}
function show(
person: Student | Teacher
) {
if (person instanceof Student) {
console.log(
"Student:",
person.name
);
} else if (
person instanceof Teacher
) {
console.log(
"Teacher:",
person.subject
);
}
}
The in operator checks whether a property
exists in an object.
interface Student {
name: string;
course: string;
}
interface Teacher {
name: string;
subject: string;
}
function show(
user: Student | Teacher
) {
if ("course" in user) {
console.log(
user.course
);
}
}
interface Admin {
name: string;
permissions: string[];
}
interface Student {
name: string;
course: string;
}
function showUser(
user: Admin | Student
) {
if ("permissions" in user) {
console.log(
user.permissions
);
} else {
console.log(
user.course
);
}
}
Equality checks can also narrow a value.
function show(
value: "admin" | "student"
) {
if (value === "admin") {
console.log(
"Admin User"
);
} else {
console.log(
"Student User"
);
}
}
A null check can act as a type guard.
function showName(
name: string | null
) {
if (name !== null) {
console.log(
name.toUpperCase()
);
} else {
console.log(
"No name"
);
}
}
You can check whether a value is undefined.
function showPhone(
phone: string | undefined
) {
if (phone !== undefined) {
console.log(
phone
);
} else {
console.log(
"Phone unavailable"
);
}
}
Array.isArray() can identify an array value.
function showData(
value: string | number[]
) {
if (Array.isArray(value)) {
console.log(
value.length
);
} else {
console.log(
value.toUpperCase()
);
}
}
Truthiness checks can narrow values that may be null or undefined.
function showName(
name: string | null
) {
if (name) {
console.log(
name.toUpperCase()
);
}
}
A common literal property can be used to distinguish between object types.
interface Admin {
type: "admin";
name: string;
}
interface Student {
type: "student";
name: string;
}
type User =
Admin | Student;
function showUser(
user: User
) {
if (user.type === "admin") {
console.log(
"Admin:",
user.name
);
} else {
console.log(
"Student:",
user.name
);
}
}
A custom type guard is a function whose return type uses
the is keyword.
function isString(
value: unknown
): value is string {
return typeof value === "string";
}
The expression value is string tells TypeScript
that the function can be used to narrow the value to string.
function isString(
value: unknown
): value is string {
return typeof value === "string";
}
const value: unknown =
"Hello";
if (isString(value)) {
console.log(
value.toUpperCase()
);
}
After the condition succeeds, TypeScript treats
value as a string.
interface Student {
name: string;
course: string;
}
interface Teacher {
name: string;
subject: string;
}
function isStudent(
user: Student | Teacher
): user is Student {
return "course" in user;
}
function showUser(
user: Student | Teacher
) {
if (isStudent(user)) {
console.log(
"Course:",
user.course
);
} else {
console.log(
"Subject:",
user.subject
);
}
}
The custom function makes the narrowing logic reusable.
Type guards are particularly useful with
unknown.
function processValue(
value: unknown
) {
if (typeof value === "string") {
console.log(
value.toUpperCase()
);
} else if (
typeof value === "number"
) {
console.log(
value.toFixed(2)
);
} else {
console.log(
"Unknown value"
);
}
}
Type guards can be used to identify different API response structures.
interface SuccessResponse {
status: "success";
data: string;
}
interface ErrorResponse {
status: "error";
message: string;
}
type ApiResponse =
SuccessResponse |
ErrorResponse;
function isSuccess(
response: ApiResponse
): response is SuccessResponse {
return response.status === "success";
}
function handleResponse(
response: ApiResponse
) {
if (isSuccess(response)) {
console.log(
response.data
);
} else {
console.log(
response.message
);
}
}
The type guard tells TypeScript which response structure is available in each branch.
Type guards can help when a React component receives different types of props.
type ValueProps = {
value: string | number;
};
function ValueDisplay(
props: ValueProps
) {
if (
typeof props.value === "string"
) {
return (
<div>
{props.value.toUpperCase()}
</div>
);
}
return (
<div>
{props.value.toFixed(2)}
</div>
);
}
Type guards can also be used when React Native components work with union types.
type Message =
string | {
text: string;
};
function MessageView(
message: Message
) {
if (typeof message === "string") {
return (
<Text>
{message}
</Text>
);
}
return (
<Text>
{message.text}
</Text>
);
}
A common mistake is performing a type-specific operation before checking the type.
function show(
value: string | number
) {
// Incorrect:
// console.log(
// value.toUpperCase()
// );
if (typeof value === "string") {
console.log(
value.toUpperCase()
);
}
}
Always perform the appropriate type check before using type-specific methods.
Type guards are useful whenever your code works with multiple possible types.
Important type guard techniques include:
typeofinstanceofinArray.isArray()interface Student {
type: "student";
name: string;
course: string;
}
interface Teacher {
type: "teacher";
name: string;
subject: string;
}
type User =
Student | Teacher;
function isStudent(
user: User
): user is Student {
return user.type === "student";
}
function showUser(
user: User
) {
console.log(
"Name:",
user.name
);
if (isStudent(user)) {
console.log(
"Course:",
user.course
);
} else {
console.log(
"Subject:",
user.subject
);
}
}
showUser({
type: "student",
name: "Amit",
course: "TypeScript"
});
showUser({
type: "teacher",
name: "Rahul",
subject: "JavaScript"
});
Type guards help TypeScript determine the exact type of a value during program execution.
function isString(
value: unknown
): value is string {
return typeof value === "string";
}
Type guards make code safer by allowing you to perform operations only after TypeScript knows that the value has the required type.
typeof is useful for primitive types.instanceof is useful for class instances.in operator checks for object properties.Array.isArray() can identify arrays.value is Type syntax.unknown type.Question: Which keyword is used in a custom type guard return type?