我有一个函数接受带有查询参数的对象。下面是查询参数的简化示例。我想确保
sortBy
中的任何值都存在于 fields
中。我可以在运行时检查这一点,但我想知道是否可以使用 TypeScript 来做到这一点。
const validQueryArgs = {
fields: ['a', 'b', 'c'] as const,
sortBy: ['a'] as const
};
const invalidQueryArgs = {
fields: ['a', 'b', 'c'] as const,
sortBy: ['d'] as const // ts error here would be great :)
};
我知道你可以按照这个思路做一些事情(Matt Pocock 有一个关于这个的 YouTube 短片):
function createQueryArgs<T>({sortBy, fields}: { sortBy: NoInfer<T>, fields: T[] }): {fields: T[], sortBy: T } {
return {
fields,
sortBy
}
}
const someArgs = createQueryArgs({
sortBy: "c", // error here :thumbs-up:
fields: ["a", "b"] as const
})
有什么方法可以在不使用传递函数的情况下做到这一点吗?
我尝试走条件类型的道路,但很快就陷入了死胡同。
type CheckQueryArgs<T> = T extends { fields: infer Fields, sortBy: infer SortBy } ?
SortBy extends Fields ?
true :
never :
never
type CheckInvalid = CheckQueryArgs<typeof invalidQueryArgs> // never :thumbs-up:
type CheckValid = CheckQueryArgs<typeof validQueryArgs> // never :thumbs-down:
谢谢!
A
type
无法描述关系中没有使用特定类型的关系。
这意味着虽然这有效:
type TestA<T extends string[]>{ items: T, item: T[number] }
您需要能够满足
T
才能使用该类型。所以你不能这样做:
const objA: TestA = { items: [1,2,3], item: 2 } // wont work
使用函数是提供推理点的一种方法,该推理点可以为
T
提供类型。
但是如果您想要一种类型来检查这些对象之一,您可以这样做。您的尝试:
type CheckQueryArgs<T> = T extends { fields: infer Fields, sortBy: infer SortBy } ?
SortBy extends Fields ?
true :
never :
never
失败,因为
Fields
被推断为 readonly ["a", "b", "c"]
并且 SortBy
被推断为 readonly ["c"]
。这两种类型都不会扩展另一种类型,因为它们是具有不同成员数量的元组,因此它们不能相互分配。
您需要通过
number
对这些类型进行索引,以获取该元组中可能的值作为联合。然后一个可以扩展另一个。
将所有这些放在一起,你可以做这样的事情:
type CheckQueryArgs<T extends QueryArgs> =
T['sortBy'][number] extends T['fields'][number]
? T
: never
type CheckInvalid = CheckQueryArgs<{ fields: ['a', 'b'], sortBy: ['c'] }>
// never
type CheckValid = CheckQueryArgs<{ fields: ['a', 'b'], sortBy: ['a'] }>
// { fields: ['a', 'b'], sortBy: ['a'] }