El conocimiento es el nuevo dinero.
Aprender es la nueva manera en la que inviertes
Acceso Cursos

Explicación del Index Signature en TypeScript

Desmitificación de los Index Signatures para mejorar la flexibilidad del código

· 5 min de lectura
Explicación del  Index Signature en TypeScript

Los index signature en TypeScript proporcionan una forma de definir una estructura de datos dinámica cuando las propiedades de un objeto no se conocen de antemano, pero sí los tipos de propiedades.

Permiten el acceso dinámico a las propiedades y son particularmente útiles cuando se trabaja con objetos con un conjunto variable de claves.

Este post profundizará en los inex signature, cómo usarlos y cuándo usarlos en TypeScript.

¿Qué es el index signature?


Un index signature se define utilizando corchetes [] y el tipo de las claves, seguido de dos puntos y el tipo de los valores correspondientes. Permite a TypeScript entender y aplicar la estructura esperada del objeto.

interface MyStats {
  [key: string]: number;
}
const scores: MyStats = {
  total: 50,
  average:80
}
// index siganture enforce the type constraint
// here, the value must be a number
const scores2: MyStats = {
  total: "50", //Type 'string' is not assignable to type 'number'.(2322)
  average:80
}

En este ejemplo, MyStats puede tener cualquier clave de cadena, y los valores asociados a esas claves deben ser de tipo número.

La sintaxis de las firmas de índice implica el uso de la notación [] dentro de la declaración de la interfaz o del tipo.

El siguiente ejemplo muestra la misma firma de índice para la interfaz y el tipo.

interface Car {
  [key: string]: boolean;
}

type CarType = {
  [key: string]: boolean;
}

Ten en cuenta que los index siganture pueden utilizar diferentes tipos de clave, como cadena, número, símbolo o tipo literal, y el tipo de valor asociado puede ser cualquier tipo válido de TypeScript.

CPU
1 vCPU
MEMORIA
1 GB
ALMACENAMIENTO
10 GB
TRANSFERENCIA
1 TB
PRECIO
$ 4 mes
Para obtener el servidor GRATIS debes de escribir el cupon "LEIFER"

Mezclar un index signature con miembros explícitos


En TypeScript, podemos mezclar un index siganture con declaraciones explícitas de miembros. Es útil para casos que requieren una combinación de propiedades conocidas y dinámicas.

interface CarConfiguration {
  [feature: string]: number;
  price: number;
}

Cuando mezclamos los index signature con miembros explícitos, todos los miembros explícitos deben ajustarse a los tipos de index signature.

// invalid case
interface CarConfiguration {
  [feature: string]: number;
  price: number;
  model: string; // Error: Property 'model' of type 'string' is not assignable to 'string' index type 'number'
}

// valid
interface CarConfiguration {
  [feature: string]: number | string;
  price: number;
  model: string;
}

Index signature de sólo lectura


El index signature admite el modificador readonly. Aplicando el modificador readonly, las propiedades del objeto serán inmutables.

interface Car {
  readonly [key: string]: boolean;
}

const toyota: Car = {hybrid: true, luxury: false};
toyota.hybrid = false; //Error: Index signature in type 'Car' only permits reading.(2542)

En el ejemplo anterior, se produce un error al intentar modificar la propiedad hybrid porque la interfaz sólo permite la lectura, no la escritura.

Cómo utilizar el index signature


Veamos un ejemplo real de cómo se pueden utilizar los index signature.

Imaginemos que estamos desarrollando una aplicación web con varias características. Cada característica incluye su propio conjunto de ajustes. También podemos activar o desactivar estas características.

interface FeatureConfig {
  [feature: string]: {
    enabled: boolean;
    settings: Record<string, boolean>;
  }
}

En este ejemplo, definimos una interfaz llamada FeatureConfig.

Utiliza un index signature para permitir nombres de propiedad dinámicos de tipo cadena asociados con una propiedad booleana habilitada y un objeto de configuración.

