Búsqueda de sitios web

typescript - Formas de obtener el tipo literal de cadena de los valores de un array sin la sobrecarga de un enum


En mi proyecto necesito una serie de tipos de literales de cadena y arreglos de valores permitidos para varias tareas como los typeguards.

Esto es lo que tenemos:

type Animal = 'cat' | 'dog' | 'rabbit' | 'snake'
const animals: Animal[] = ['cat', 'dog', 'rabbit', 'snake']

Funciona, pero requiere listar las claves varias veces en el código fuente.

Otra forma es usar enums como se sugiere en Obtener un array de valores de tipo literal de cadena, pero crea sobrecarga en tiempo de ejecución ya que el código compilado para enum es más grande que el array.

Encontré otra manera de hacerlo que no tiene sobrecarga de ejecución, pero no estoy seguro de si funcionará en el futuro, ya que podría ser un error.

const notWidened = <T extends string>(val: T[]) => val

const animals = notWidened(['cat', 'dog', 'rabbit', 'snake'])
type Animal = typeof animals[0]

Entonces, la pregunta es si es seguro usar este fragmento o si va a fallar en el futuro. ¿Hay una mejor manera de obtener tanto el tipo de cadena literal como el arreglo sin duplicación?

Respuesta #1

No puedo encontrar una buena documentación sobre esto en este momento, pero tu función notWidened() funciona perfectamente y no es un error. TypeScript inferirá literales de cadena para variables de tipo genérico si limitas el parámetro de tipo a string o a un subtipo de string. Así que <T extends keyof U>, <T extends string>, <T extends 'a'|'b'>, etc., inferirán literales de cadena para T. (También puedes inferir literales de number o boolean si limitas T de manera similar).

Así que tu código está perfectamente bien por lo que puedo ver; lo único que podría hacer de manera diferente es

type Animal = (typeof animals)[number]

en lugar de

type Animal = typeof animals[0]

ya que el 0º elemento de animals es en realidad 'cat', aunque le hayas dicho a TypeScript que es 'cat'|'dog'|.... El tuyo está bien, sin embargo.

Como comenté arriba, si quieres que TypeScript considere que animals es una tupla donde animals[0] es del tipo 'cat' y animals[1] es del tipo 'dog', etc., puedes usar algo como la función tuple() en tuple.ts (ACTUALIZACIÓN julio 2018, a partir de TypeScript 3.0 el compilador podrá inferir automáticamente los tipos de tupla, por lo que la función puede ser más concisa):

export type Lit = string | number | boolean | undefined | null | void | {};
export const tuple = <T extends Lit[]>(...args: T) => args;

const animals = tuple('cat', 'dog', 'rabbit', 'snake');
type Animal = (typeof animals)[number];  // union type

lo cual podría serte útil.

TL;DR: 👍 tu código está bien.

¡Espero que eso ayude; buena suerte!

Respuesta #2

Tal vez puedas usar un const enum de cadenas, esto no tiene sobrecarga en tiempo de ejecución. Compila los valores en el código (Playground):

const enum Animals {
  Cat = 'cat',
  Dog = 'dog',
  Rabbit = 'rabbit',
  Snake = 'snake'
}

alert(Animals.Cat)

// Generated JavaScript code: 
// alert("cat" /* Cat */);

Artículos relacionados