打字稿:如何使用未知的字符串键遍历对象

时间:2020-11-12 02:30:59

标签: typescript

我正在尝试使用JavaScript使用API​​。

端点每次可以给我一个具有唯一键的对象,例如:

{
  "-MKlw6VSTSf-FPBaTxfB": {
    "created_at": 1603934385.9833121,
    "jugadores": 0,
    "posiciones": 4
  },
  "-MKlxam1Zjtz14wZgMNp": {
    "created_at": 1603934776.2540152,
    "jugadores": 0,
    "posiciones": 4
  },
  "-MKm8JvbKJmMAumJbmoU": {
    "created_at": 1603937848.809657,
    "jugadores": 0,
    "posiciones": 4
  },
  "-ML3-HtshKPcKrME5Jk6": {
    "created_at": 1604237470.857504,
    "jugadores": 0,
    "posiciones": 4
  }
}

或者它可以给我这样的错误:

{
  "error": true,
  "mensaje": "Hubo un error"
}

我已经声明了以下类型:


type APIError = {
  error: boolean
  mensaje: string
}

type ListadoJuegosPublicos = {
  [key:string]: {
    jugadores: number
    posiciones: number
    created_at: number
  }
}

我有这个函数签名来检索数据:

async juegosPublicos ():Promise<APIError|ListadoJuegosPublicos>

该功能按预期工作。但是,当我尝试遍历响应时,出现此错误:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'APIError | ListadoJuegosPublicos'.
  No index signature with a parameter of type 'string' was found on type 'APIError | ListadoJuegosPublicos'.ts(7053)

这是我试图用来遍历对象的代码

const juegos = await api.juegosPublicos()
if (juegos.error && typeof juegos.mensaje === 'string') {
  console.error(mensaje)
  return
}

Object.keys(juegos).forEach((juegoId:string) => {
  console.log(juegos[juegoId]) // <- this is the line than generates the error
})

如何正确遍历打字稿中的对象

2 个答案:

答案 0 :(得分:2)

开始:我不明白您为什么要检查juegos.error的真实性。如果juegosAPIError,则此值为truefalse;如果它是false,那么您就不应检查其真实性,因为您会错误地认为它不是APIError,而是它。如果juegosListadoJuegosPublicos,则此值为undefined或对象;如果它是一个对象,那么您不应该检查真实性,因为您会错误地认为它是APIError,而不是它。

接下来,我将仅保留typeof juegos.mensaje === 'string'检查,因为这足以确定某物是否为APIError


显然,即使 you 理解typeof juegos.mensaje !== 'string'暗示juegosListadoJuegosPublicos,但编译器也不理解 ,并且不对juegos执行基于control flow analysis的缩小。也就是说,对juegos.mensaje的支票不会充当juegos上的type guard

看起来ListadoJuegosPublicos的问题是index signature,如果是,则microsoft/TypeScript#17960的问题是相关的开放GitHub问题,列为错误。如果是这样,看来很快就不会解决。

无论如何,当编译器无法将某些代码识别为类型保护时,您可以选择将该代码提取到自己的user-defined type guard function中。例如:

function isAPIError(x: APIError | ListadoJuegosPublicos): x is APIError {
  return (typeof x.mensaje === 'string');
}

这与以前相同,但被显式标记为类型防护函数,该函数接受名为x的值并返回一个boolean值,该值确定编译器是否对待{{1} }作为x。现在,我们在原始代码中调用类型保护功能:

APIError

,并且有效。在 if (isAPIError(juegos)) { console.error(juegos.mensaje) return } Object.keys(juegos).forEach((juegoId: string) => { console.log(juegos[juegoId]) // okay now }); 语句之后,编译器现在认识到return必须是juegos并允许字符串索引迭代。

Playground link to code

答案 1 :(得分:0)

您的代码可以正常工作。实际问题在于下面的行

async juegosPublicos ():Promise<APIError|ListadoJuegosPublicos>

您可以做的一件事是将type替换为any:

async juegosPublicos ():Promise<any>