问题:
当我忘记向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允许子类型移除其超类的属性,这使得对于某些子类型T of User,profile属性可能不包含任何属性成为可能。(如果是这样的话,我不知道TypeScript允许你这么做.)
TypeScript版本4.1.2
发布于 2021-03-09 01:26:58
这与与普通类型相比如何使用泛型类型(请参阅按下)解析传播有关。如果将结果对象写入变量,您将立即注意到不同之处:对于非泛型类型,合并类型推断为:
{
profile: {};
userId: number;
}这导致此类型不可分配给带注释的返回类型User,该类型具有所需的username子属性。这正是编译器错误TS 2322所告诉您的:
属性'username‘在类型'{}’中缺失,但在类型'{ 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 //neverhttps://stackoverflow.com/questions/66538678
复制相似问题