I'm trying to understand how to write a function that would refine my type and then return it.
It works for the passed object itself and within the function body but not outside. Furthermore what I find odd is that even if it works within the body of the function, it stops working on the return statement itself.
My expectation would be that TypeScript correctly returns a refined / narrowed down type, checked properties included not just the main object.
What am I doing wrong? Does TypeScript require more help, maybe some generic return type?
Example:
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
} | null;
function checkUserHasReferrer(user: User) {
if (!user) {
throw Error('No User');
}
if (!user.referrer) {
console.log(user.referrer) // referrer correctly narrowed to `referrer: null`
throw Error('No Referrer');
}
const referrer = user.referrer; // referrer correctly narrowed to `referrer: Referrer`
console.log(user.referrer). // same here, `referrer: Referrer`
return user; // <- Here however it's back to `referrer: Referrer | null`
}
let initialUser: User = null;
const user = checkUserHasReferrer(initialUser);
console.log(user.id); // <-- knows User is not `null`
console.log(user.referrer.id); // However it doesn't know referrer is not `null`
TypeScript playground
You need to use assertion functions
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
}
function notNull(user: unknown): asserts user {
if (!user) {
throw Error('No User');
}
}
declare const user: User | null
notNull(user)
notNull(user.referrer)
console.log(user.id);
console.log(user.referrer.id); // ok
Playground
When you need to throw an error in TypeScript, usually such function is a good candidate to be assertion function
Or, you can use custom typeguard functions
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
}
const isValidUser = (user: User | null): user is User & { referrer: Referrer } =>
Boolean(user && user.referrer)
declare const user: User
if (isValidUser(user)) {
console.log(user.id);
console.log(user.referrer.id); // ok
}
Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With