Búsqueda de sitios web

typescript - Asegúrate de que todos los miembros de un objeto implementen alguna versión de una interfaz genérica


¿Es posible que un tipo de firma de índice en TypeScript declare que cada índice implemente alguna versión de una interfaz genérica? Me gustaría escribir código así:

interface HasState<T> {
  state : T;
}

interface ModuleDefinition {
  [moduleName : string] : <T>HasState<T>;
}

Aquí está mi caso de uso:

Tengo un tipo mapeado de TypeScript para un objeto:

interface ModuleDefinition {
  [moduleName : string] : HasState;
}

interface HasState {
  state : any;
}

Este código asegura que cada valor en un objeto ModuleDefinition tenga un parámetro llamado state.

Sin embargo, si uso este tipo mapeado para intentar desempaquetar los objetos de estado:

type UnwrappedModules<Mod extends ModuleDefinition> = {
  [K in keyof Mod] : Mod[K]["state"];
}

y declarar y usar una función

function unwrap<Mod extends ModuleDefinition>(mod: Mod) : UnwrappedModules<Mod> {
  // ...
}

const result = unwrap({
  apple: {
    state: {
      color: 'red'
    }
  }
}).apple

result tiene tipo any, en lugar de { color : string }, como yo esperaría. ¿Hay alguna manera de hacer que TypeScript infiera el tipo correcto aquí?

Respuesta #1

¡Sí, puedes hacer esto! La solución es mirar el problema al revés: puedes usar tipos mapeados para envolver incluso si no puedes usarlos para desenvolver:

type StateWrapped<T> = {
  [K in keyof T]: HasState<T[K]>;
}

En lugar de que tu función unwrap() acepte una entrada del tipo simple T y devuelva un T sin envolver, puede tomar una entrada del tipo envuelto T y devolver el T simple:

function unwrap<T>(mod: StateWrapped<T>): T {
  var ret = {} as T;
  for (var k in mod) {
    ret[k] = mod[k].state;
  }
  return ret;
}

Todo esto pasa la verificación de tipos y tiene el resultado que estabas buscando:

const result = unwrap({
  apple: {
    state: {
      color: 'red'
    }
  },
  banana: {
    state: {
      ripe: false
    }
  }
});
console.log(result.apple.color.charAt(0));
console.log(result.banana.ripe);

¡Espero que eso ayude!