为什么在传递T时Pick <T,K>不会引发错误?

时间:2020-02-09 07:35:33

标签: typescript

我想从客户端创建一个具有名称作为参数的用户,并在服务器上分配created。因此,我使用Pick<User, "name">。但是当我向该函数传递完整的User对象时,Typescript不会抱怨:

interface User {
  name: string;
  created: Date;
}

const user: User = {
  name: "Test User",
  created: new Date()
};

const createUser = (user: Pick<User, "name">): User => ({
  name: user.name,
  created: new Date()
});

const newUser1 = createUser(user); // No error
const newUser2 = createUser({
  name: "Another Test User",
  created: new Date() // Error
});

沙盒:https://codesandbox.io/s/unruffled-wave-tewh8

我希望这两种情况都将引发错误,但是第一种情况不会引发错误。为什么?

1 个答案:

答案 0 :(得分:1)

类型UserPick<User, "name">的结构子类型。类型为Pick<User, "name">的对象是具有类型name的{​​{1}}属性的任何对象,并且string对象符合该描述,因此传递User,其中应该是User

后一种情况是错误的,因为存在object literals cannot have excess properties的特殊规则。尽管您的第二个示例同样具有类型安全性,但与第一个示例相比,它更可能是一个错误,因此即使没有将其作为对象文字传递时,Typescript也会发出警告,尽管它会默默地接受相同的值。

如果您特别希望此函数不能用类型Pick<User, "name">的参数来调用,则可以使它的参数类型不是User的子类型:

User

但是,请注意,这并不能绝对阻止您使用interface UninitialisedUser { name: string; created?: undefined; } const createUser = (user: UninitialisedUser): User => ({ name: user.name, created: new Date() }); const newUser = createUser(user); // error 对象调用该函数。您仍然可以编写User,然后let temp: { name: string } = user;不会是类型错误。真的没有办法解决,但至少很难无意间做到这一点。

Playground Link