带有元组类型联合的 typescript 类型推断

标签 typescript typescript-typings typescript-generics

给定以下类型

type Tool = 'network_query' | 'cluster'
type Network = 'one' | 'two' | 'three'

class QueryOneParams {...}
class QueryTwoParams {...}
class QueryThreeParams {...}
class ClusterParams {...}

我正在尝试定义 Tool 组合之间的映射和 Network这样我就可以写这样的东西:

const queryParamsLookup = {
    'one': QueryOneParams,
    'two': QueryTwoParams,
    'three': QueryThreeParams
}

type Job<Tool, Network> = {
    id: string,
    params: JobParams<Tool, Network>
}

在哪里

  • JobParams<'network_query', 'one'>解析为 QueryOneParams
  • JobParams<'network_query', 'two'>解析为 QueryTwoParams
  • JobParams<'network_query', 'three'>解析为 QueryThreeParams
  • JobParams<'cluster'>解析为 ClusterParams
  • JobParams<'cluster', 'one'> , JobParams<'cluster', 'two'>JobParams<'cluster', 'three'>无效

这需要我以某种方式定义第二个通用参数 'one' | 'two' | 'three'仅在第一个参数为 'network_query' 时使用且需要. Afaik,Typescript 不支持基于另一个参数类型的可选定义的泛型参数。

这样对吗?我很乐意在这里是错误的:)

作为替代方案,我定义了一个辅助类型,如下所示:

type NetworkQueryJobType = {
    [N in keyof typeof queryParamsLookup]: ['network_query', N]
}[keyof typeof queryParamsLookup]
// ['network_query', 'one'] | ['network_query', 'two'] | ['network_query', 'three']

type JobType = NetworkQueryJobType | ['cluster']
// ['network_query', 'one'] | ['network_query', 'two'] | ['network_query', 'three'] | ['cluster']

并修改了Job的定义到

type Job<JobType> = {
    id: string,
    params: JobParams<JobType>
}

使用这种方法,我无法让类型推断在映射器类型中正常工作 JobParams :

type JobParams<T extends JobType> = T extends ['network_query', infer N] ?
typeof queryParamsLookup[N] // Error: N is not compatible with 'one' | 'two' | 'three'
: ClusterParams

我可以通过以下方式解决类型推断问题:

type JobParams<T extends JobType> = T extends ['network_query', infer N] ?
N extends Network ?
    typeof queryParamsLookup[N] // No error
    : ClusterParams
: ClusterParams

然而,所有这些仍然让我在输入时自动完成性能不佳,例如:

const params: JobParams<['

VSCode 不会提示 'cluster' | 'network_query'

总而言之,我想在这里打一场必败仗。我做错了什么吗?

Playground Link

最佳答案

您可以使用通用参数默认值和中性类型(例如null)来匹配提供的行为。

我对你的代码做了一些假设,如果我解释不正确,请纠正我。

首先,queryParamsLookup 是一个类型QueryParamsLookup:

type QueryParamsLookup = {
    'one': QueryOneParams,
    'two': QueryTwoParams,
    'three': QueryThreeParams
}

按照问题中的定义定义对象会将类型解析为提供的类构造函数的类型,这可能不是预期的,因为这种情况很少见。

JobParams 类型可以定义为:

type JobParams<T extends Tool, N extends T extends "network_query" ? Network : null = T extends "network_query" ? Network : null> =
    N extends Network ? QueryParamsLookup[N] : ClusterParams

这将满足以下要求:

// valid cases
const one: JobParams<"network_query", "one"> = new QueryOneParams()
const two: JobParams<"network_query", "two"> = new QueryTwoParams()
const three: JobParams<"network_query", "three"> = new QueryThreeParams()
const cluster: JobParams<"cluster"> = new ClusterParams()

// invalid cases
const inv1: JobParams<"cluster", "one"> = new QueryOneParams()
const inv2: JobParams<"cluster", "two"> = new QueryTwoParams()
const inv3: JobParams<"cluster", "three"> = new QueryThreeParams()

JobParams 的定义是悲观的,当联合作为其第一个参数提供时,这意味着它会将联合视为不是 network_query。这种行为可以通过翻转三元运算符来反转。

playground 的链接与解决方案。

虽然这也完全有可能用泛型元组来做,但在我看来这太过分了而且不是很直观。

关于带有元组类型联合的 typescript 类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70134056/

相关文章:

typescript - 在指定其他类型时,如何让 TypeScript 推断受约束的泛型类型的值?

Typescript:如何用全名填充对象属性

javascript - Typescript/ES6 中单例模式的正确方法是什么?

Typescript:在条件类型中使用元组

typescript - 将 KeysOfType 与泛型一起使用

node.js - TypeScript - 带有 Sequelize 的存储库模式

typescript - 基于另一个属性的通用映射类型属性

typescript - ng2-charts 的自定义工具提示

html - Angular ,类型错误 : Cannot read property 'toLowerCase' of undefined

TypeScript TupleIndexed 类型并强制只读作为参数传入的数组