在 TypeScript 中定义没有不兼容错误的本地类型

Defining Local Types Without Incompatibility Errors in TypeScript

TypeScript 版本:3.8.2

搜索词: map, over, utility, type, result, keys, extract

代码

以下帮手为我工作得很好:

export type ComputeUnionValue<V extends AnyCodec[]> = {
  [i in Extract<keyof V, number>]: GetValue<V[i]>;
}[Extract<keyof V, number>];

我尝试简化这个助手,它在两个地方使用了 Extract<keyof V, number>

export type ComputeUnionValue<V extends AnyCodec[], E = Extract<keyof V, number>> = {
  [i in E]: GetValue<V[i]>;
}[E];

E[i in E] 个错误:

Type 'E' is not assignable to type 'string | number | symbol'.
  Type 'E' is not assignable to type 'symbol'.ts(2322)

V[i] 错误:

Type 'V[i]' does not satisfy the constraint 'AnyCodec'.
  Type 'V[E]' is not assignable to type 'Codec<VType, unknown>'.
    Type 'AnyCodec[][E]' is not assignable to type 'Codec<VType, unknown>'.ts(2344)

Type 'i' cannot be used to index type 'V'

我使用 E = 是为了在范围内使用该新类型...而不是作为可选参数。但它是一个可选参数的事实似乎影响了 "guarantees" 可以这么说,这导致了上面的不兼容性。有没有一种方法可以创建 E 类型——该实用程序类型的本地类型——以一种没有潜在类型不兼容的方式?

虽然我看到类型参数用作 'local types variable',但我认为这不是一个好主意,因为它们基本上将内部逻辑暴露给外部(毕竟有人可以传入 E而不是使用默认值)。它们还意味着用户必须知道哪些参数是 "real",哪些只是 "local",在我看来这是糟糕的 DX。 (仅供参考:GH 上有一个允许本地类型别名的提案,但我认为它没有任何进展)

也就是说,您收到错误的原因是 = 只是为类型参数提供了默认值,但类型参数可以是任何类型,包括不能使用的类型在映射类型中。

解决方案很简单,如果有点冗长,请为类型参数提供默认值和约束(使用extends):

export type ComputeUnionValue<V extends AnyCodec[], E extends Extract<keyof V, number> = Extract<keyof V, number>> = {
  [i in E]: GetValue<V[i]>;
}[E];

Playground Link