打字稿:将元组映射到联合类型似乎在嵌套对象中不起作用
Typescript: Mapping Tuples to Union types doesn't seem work inside nested objects
我在 Typescript 中有一个 Vue 项目,我 运行 遇到了一个关于将元组对象映射到联合对象类型的问题。
关于一些上下文,我正在研究后端端点的预期响应类型。目前,此端点接收 2 个值:一个枚举和一个字符串。根据枚举,响应对象将发生变化。
这是当前的实现:
const validations = {
email: ['isValid', 'isAvaliable'],
password: ['isMinLengthValid', 'hasUppercase', 'hasLowercase', 'hasSpecialChars', 'hasNumbers'],
iban: ['isValid', 'isRegistered']
} as const
type ValidationsMap = {
[T in keyof typeof validations]: typeof validations[T][number]
}
function validate<T extends keyof ValidationsMap>(type: T, value: string): Record<ValidationsMap[T], boolean> {
// Do something
}
现在 Backend 端点将多接收一个参数,响应对象也将依赖于它。
这是我试过的方法,但它不起作用:
const validations = {
portal: {
email: ['isValid', 'isAvaliable'],
password: ['isMinLengthValid', 'hasUppercase', 'hasLowercase', 'hasSpecialChars', 'hasNumbers'],
},
payment: {
email: ['isValid'],
iban: ['isValid', 'isRegistered']
}
} as const
type ValidationsMap = {
[S in keyof typeof validations]: {
[T in keyof typeof validations[S]]: typeof validations[S][T][number] // Error: Type 'number' cannot be used to index type...
}
}
function validate<S extends keyof ValidationsMap, T extends keyof ValidationsMap[S]>(service: S, type: T, value: string): Record<ValidationsMap[S][T], boolean> {
// Do something
}
有谁知道为什么这不起作用?
我认为将元组映射到联合的深度可能存在限制,但事实并非如此。
这可以正常工作:
type PortalEmailValidation = typeof validations['portal']['email'][number]
这似乎是 TypeScript 中的一个错误,如 microsoft/TypeScript#27709. The type checker apparently doesn't properly track the constraints for deep index access types when the keys are generic 中所述。这个问题已经开放了很长时间,没有任何进展的迹象,所以现在我们所能做的就是解决它。
当编译器不接受 T[K]
形式的索引访问时,一种方法是使用 conditional type inference, like T extends Record<K, infer V> ? V : never
(using the Record<K, V>
utility type)。如果 K
是 T
的 (non-optional) 键,那么 T
将被视为某些 V
的 Record<K, V>
,我们推断.
所以我们可以写 typeof validations[S][T] extends Record<number, infer V> ? V : never
而不是 typeof validations[S][T][number]
。或者等价地:
type ValidationsMap = {
[S in keyof typeof validations]: {
[T in keyof typeof validations[S]]:
typeof validations[S][T] extends { [k: number]: infer V } ? V : never
}
}
另一种方法是显式添加回缺失的约束。如果你有一个 你 知道的类型 A
可以分配给另一个类型 B
但编译器不知道这一点,那么你可以替换 A
与 Extract<A, B>
(使用 the Extract<T, U>
utility type),编译器将接受它。它知道 Extract<A, B>
可以分配给 B
。假设您关于 A
可分配给 B
的说法是正确的,那么 Extract<A, B>
的计算结果将只是 A
.
所以如果ValidationsMap[S][T]
可以赋值给string
但是编译器看不到,我们可以写成Extract<ValidationsMap[S][T], string>
:
function validate<S extends keyof ValidationsMap, T extends keyof ValidationsMap[S]>(
service: S, type: T, value: string
): Record<Extract<ValidationsMap[S][T], string>, boolean> { // okay
return null!
}
我在 Typescript 中有一个 Vue 项目,我 运行 遇到了一个关于将元组对象映射到联合对象类型的问题。
关于一些上下文,我正在研究后端端点的预期响应类型。目前,此端点接收 2 个值:一个枚举和一个字符串。根据枚举,响应对象将发生变化。
这是当前的实现:
const validations = {
email: ['isValid', 'isAvaliable'],
password: ['isMinLengthValid', 'hasUppercase', 'hasLowercase', 'hasSpecialChars', 'hasNumbers'],
iban: ['isValid', 'isRegistered']
} as const
type ValidationsMap = {
[T in keyof typeof validations]: typeof validations[T][number]
}
function validate<T extends keyof ValidationsMap>(type: T, value: string): Record<ValidationsMap[T], boolean> {
// Do something
}
现在 Backend 端点将多接收一个参数,响应对象也将依赖于它。
这是我试过的方法,但它不起作用:
const validations = {
portal: {
email: ['isValid', 'isAvaliable'],
password: ['isMinLengthValid', 'hasUppercase', 'hasLowercase', 'hasSpecialChars', 'hasNumbers'],
},
payment: {
email: ['isValid'],
iban: ['isValid', 'isRegistered']
}
} as const
type ValidationsMap = {
[S in keyof typeof validations]: {
[T in keyof typeof validations[S]]: typeof validations[S][T][number] // Error: Type 'number' cannot be used to index type...
}
}
function validate<S extends keyof ValidationsMap, T extends keyof ValidationsMap[S]>(service: S, type: T, value: string): Record<ValidationsMap[S][T], boolean> {
// Do something
}
有谁知道为什么这不起作用?
我认为将元组映射到联合的深度可能存在限制,但事实并非如此。
这可以正常工作:
type PortalEmailValidation = typeof validations['portal']['email'][number]
这似乎是 TypeScript 中的一个错误,如 microsoft/TypeScript#27709. The type checker apparently doesn't properly track the constraints for deep index access types when the keys are generic 中所述。这个问题已经开放了很长时间,没有任何进展的迹象,所以现在我们所能做的就是解决它。
当编译器不接受 T[K]
形式的索引访问时,一种方法是使用 conditional type inference, like T extends Record<K, infer V> ? V : never
(using the Record<K, V>
utility type)。如果 K
是 T
的 (non-optional) 键,那么 T
将被视为某些 V
的 Record<K, V>
,我们推断.
所以我们可以写 typeof validations[S][T] extends Record<number, infer V> ? V : never
而不是 typeof validations[S][T][number]
。或者等价地:
type ValidationsMap = {
[S in keyof typeof validations]: {
[T in keyof typeof validations[S]]:
typeof validations[S][T] extends { [k: number]: infer V } ? V : never
}
}
另一种方法是显式添加回缺失的约束。如果你有一个 你 知道的类型 A
可以分配给另一个类型 B
但编译器不知道这一点,那么你可以替换 A
与 Extract<A, B>
(使用 the Extract<T, U>
utility type),编译器将接受它。它知道 Extract<A, B>
可以分配给 B
。假设您关于 A
可分配给 B
的说法是正确的,那么 Extract<A, B>
的计算结果将只是 A
.
所以如果ValidationsMap[S][T]
可以赋值给string
但是编译器看不到,我们可以写成Extract<ValidationsMap[S][T], string>
:
function validate<S extends keyof ValidationsMap, T extends keyof ValidationsMap[S]>(
service: S, type: T, value: string
): Record<Extract<ValidationsMap[S][T], string>, boolean> { // okay
return null!
}