express
, the value of
req.params
is an object, which can only have string properties.By default,
req.params
is typed to be
Record<string, string>
, which isn't really useful.我想向编译器提供键入信息,以便它知道预期的属性,而不是预期的。我可以在许多方面做到这一点。我最终做的是创建一个抽象,使我可以使用类定义来定义
req.params
的形状。这很有用,因为我可以免费提供此类班,并且可以免费进行运行时验证。
此抽象接受课程并返回打字请求处理程序。沿着:
req.params
class-validator
问题是,我可以将参数类定义为具有任何属性,不仅是declare function createRequestHandler<Params extends object>(
paramsConstructor: new (...args: never) => Params,
handler: express.RequestHandler<Params>,
): express.RequestHandler<Params>
class GetUserByIdParams {
userId!: string
}
app.get('/users/:userId', createRequestHandler(GetUserByIdParams, (req) => {
req.params.userId; // ✅ well-typed
}))
或从string
衍生而来的,而且还具有复合对象 - 当然是,这是一个复合对象。 string
的运行时值不是什么。
number
是的,当我实际运行此代码时,我会从
boolean
中遇到验证错误,但是我也想在设计时有此约束。
我如何限制
req.params
仅具有字符串属性?
有两种方法:1)轻松而烦人,2)复杂而健壮。easyand Toughing:索引签名
限制类属性为
class InvalidParams {
userId!: number
}
app.get('/users/:userId', createRequestHandler(InvalidParams, (req) => {
req.params.userId.toFixed(2); // ❌ method 'toFixed' doesn't exist, but no errors here
}))
S的简便方法是添加索引签名:
class-validator
Params
string
class Params {
[key: string]: string
userId!: number // ✅ Error: Property 'userId' of type 'number' is not assignable to 'string' index type 'string'
}
:
class Params {
userId!: number // ❌ no errors
}
createRequestHandler
这使得不可避免地会使索引特征。但是索引签名具有其他不幸的功能:它们允许访问未知的属性:
extends object
与默认值相比,这种方法几乎没有更有用。 complex andRobust
一种更为复杂但更强大的方法是收集模型的属性,并评估它们都可以分配给extends Record<string, string>
(含义,属性值是a的,或者是从declare function createRequestHandler<Params extends Record<string, string>>(…): …
,例如字符串字面类型,或解决字符串值的枚举)。如果其属性中的任何一个比class Params {
userId!: number
}
createRequestHandler(Params, () => …)
// ^^^^^^
// ✅ Error: Index signature for type 'string' is missing in type 'Params'
(例如
enum UserRole { Admin = 'Admin', User = 'User' }
class Params {
[key: string]: string
userId!: string
userRole!: UserRole
}
new Params().userId // ✅ string
new Params().userRole // ✅ UserRole
new Params().fooBar // ❌ string, no error
new Params().literallyDoesNotExist // ❌ string, no error
或
Record<string, string>
)更通用的情况,这将使输入类无效。
操作员可以帮助使对象的所有属性的所有值结合:string
string
string
如果对象的至少一个属性比string
::
string | number
要将其应用于unknown
,请指示给定类构造一个仅具有
keyof
或type ValuesOf<Obj extends object> = Obj[keyof Obj]
衍生属性的实例:
type SubType<Type, SuperType> = [Type] extends [SuperType] ? Type : never
never