Consider User
class:
class User {
isAuthenticated: boolean
friends: User[] | undefined,
}
User has friends only if he is authenticated. I want to do this:
declare const user: User
if (user.isAuthenticated) {
// user.friends is an array of Users
} else {
// user.friends is undefined
}
Splitting the class into two classes User
and AnonymousUser
is not a solution.
UPDATE: Maybe my question wasn't clear enough. I guarantee that if user isAuthenticated
, his friends
field will be an array, otherwise undefined
. I want to tell typescript about that. Something like this:
class User {
isAuthenticated: boolean
// Here I don't know what to do.
friends: this.isAuthenticated extends true ? User[] : undefined
}
You can almost do this using a feature called user-defined type guards:
class User {
private isAuthenticated_: boolean;
public isAuthenticated(): this is User & HasFriends {
return this.isAuthenticated_;
// At the call sites, you will need to "guard" some code with a condition
// involving this function. `this` will get a static type "upgrade" in the
// `true` branch only.
}
}
interface HasFriends {
friends: User[];
}
The difference to what you want is that you cannot make the original property User.isAuthenticated
itself serve double duty as a type guard. Getters cannot be type guards either, thus the above solution involving a function.
Now you can do this:
if (someUser.isAuthenticated()) {
// Here, TypeScript will see `someUser` as being typed `User & HasFriends`
// and allow you to access its `friends` property (regardless of whether
// it is actually defined on the object or not).
celebrateWith(someUser.friends);
}
else {
// Here, `someUser`'s static type remains unchanged, so friends isn't
// visible to TypeScript (again, regardless of actual existence at runtime).
}