Imagine we have the following function:
type User = {
id: string;
permissions: string[];
};
async function getUserPermissions(userId: string) {
const user = await db.getUserById(userId);
if (!user) {
return { found: false };
}
return user.permissions;
}
The above function has the following implicit return type:
Promise<string[] | { found: boolean }>
The issue is that the two return types have no properties in common, so if you try to do the following:
const userPermissions = await getUserPermissions('123');
if (!userPermissions.found) { // compiler error here
// Handle user not found
} else if (userPermissions.includes('admin'))
// Allow them access
}
the TypeScript compiler will yell at you with:
Property 'found' does not exist on type 'string[] | { found: boolean; }'
The Fix
The fix will depend on your business logic. In the above scenario, does your app need to distinguish between a user not being found and the user not having the necessary permissions? If not, you should do the following:
async function getUserPermissions(userId: string) {
const user = await db.getUserById(userId);
if (!user) {
return [];
}
return user.permissions;
}
This way your function always returns string[]
. But if your app does need to distinguish between a user not being found and the user not having the necessary permissions, returning null
is probably best:
async function getUserPermissions(userId: string) {
const user = await db.getUserById(userId);
if (!user) {
return null;
}
return user.permissions;
}
The return type of Promise<string[] | null>
is much easier to work with:
const userPermissions = await getUserPermissions('123');
if (!userPermissions) {
// Handle user not found
} else if (userPermissions.includes('admin'))
// Allow them access
}
One Step Further
The easiest way to make sure you know exactly what type your function is returning is to explicitly add the return type:
async function getUserPermissions(userId: string): Promise<string[]> {
const user = await db.getUserById(userId);
if (!user) {
return [];
}
return user.permissions;
}
This is a good habit to get into because if you do end up returning something other than string[]
in the above function, the TypeScript compiler will yell at you.
If you're using eslint, I would recommend enabling the explicit-function-return-type rule, at least as a warning to start. This will ensure that your functions are returning exactly what you expect, with no surprises.