Búsqueda de sitios web

Tipo genérico de TypeScript para argumento de valor predeterminado opcional


Soy nuevo en Typescript y estoy teniendo un problema con los tipos genéricos. Estoy creando una biblioteca que lee nuestra configuración. Debido a que nuestra configuración es un JSON arbitrario, estamos usando el tipo any. Sin embargo, cuando un usuario utiliza esta biblioteca, debería conocer la forma de los datos que espera y quiero crear una función para recuperar información de la configuración a la que podamos pasar un tipo para eliminar el tipo any de la cadena. Además, quiero que esta función que recupera el valor de la configuración tome un argumento opcional que será el valor predeterminado que se devuelva si la clave falta en la configuración. Mi problema es averiguar cómo tipar correctamente esta función de modo que el tipo de retorno de la función sea ya sea el tipo que pasamos como genérico o el tipo del valor predeterminado, teniendo en cuenta que podemos omitir completamente el argumento del valor predeterminado.

He reducido mi caso de uso a este ejemplo realmente simple.

/* Our config provider module */

//In our real use, config is loaded from a JSON file that can be an arbitrary shape so we must use any type here
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config: Record<string, any> = {
  'mode': 'alpha'
}

const getConfig = <Result, DefaultValue extends Result | undefined = undefined>(key: string, defaultValue?: DefaultValue): Result | DefaultValue => {
  const result = config[key] as Result
  return result ?? defaultValue
}

/* In another module */
// The consumer of the config knows what shape they are going to read so we want to break the `any` usage here:

// mode should be of type string | undefined since a default value was not provided
const mode = getConfig<string>('mode')

// mode2 should be of type string | string (or just string) since a default value was provided
const mode2 = getConfig<string>('mode', 'omega')

Este ejemplo no compila y obtengo el error:

El tipo 'Result | DefaultValue | undefined' no se puede asignar al tipo 'Result | DefaultValue'. El tipo 'undefined' no se puede asignar al tipo 'Result | DefaultValue'.ts(2322)

¿Algún consejo sobre cómo puedo tipar correctamente mi función getConfig de manera que si paso un tipo para el Resultado, la función infiera correctamente el tipo del argumento defaultValue (incluyendo undefined) y lo use como parte de su tipo de retorno, manteniendo también que si realmente pasamos un valor para el defaultValue, entonces el tipo no incluya undefined?

Nota: Soy consciente de que lo que quiero no validará realmente el contenido de la estructura de datos JSON y se compilará sin problemas, pero si al analizar el JSON el valor no coincide con lo que pasamos como el tipo genérico, podría fallar en tiempo de ejecución. Nuestro objetivo será validar el JSON más adelante usando un esquema o algo similar. Por ahora solo quiero averiguar cómo tipar esto correctamente.

Respuesta #1

Lo que parece que estás diciendo es que el valor devuelto es o el valor del config, o el defaultValue. Debido a que defaultValue es opcional, esto significa que return result ?? defaultValue también incluye la posibilidad de undefined.

Si quieres que undefined sea posible (en el caso de que falte en config Y no se especifique defaultValue), entonces el tipo de retorno también debe incluir undefined.

Si quieres que el valor de retorno siempre sea un valor, y no estás proporcionando uno con defaultValue, entonces necesitas un guardián de tipo para prevenir la posibilidad de que se devuelva undefined, con un throw (lo que hace que el tipo de retorno sea efectivamente T | never).

También parece que no es necesario incluir la aserción de tipo en config[key] para reducir de any a T, ya que eso está implícito por el tipo de retorno.

const getConfig = <T>(key: string, defaultValue?: T): T => {
  const result = config[key] ?? defaultValue
  if (result === undefined) {
    throw "Can't be undefined without a default value"
  }
  return result
}

Enlace del parque