Búsqueda de sitios web

problemas de TypeScript en la ciudad de las tuplas: preservando los tipos de los argumentos


Me está resultando difícil descubrir cómo hacer que mi función and funcione aquí: tiene un trabajo simple. Debe aceptar un montón de elementos y concatenarlos en un arreglo que comience con "and"

// the items 'and' may accept
type Item = false
  | {type: "alpha", a: boolean}
  | {type: "bravo", b: number}

// 'and' function adds items to an array
function and<Items extends Item[]>(
    ...items: Items
  ): ["and", ...Items] {

  return ["and", ...items]
}

// calling 'and' with a few items
const [op, item1, item2, item3] = and(
  false,
  {type: "alpha", a: true},
  {type: "bravo", b: 3.14},
)

// verifying the types are preserved
op    //> ✔ type "and"
item1 //> ✘ type Item, expected false
item2 //> ✘ type Item, expected {type: "alpha", a: true}
item3 //> ✘ type Item, expected {type: "bravo", b: 3.14}

el problema es que item1, item2 y item3 están perdiendo toda su información de tipo específica original, por lo tanto mi función and es en realidad una operación destructiva sobre los tipos; en cambio, necesito que los tipos se conserven

¿Hay un enfoque diferente? ¡Gracias!

Respuesta #1

De hecho, Typescript no es muy adecuado para este tipo de operaciones (al menos que yo sepa).

El problema parece ser que hay casos en los que el compilador no podrá tipar correctamente a esos chicos

const whatsMyType: Item = false

// calling 'and' with a few items
const [op, item1] = and(
  whatsMyType
)

Optaría por cualquiera de los dos enfoques siguientes:

Sobrecarga

Pros: Tener la mecanografía correcta sin mucho esfuerzo

Contras: No muy escalable

(obs: es el enfoque elegido por RxJS pipe, por ejemplo)

function and<A extends Item>(a: A): ['and', A];
function and<A extends Item, B extends Item>(a: A, b: B): ['and', A, B];
function and<A extends Item, B extends Item, C extends Item>(a: A, b: B, c: C): ['and', A, B, C];
function and<Items extends Item[]>(
    ...items: Items
  ): ["and", ...Items] {

  return ["and", ...items]
}

Predicado de tipo

Pros: Extensible

Contras: Lógica extra en el código

const isFalseItem = (a: Item): a is false => {return a === false};
const isAlphaItem = (a: Item): a is ({type: "alpha", a: boolean}) => { return !isFalseItem(a) && a.type === 'alpha'}
const isBravoItem = (a: Item): a is ({type: "bravo", b: number}) => { return !isFalseItem(a) && a.type === 'bravo'}

const [op, item1, item2, item3] = and(
  false,
  {type: "alpha", a: true},
  {type: "bravo", b: 3.14},
)

if (isAlphaItem(item1)) {
 // do something alpha
} else if (isBravoItem(item1)) {
 // do something bravo
}