typescript 类型脚本:防止为对象分配的属性多于目标接口中指定的属性

cbwuti44  于 2023-02-13  发布在  TypeScript
关注(0)|答案(2)|浏览(237)

假设我有一个典型的“用户”对象,它有用户名、电子邮件、密码等属性。我想创建和管理一个对象,它是这样一个用户的真正“子集”,并保证不包括密码。下面是一个粗略的方法:

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出现上述†††中的错误?

insrf1ej

insrf1ej1#

这里的其他答案基本上是正确的:TypeScript中的类型通常是开放的/可扩展的,并且总是可以添加属性;也就是说,它们不是 * exact type *(如microsoft/TypeScript#12936中所要求的),其中只允许已知属性存在。TypeScript通常并不真正支持exact type,尽管它确实通过额外的属性检查将新创建的对象常量的类型视为exact类型,正如您所注意到的。
如果确实要禁止TypeScript中某个类型的特定属性键,可以通过将该属性设置为可选并将其类型设置为neverundefined来实现:

interface IUserSansPassword {
  username: string;
  email: string;
  password?: never; // cannot have a password
}

declare class UserSansPassword implements IUserSansPassword {
  username: string;
  email: string;
  password?: never; // need to declare this also
}

现在已知UserSansPassword * 没有 * 具有定义的password属性。当然,现在以下是错误:

interface IUser extends IUserSansPassword { // error! 
// Types of property "password" are incompatible
  password: string;
}

你不能通过添加密码来扩展IUserSansPassword ...如果是A extends B,那么你总是可以在需要B示例的地方使用A示例。你可以做的是扩展一个相关的类型,你原来的IUserSansPassword,它可以使用Omit帮助器类型来计算:

interface IUser extends Omit<IUserSansPassword, "password"> {
  password: string;
}

declare class User implements IUser {
  username: string;
  email: string;
  password: string;
}

然后下面是一个你所期望的错误:

const userSansPassword: UserSansPassword = new User();
// error, mismatch on "password" prop

链接到代码

yhxst69z

yhxst69z2#

如果UserWithPasswordUserWithoutPasword的一个子类型,任何OOP语言都允许赋值。唯一的TypeScript区别--你不应该显式地实现接口。TS只检查对象的形状是否符合接口类型。
另一种情况-对象字面初始化,多余的属性在这里是不允许的。编译器期望创建的对象形状应该完全匹配变量的类型,对象将被赋值。创建一个对象并在赋值后立即丢失其类型的一部分是相当错误的。

相关问题