打字稿拒绝不符合接口的属性

时间:2020-03-09 18:49:39

标签: javascript typescript

我有一个API控制器,它需要像这样的主体参数

insertUser(@Body() user: IUser) {}

但是问题是我可以发布一个对象,该对象包含的属性比IUser定义的更多。

确保user严格遵守类型或克隆它仅保留接口IUser`中定义的内容的最简单的最简单方法是什么?

1 个答案:

答案 0 :(得分:1)

我的答案将与框架无关。您询问如何强制TS不允许传递具有附加字段的类型的值,这意味着我们要具有该类型的排他性行为,具有相同的属性或不允许这样做。

首先,这种行为在类型级别上是无益的,就像给定值包含我们需要具有的字段一样,其他字段对我们来说并不重要。另一件事是,即使我们可以执行类型的编译时间检查器,但是如果您的数据来自API,则您需要在运行时级别进行验证。

首先是类型级别的排他类型行为实现:

type IUser = {
  name: string;
  lastname: string;
}
// utility type to enforce exlusive type
type OnlyExistingFields<Champion, Pretendent> = {
  [K in keyof Pretendent]: K extends keyof Champion ? Pretendent[K] : never
}

// example type with more fields
type IMoreUser = IUser & {
  age: number;
}
// instance of correct type
const u1: IUser = {
  lastname: '',
  name: ''
}
// instance of type with more fields
const u2: IMoreUser = {
  lastname: '',
  name: '',
  age: 12
}
// below type declaration of exlusive function
declare function insertUser<U extends OnlyExistingFields<IUser, U>>(user: U): void

// usage:
insertUser(u1) // ? correct type is the same
insertUser(u2) // ?  error type has more fields

上面是完全类型级别检查,我们只能传递特定类型的参数,不允许使用该类型的任何超集。

正如我所说的,以上解决方案仅是类型级别,实际上,如果对象在运行时具有或不希望使用接口,则无法在类型级别强制实施,一切都只是假设。

也就是说,我们还需要运行时转换器才能删除所有不需要的字段。考虑这样的转换器:

// converts object with probably more fields to object with only wanted fields
function toIUser<T extends IUser>(a: T): IUser {
    return {
      name: a.name,
      lastname: a.lastname
    }
}
const u3 = toIUser(u2); // u3 is IUser and also in runtime has only IUser fields