假设我有一个典型的“用户”对象,它有用户名、电子邮件、密码等属性。我想创建和管理一个对象,它是这样一个用户的真正“子集”,并保证不包括密码。下面是一个粗略的方法:
interface IUserSansPassword {
username: string;
email: string;
}
class UserSansPassword implements IUserSansPassword { ... }
interface IUser extends IUserSansPassword {
password: string;
}
class User implements IUser { ... }
在尝试创建IUserSansPassword
类型的对象时,我预期会出现以下错误:
const userSansPassword: UserSansPassword = new User(); // No TS Error †††
然而,我没有得到一个TS错误,因为令我惊讶的是,TS并不禁止用已经建立的'extra'属性赋值对象。这是令人惊讶的,因为如果我试图直接用extra属性定义,我会得到一个错误,如下所示:
const userSansPassword: IUserSansPassword = {
username: 'jake',
email: 'jake@snake.com',
password: '' // TS Error ***
}
我的问题总结如下:
1.为什么TS会有这样的行为?允许赋值给一个有多余属性的类型不是很糟糕吗(因此你在上面的***中得到了一个错误)?
1.是否有TS设置或技术可用于使TS出现上述†††中的错误?
2条答案
按热度按时间insrf1ej1#
这里的其他答案基本上是正确的:TypeScript中的类型通常是开放的/可扩展的,并且总是可以添加属性;也就是说,它们不是 * exact type *(如microsoft/TypeScript#12936中所要求的),其中只允许已知属性存在。TypeScript通常并不真正支持exact type,尽管它确实通过额外的属性检查将新创建的对象常量的类型视为exact类型,正如您所注意到的。
如果确实要禁止TypeScript中某个类型的特定属性键,可以通过将该属性设置为可选并将其类型设置为
never
或undefined
来实现:现在已知
UserSansPassword
* 没有 * 具有定义的password
属性。当然,现在以下是错误:你不能通过添加密码来扩展
IUserSansPassword
...如果是A extends B
,那么你总是可以在需要B
示例的地方使用A
示例。你可以做的是扩展一个相关的类型,你原来的IUserSansPassword
,它可以使用Omit
帮助器类型来计算:然后下面是一个你所期望的错误:
链接到代码
yhxst69z2#
如果
UserWithPassword
是UserWithoutPasword
的一个子类型,任何OOP语言都允许赋值。唯一的TypeScript区别--你不应该显式地实现接口。TS只检查对象的形状是否符合接口类型。另一种情况-对象字面初始化,多余的属性在这里是不允许的。编译器期望创建的对象形状应该完全匹配变量的类型,对象将被赋值。创建一个对象并在赋值后立即丢失其类型的一部分是相当错误的。