Es útil para representar configuraciones con nombres de características dinámicas y configuraciones asociadas. Por ejemplo, podemos aplicar la interfaz al siguiente objeto.

const features: FeatureConfig = {
  profile: {
    enabled: true,
    settings: {
      showPhoto: true,
      allowEdit: false,
    },
  },
  notification: {
    enabled: false,
    settings: {
      richText: true,
      batchMode: true
    },
  }
};

En el objeto features, los nombres de las features pueden variar, pero la estructura de cada feature permanece consistente.

Se espera que cada característica tenga un booleano habilitado y un objeto de configuración.

Para mejorar la seguridad de tipos, ¿podemos aplicar una restricción de tipo unión al nombre de la función en la interfaz anterior?

Si se conoce el conjunto de características de nuestra aplicación, podemos definir la unión de literales de cadena denominadosFeatureType.

type FeatureType = 'profile' | 'notification' | 'reporting';

La clave del index signature no admite el tipo de unión, pero podemos solucionarlo utilizando un tipo mapeado.

type FeatureConfig2 = {
  [feature in FeatureType]: {
    enabled: boolean;
    settings: Record<string, boolean>;
  }
}

[feature in FeatureType]es un tipo mapeado que itera sobre cada literal de cadena en el tipo de unión FeatureType (que incluye 'profile', 'notification', y 'reporting'), y utiliza cada valor como nombre de propiedad del tipo resultante.

He aquí un ejemplo de cómo podemos utilizarlo:

 const allFeatures: FeatureConfig2 = {
  profile: {
    enabled: true,
    settings: {
      showPhoto: true,
      allowEdit: false,
    },
  },
  notification: {
    enabled: false,
    settings: {
      richText: true,
      batchMode: true
    },
  },
    reporting: {
    enabled: true,
    settings: {
      template: false,
      advanceExport: true
    },
  },
};

Ten en cuenta que tenemos que incluir todas las características definidas en FeatureType en el objeto para que coincida con las expectativas de tipo.

Si queremos permitir un subconjunto de las características como clave, tenemos que modificar el tipo de firma de índice con un "?" como bandera opcional. Entonces, podríamos utilizar el tipo FeatureConfig2 para un objeto que sólo contenga un subconjunto de características.

type FeatureType = 'profile' | 'notification' | 'reporting';

type FeatureConfig2 = {
  [feature in FeatureType]?: {
    enabled: boolean;
    settings: Record<string, boolean>;
  }
}

const subsetFeatures: FeatureConfig2 = {
  profile: {
    enabled: true,
    settings: {
      showPhoto: true,
      allowEdit: false,
    },
  }
};

Cómo utilizar eficazmente las firmas índice


Algunos escenarios de uso común incluyen:

  • Objetos de configuración: Como ilustra el ejemplo anterior, los index signature destacan en escenarios en los que los objetos de configuración pueden tener claves dinámicas y valores asociados.
  • Transformación de datos:  Los index signature pueden ser beneficiosas cuando se trata de transformaciones o análisis sintáctico de datos. Permiten un manejo flexible de los datos de entrada con estructuras variables.
  • Extensibilidad: En proyectos en los que la extensibilidad es una prioridad, como las arquitecturas de plugins o los sistemas modulares, los index signature permiten añadir nuevos componentes sin modificar el código existente.

Aunque potentes, los index signature no deben utilizarse en exceso.

Antes de implementar un index signature, considera si una interfaz o definición de tipo más explícita podría representar mejor la estructura de datos, especialmente cuando las claves tienen significados específicos.

Otra consideración es aplicar rigurosamente escenarios de prueba que impliquen index signature.

Esto incluye probar varias combinaciones clave-valor para garantizar que la naturaleza dinámica de la estructura no introduce problemas imprevistos.

Evitando errores comunes y siguiendo las mejores prácticas, podemos utilizar las firmas de índice para hacer que el código TypeScript sea más flexible y resistente.

¡Feliz programación!

Fuente

Plataforma de cursos gratis sobre programación