Búsqueda de sitios web

Parámetros predeterminados de Typescript para objetos pasados a una función


Me gustaría inicializar mi objeto pasado a la función de manera similar a como aparece en los valores escalares, por ejemplo:

someFunction(myValue: number = 5) {  }

Entonces, mantengo tanto el tipo como el valor predeterminado. Me gustaría aplicar una construcción similar a lo siguiente:

someFunction(myObj: {val1: number, val2: number}) { }

Si voy directamente y simplemente hago la solución de copiar y pegar sin pensar:

someFunction(myObj: {val1: number = 5, val2: number = 10}) {}  

Cada enfoque de inicialización da un error de TypeScript A type literal property cannot have an initializer.. Está bien, entendido, no puedo mezclar literales de tipo con inicialización. Usando una analogía tenemos:

someFunction(myValue: number = 5) {  }
             ^^^^^^^  ^^^^^^   ^^
              |         |       +-- Initialization value
              |         +-- Type literal
              +-- Parameter name

Y análogamente:

someFunction(myObj: {val1: number, val2: number} = {val1: 5, val2: 10}) {}
             ^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^
              |         |                              +-- Initialization
              |         +-- Type literal
              +-- Parameter name

La solución es usar una sintaxis mucho más verbosa:

someFunction(myObj: {val1: number, val2: number} = {val1: 5, val2: 10}) {}

La pregunta es si es posible, tener un pastel y comérselo: tener sintaxis concisa como en la definición de valor predeterminado de valor escalar (number=5) y control completo de tipos con la definición de objetos?

PD Solicito puntos extra por arte ASCII :)

Respuesta #1

Si aprovechas la desestructuración, puedes reducir mucho el código duplicado, sin embargo, es importante estar al tanto de las diferencias. Aquí está el patrón que generalmente uso.

function fun({ val1 = 1, val2 = 2 } = {}) {
    return val1 + val2
}

La firma de esta función se infiere correctamente como function fun({ val1, val2 }?: { val1?: number; val2?: number; }): number.

Sin embargo, es importante tener en cuenta que esta función difiere en un par de formas importantes del original.

  1. fun({ val1: 2 }) resultará en que val2 siga siendo el valor predeterminado, 2

  2. Si necesitas un parámetro obligatorio (en el sentido de que si proporcionas algo, debes proporcionarlo), tienes que volver a especificar todo, lo que rápidamente se vuelve aún más desordenado ya que tienes inicializadores en dos ubicaciones. Esto se puede mitigar utilizando una interfaz para describir el tipo.

    interface Args {
        val1?: number
        val2?: number
        val3: string
    }
    
    function fun({ val1 = 1, val2 = 2, val3 }: Args = { val3: ' ' }) {
        return val3.repeat(val1 + val2)
    }
    

Respuesta #2

No hay una forma integrada de hacer esto. La solución más simple sería dejar que el compilador infiera el tipo de parámetro basándose en el valor predeterminado:

function someFunction(myObj = { val1: 5, val2: 10 }) { }

El problema con esta solución es que todas las propiedades son obligatorias en el parámetro, por lo que tendrías que especificarlas incluso si su valor fuera indefinido.

Para evitar esto, podemos definir una función auxiliar que devuelva un tipo con todos los elementos marcados como opcionales:

function params<T>(o: T): Partial<T> {
    return o;
}
function someFunction2(myObj = params({ val1: 5, val2: 10, otherValueWithoutDefault: null as number })) { } 

O una versión que soporte tanto parámetros opcionales como obligatorios:

function params<TRequired>() : <TOptional>(o: TOptional) => Partial<TOptional> & TRequired
function params<TOptional>(o: TOptional) : Partial<TOptional> 
function params<T>(o?: T): Partial<T> | (<TOptional>(o: TOptional) => Partial<TOptional> & T) {
    if(o != null) {
        return o;
    } else {
        return function(o : any) { return o; } as any
    }
}
function someFunction2(myObj = params<{ required: boolean}>()({ val1: 5, val2: 10})) { }

someFunction2({
    required: true,
    val1: 0
})

Artículos relacionados