打字稿:防止分配具有比目标接口中指定的更多属性的对象

Mag*_*nus 14 typescript

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

interface IUserSansPassword {
    username: string;
    email: string;
}

class UserSansPassword implements IUserSansPassword { ... }

interface IUser extends IUserSansPassword {
    password: string;
}

class User implements IUser { ... }
Run Code Online (Sandbox Code Playgroud)

在尝试创建 type 的对象时IUserSansPassword,我预计会出现以下错误:

const userSansPassword: UserSansPassword = new User(); // No TS Error †††
Run Code Online (Sandbox Code Playgroud)

但是,我没有收到 TS 错误,因为令我惊讶的是,TS 不禁止分配具有已建立的“额外”属性的对象。这是令人惊讶的,因为如果我尝试直接使用额外的属性进行定义,我会得到一个错误,如下所示:

const userSansPassword: IUserSansPassword = {
    username: 'jake',
    email: 'jake@snake.com',
    password: '' // TS Error ***
}
Run Code Online (Sandbox Code Playgroud)

我的问题总结:

  1. 为什么 TS 会这样?允许分配给具有过多属性的类型是不是很糟糕(因此为什么在上面的 *** 中出现错误)?

  2. 是否有我可以采用的 TS 设置或技术使 TS 在上面的 ††† 中出错?

jca*_*alz 15

这里的其他答案基本上是正确的:TypeScript 中的类型通常是开放/可扩展的,并且总是可以添加属性;也就是说,它们不是只允许存在已知属性的精确类型。一般而言,TypeScript 并不真正支持精确类型,尽管如您所见,它确实通过额外的属性检查将新创建的对象字面量的类型视为精确类型。

如果您真的想禁止 TypeScript 中的某个类型的特定属性键,您可以通过将属性设为可选并将其类型设为neveror 来实现undefined

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
}
Run Code Online (Sandbox Code Playgroud)

现在UserSansPassword已知没有定义的password属性。当然,现在下面是一个错误:

interface IUser extends IUserSansPassword { // error! 
// Types of property "password" are incompatible
  password: string;
}
Run Code Online (Sandbox Code Playgroud)

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

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

declare class User implements IUser {
  username: string;
  email: string;
  password: string;
}
Run Code Online (Sandbox Code Playgroud)

然后以下是您期望的错误:

const userSansPassword: UserSansPassword = new User();
// error, mismatch on "password" prop
Run Code Online (Sandbox Code Playgroud)

好的,希望有帮助;祝你好运!

代码链接

  • 很好的解释和建议的解决方案。谢谢! (2认同)