javascript - typescript :将元组映射到联合类型似乎在嵌套对象中不起作用

标签 javascript typescript

我在 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
}

有谁知道为什么这不起作用?

我认为将 Tuple 映射到 Union 的深度可能存在限制,但事实并非如此。

这可以正常工作:

type PortalEmailValidation = typeof validations['portal']['email'][number]

最佳答案

这似乎是 TypeScript 中的一个错误,如 microsoft/TypeScript#27709 中所述.类型检查器显然没有正确跟踪深度 index access 的约束键为 generic 时的类型.这个问题已经开放了很长时间,没有任何进展的迹象,所以现在我们所能做的就是解决它。


当编译器不接受 T[K] 形式的索引访问时的一种方法就是用conditional type inference , 比如 T extends Record<K, infer V> ? V : never (使用 Record<K, V> utility type )。如果KT 的(非可选)键, 然后 T将被视为 Record<K, V>对于一些 V ,我们推断。

所以不是 typeof validations[S][T][number] ,我们可以写成typeof validations[S][T] extends Record<number, infer V> ? V : never .或者等价地:

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但是编译器不知道这个,那么你可以替换AExtract<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!
}

Playground link to code

关于javascript - typescript :将元组映射到联合类型似乎在嵌套对象中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72460550/

相关文章:

javascript - 移动 Firefox 中的触摸事件未触发

typescript - 无法访问 Firebase Functions 项目中的全局方法

Typescript 接口(interface)合并使返回类型更具体

typescript - 如何在 typescript 中用另一个参数约束可选参数

javascript - Angular 5多个条件路由: "AuthGuards"

javascript - 如何使用 Angular 获取数组中对象的索引?

javascript - 使用按键和 jquery 移动一个 div?

javascript - 通过 JavaScript 使用 HTML5 音频标签

javascript - 如何从 Controller 动态更改指令的 templateUrl

javascript - 为什么 x.func1 x.fun2 x.func3 函数在 onClick 中不起作用