typescript - 为什么类型保护类型的差价会导致类型检查被跳过?

标签 typescript typescript-generics

问题:
当我忘记将嵌套字段添加到类型 T 的对象时,为什么我没有收到编译时错误,当使用对象传播构造所述对象时?
示例:

interface User {
  userId: number;
  profile: {
    username: string
  }
}

function updateUsername(user: User): User {
  return {
    ...user,
    profile: {
      // Error (as expected)
    }
  }
}

function updateUsernameGeneric<T extends User>(user: T): T {
  return {
    ...user,
    profile: {
      // No error (unexpected... why?)
    }
  }
}
我自己对答案的猜测:
我所能想象的是,TypeScript 允许子类型删除其父类(super class)的属性,这使得对于某些子类型 T 成为可能。的 User , profile属性可能不包含任何属性。 (如果是这样,我不知道 TypeScript 允许你这样做......)

typescript 版本 4.1.2
Playground

最佳答案

与普通类型相比,这与泛型类型(参见 PR )如何解决传播有关。如果将结果对象写入变量,您会立即注意到不同之处:对于非泛型类型,合并类型推断为:

{
    profile: {};
    userId: number;
}
这导致此类型无法分配给带注释的返回类型 User有一个必需的 username子属性。这正是编译器错误 TS 2322 告诉您的:

Property 'username' is missing in type '{}' but required in type '{ username: string; }'


现在,泛型的情况有点不同:类型实际上被推断为 User 的子类型的交集。和一个 { profile: {}; }类型:
T & {
    userId: string;
    profile: {};
}
编译器对此没问题,因为交集是带注释的返回类型的“扩展”,其中包含根据交集定义的所有属性。

这是否是一种良好的行为值得商榷,因为您可以执行以下操作,编译器也不会更明智:
function updateUsernameGeneric<T extends User>(user: T): T {
  const newUser = {
    ...user,
    userId: "234",
    profile: {
      // No error (unexpected... why?)
    }
  }

  return newUser;
}

updateUsernameGeneric({ profile: { username: "John" }, userId: 123 }).userId //a-ok, "number"

由于返回类型是泛型类型参数和合并属性的交集,您可以取消注释返回类型并让 TypeScript 推断它。不兼容的属性类型将被正确推断为 never :
function updateUsernameGenericFixed<T extends User>(user: T) {
  const newUser = {
    ...user,
    userId: "234",
    profile: {
      // No error (unexpected... why?)
    }
  }

  return newUser;
}

updateUsernameGenericFixed({ profile: { username: "John" }, userId: 123 }).userId //never
Playground

关于typescript - 为什么类型保护类型的差价会导致类型检查被跳过?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66538678/

相关文章:

reactjs - typescript + React : define a generic table component

typescript - 如何在 typescript 中使用 "fill in"泛型参数

angular - TS2345 : Argument of type 'string' is not assignable to parameter of type 'never'

typescript - 如何删除重复的类型并避免以 never 结尾?

javascript - 转换 React 项目时出现 Typescript 错误

typescript - tsconfig.js 排除属性在使用导入模块时不起作用

javascript - 使用 ng-container 与模板的好处?

Typescript 映射泛型

javascript - Web Worker onMessage 在 IE11 中为 null

简单 Aurelia ASP.Net 5.0 RC1 设置中的 Javascript 错误