Cuando utilices tipos de unión, debes tener en cuenta los conceptos de covarianza y contravarianza. Se explican en la documentación, pero estos ejemplos pueden ayudarte.
Supongamos que tenemos una clase Calculator que utiliza una función como el ejemplo anterior:
class Calculator
{
public function add(int|float $x, int|float $y): int|float
{
return $x + $y;
}
}
Si extiendes la clase, se te permite cambiar los tipos de los parámetros y los tipos de retorno, pero solo de acuerdo con las siguientes reglas.
En primer lugar, los tipos de los parámetros pueden ser «más amplios», es decir, pueden aceptar más tipos que la clase base. Por ejemplo, esto está permitido:
class StringCalc extends Foo
{
public function add(float|int|string $x, float|int|string $y): int|float
{
if (!is_numeric($x) || !is_numeric($y)) {
throw new \InvalidArgumentException('$x and $y must be numeric');
}
return $x + $y;
}
}
Podríamos sustituir StringCalc en lugar de un Calculator original y todo seguiría funcionando. Quien llama seguiría pudiendo enviar el float o el int que originalmente podía, y el tipo de retorno seguiría siendo el int o float esperado.
De manera similar, puedes estrechar el tipo de retorno, es decir, hacerlo más específico eliminando tipos de la unión. Este ejemplo aprovecha esta regla:
class IntCalc extends Calculator
{
public function add(float|int $x, float|int $y) : int
{
return (int)($x + $y);
}
}
Como IntCalc sigue aceptando int y float, todavía puede sustituir a un Calculator original sin problemas.
Para leer más sobre lo que está y no está permitido, puedes consultar el principio de sustitución de Liskov (LSP).
Para ver un ejemplo de lo que no estaría permitido, echa un vistazo a InvalidCalc a continuación:
class InvalidCalc extends Calculator
{
public function add(int $x, int $y) : int|float|string
{
// Declaration must be compatible with Calculator->add(x: float|int, y: float|int)
// Return type declaration must be compatible with Calculator->add(x: float|int, y: float|int) : float|int
}
}
La clase InvalidCalc no compilará y tiene dos problemas. Al reducir lo que se permite como parámetro, InvalidCalc no podría sustituir al Calculator original porque ya no acepta float. Además, ahora podría devolver un string, que tampoco está dentro de los tipos de retorno permitidos de la clase original.