考虑User
类:
class User {
isAuthenticated: boolean
friends: User[] | undefined,
}
用户只有通过身份验证才能拥有朋友。我要这样做:
declare const user: User
if (user.isAuthenticated) {
// user.friends is an array of Users
} else {
// user.friends is undefined
}
将类分为User
和AnonymousUser
两个类不是解决方案。
更新:也许我的问题还不够清楚。我保证如果用户isAuthenticated
,他的friends
字段将是一个数组,否则为undefined
。我想告诉打字稿。像这样:
class User {
isAuthenticated: boolean
// Here I don't know what to do.
friends: this.isAuthenticated extends true ? User[] : undefined
}
答案 0 :(得分:2)
请记住TypeScript的类型检查是静态。它发生在编译时。因此,您不能让TypeScript进行取决于运行时条件的类型检查。
您当然可以按照自己的方式声明该类,但是对于一个实例,将isAuthenticated
设为true
的同时将friends
设为undefined
(或让friends
为数组,而isAuthenticated
为false
)。
对于此类型的静态类型检查,您需要使用在以下问题中排除的解决方案:匿名和已验证用户的单独类型。验证AnonymousUser
的操作将返回等效的(但已验证)User
对象。 (撤销身份验证的行为将类似地返回一个等效的[但未经身份验证的] AnonymousUser
对象。)
鉴于您不想这样做,我想说这些应该是访问器属性,以便类中的逻辑可以确保它们是一致的。例如:
class User {
private _friends: User[] | undefined = undefined;
get isAuthenticated(): boolean {
return this.friends !== undefined;
}
get friends(): User[] | undefined {
return this._friends;
}
}
...然后类中的逻辑通过设置this._friends to [];
来使用户认证(或通过设置this._friends = undefined;
来撤消认证)。这样,实例就不会不一致,因为这两个属性都依赖于相同的基础状态。
(该示例使用TypeScript的private版本,但如果使用的话,当然可以使用JavaScript's private fields。)
如果可以保证实例符合类型,则可以为此使用联合类型。假设--strictNullChecks
:
declare type UnauthenticatedUser = {
isAuthenticated: false;
friends: undefined;
};
declare type AuthenticatedUser = {
isAuthenticated: true;
friends: User[];
}
declare type User = UnauthenticatedUser | AuthenticatedUser;
// Works
const u1: User = {
isAuthenticated: true,
friends: []
};
// Works
const u2: User = {
isAuthenticated: false,
friends: undefined
};
// Errors:
// > Type '{ isAuthenticated: true; friends: undefined; }' is not assignable to type 'User'.
// > Type '{ isAuthenticated: true; friends: undefined; }' is not assignable to type 'AuthenticatedUser'.
// > Types of property 'friends' are incompatible.
// > Type 'undefined' is not assignable to type 'User[]'."
const u3: User = {
isAuthenticated: true,
friends: undefined
};
然后将正确的类型应用于任何给定的用户。
答案 1 :(得分:2)
您可以使用名为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[];
}
与您想要的不同之处在于,您不能使原始的属性 User.isAuthenticated
本身担当类型防护的双重职责。 Getters 也不是类型保护,因此上述解决方案涉及 function 。
现在您可以执行以下操作:
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).
}