Typecript tiene uniones, entonces, ¿las enumeraciones son redundantes?
Desde que TypeScript introdujo los tipos de unión, me pregunto si hay alguna razón para declarar un tipo de enumeración. Considere la siguiente declaración de tipo de enumeración:
enum X { A, B, C }
var x: X = X.A;
y una declaración de tipo de unión similar:
type X: "A" | "B" | "C"
var x: X = "A";
Si básicamente tienen el mismo propósito y los sindicatos son más poderosos y expresivos, entonces ¿por qué son necesarias las enumeraciones?
Con las versiones recientes de TypeScript, es fácil declarar tipos de unión iterables. Por lo tanto, deberías preferir los tipos de unión a las enumeraciones.
Cómo declarar tipos de unión iterables
const permissions = ['read', 'write', 'execute'] as const;
type Permission = typeof permissions[number]; // 'read' | 'write' | 'execute'
// you can iterate over permissions
for (const permission of permissions) {
// do something
}
Cuando los valores reales del tipo de unión no se describen muy bien, puedes nombrarlos como lo haces con las enumeraciones.
// when you use enum
enum Permission {
Read = 'r',
Write = 'w',
Execute = 'x'
}
// union type equivalent
const Permission = {
Read: 'r',
Write: 'w',
Execute: 'x'
} as const;
type Permission = typeof Permission[keyof typeof Permission]; // 'r' | 'w' | 'x'
// of course it's quite easy to iterate over
for (const permission of Object.values(Permission)) {
// do something
}
No se pierda as const
la afirmación que juega un papel crucial en estos patrones.
¿Por qué no es bueno usar enumeraciones?
1. Las enumeraciones no constantes no se ajustan al concepto "un superconjunto escrito de JavaScript"
Creo que este concepto es una de las razones cruciales por las que TypeScript se ha vuelto tan popular entre otros lenguajes altJS. Las enumeraciones no constantes violan el concepto al emitir objetos JavaScript que viven en tiempo de ejecución con una sintaxis que no es compatible con JavaScript.
2. Las enumeraciones constantes tienen algunos inconvenientes
Las enumeraciones constantes no se pueden transpilar con Babel
Actualmente existen dos soluciones para este problema: deshacerse de las enumeraciones constantes manualmente o con un complemento babel-plugin-const-enum
.
Declarar enumeraciones constantes en un contexto ambiental puede ser problemático
No se permiten enumeraciones de constantes ambientales cuando --isolatedModules
se proporciona la bandera. Un miembro del equipo de TypeScript dice que " const enum
en DT realmente no tiene sentido" (DT se refiere a DefinitelyTyped) y "debe usar un tipo de unión de literales (cadena o número) en lugar" de enumeraciones constantes en el contexto ambiental.
Las enumeraciones constantes bajo --isolatedModules
bandera se comportan de manera extraña incluso fuera de un contexto ambiental
Me sorprendió leer este comentario en GitHub y confirmé que el comportamiento sigue siendo cierto con TypeScript 3.8.2.
3. Las enumeraciones numéricas no son seguras para escribir
Puede asignar cualquier número a enumeraciones numéricas.
enum ZeroOrOne {
Zero = 0,
One = 1
}
const zeroOrOne: ZeroOrOne = 2; // no error!!
4. La declaración de enumeraciones de cadenas puede ser redundante
A veces vemos este tipo de enumeraciones de cadenas:
enum Day {
Sunday = 'Sunday',
Monday = 'Monday',
Tuesday = 'Tuesday',
Wednesday = 'Wednesday',
Thursday = 'Thursday',
Friday = 'Friday',
Saturday = 'Saturday'
}
Debo admitir que hay una característica de enumeración que no se puede lograr mediante tipos de unión.
Incluso si es obvio por el contexto que el valor de la cadena está incluido en la enumeración, no puede asignarlo a la enumeración.
enum StringEnum {
Foo = 'foo'
}
const foo1: StringEnum = StringEnum.Foo; // no error
const foo2: StringEnum = 'foo'; // error!!
Esto unifica el estilo de asignación de valores de enumeración en todo el código al eliminar el uso de valores de cadena o literales de cadena. Este comportamiento no es consistente con cómo se comporta el sistema de tipos TypeScript en otros lugares y es un poco sorprendente, y algunas personas que pensaron que esto debería solucionarse plantearon problemas ( this y this ), en los que se menciona repetidamente que la intención de las enumeraciones de cadenas es para proporcionar tipos de cadenas "opacos": es decir, se pueden cambiar sin modificar los consumidores.
enum Weekend {
Saturday = 'Saturday',
Sunday = 'Sunday'
}
// As this style is forced, you can change the value of
// Weekend.Saturday to 'Sat' without modifying consumers
const weekend: Weekend = Weekend.Saturday;
Tenga en cuenta que esta "opacidad" no es perfecta ya que la asignación de valores de enumeración a tipos literales de cadena no está limitada.
enum Weekend {
Saturday = 'Saturday',
Sunday = 'Sunday'
}
// The change of the value of Weekend.Saturday to 'Sat'
// results in a compilation error
const saturday: 'Saturday' = Weekend.Saturday;
Si cree que esta característica "opaca" es tan valiosa que puede aceptar todos los inconvenientes que describí anteriormente a cambio de ella, no puede abandonar las enumeraciones de cadenas.
Cómo eliminar enumeraciones de tu código base
Con la no-restricted-syntax
regla de ESLint, como se describe .
Por lo que veo, no son redundantes, por la sencilla razón de que los tipos de unión son puramente un concepto de tiempo de compilación, mientras que las enumeraciones en realidad se transpilan y terminan en el javascript resultante ( muestra ).
Esto le permite hacer algunas cosas con enumeraciones, que de otro modo serían imposibles con tipos de unión (como enumerar los posibles valores de enumeración )