No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Sobrecarga de funciones: la soluci贸n

11/22
Recursos

Aportes 15

Preguntas 3

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Resumen de la solucion

En conclusi贸n, lo que vamos a hacer es escribir de nuevo la funci贸n con los par谩metros y su tipo de dato de retorno antes de declarar la funci贸n como tal, para que de esa forma TS sepa en que casos se retorna cierto valor. Se que no le entendiste, veamos un ejemplo

type customType = string | string[];

function parseStr(arg: string): string[]
function parseStr(arg: string[]): string

function parseStr(arg: customType): customType {
  // code here...
}

Las dos primeras funciones parseStr son las que se le llama sobrecarga de funciones y le ayuda a TS a predecir que tipo de dato retornar en ciertos casos espec铆ficos.

Les comparto mis apuntes. 馃槃

Uso de sobrecarga de funciones

Para realizar la sobrecarga de funciones tenemos simplemente declarar otras funciones con el mismo nombre de la funci贸n que tiene la l贸gica implementada. Dentro de los par谩metros de las nuevas funciones vamos a definir el tipo de dato que se va a recibir y adem谩s tenemos que aclarar el tipo de dato que se va a retornar con ese par谩metro.

Adem谩s, en la funci贸n que tiene toda la l贸gica a sus par谩metros podemos colocarle como tipo de dato unknown al igual que su retorno.

Sintaxis

function functionName (parameter: dataType): dataTypeReturn;
function functionName (parameter: dataType): dataType {
	statements
}

Buenas pr谩cticas

  • Cuando tengamos sobre carga de m茅todos y por alguna raz贸n tengamos un unknown o any, en esa sobre carga, lo mejor es dejar ese unknowno any al final. Caso contario no funcionar谩 correctamente esa aserci贸n de tipos y por ende el autocompletado del editor.
  • Evaluar si realmente se necesita una sobre carga o simplemente puedes buscar otra forma de hacerlo como ser usando valores opcionales.

C贸digo de la clase

// Galeed -> ['G', 'a', 'l', 'e', 'e', 'd']
// ['G', 'a', 'l', 'e', 'e', 'd'] -> Galeed
// number -> boolean

function parseStr (input: string): string[];
function parseStr (input: string[]): string;
function parseStr (input: number): boolean;
function parseStr (input: unknown): unknown {
    if (Array.isArray(input)) return input.join(''); // string
    if (typeof(input) === 'string') return input.split(''); // string[]
    if (typeof(input) === 'number') return true; // boolean
}

const rtaArray = parseStr('Galeed');
rtaArray.reverse();
const rtaString = parseStr(['G', 'a', 'l', 'e', 'e', 'd']);
const rtaNumber = parseStr(1);
console.log('rtaArray: Galeed -> ', rtaArray);
console.log("rtaString: ['G', 'a', 'l', 'e', 'e', 'd'] -> ", rtaString);
console.log("rtaNumber: number -> ", rtaNumber);

Veo que en el ejemplo de typeORM se hace una sobrecarga 鈥淚nnecesaria鈥 ya que los tres overload retornan el mismo tipo de dato y luego adem谩s al implementar la funci贸n ponen los par谩metros como opcionales usando el ?. Ser谩 que si est谩 tan mala esa pr谩ctica? o por qu茅 lo hicieron?

Tambi茅n es posible manejar la sobrecarga con arrow functions, de esta manera lo hice yo:

type parseStrIO = {
  (input: string): string[],
  (input: string[]): string
}

export const parseStr: parseStrIO = (input: any) => {
  if(Array.isArray(input)){
    return input.join("");
  }else{
    return input.split("");
  }
}

const inputStr = "jason";
const inputArray = ["j", "a", "s", "o", "n"];
const outputArray = parseStr(inputStr);
const outputStr = parseStr(inputArray);

console.log(inputStr, " ==> ", outputArray);
console.log(inputArray, " ==> ", outputStr);
console.log("TS detects an array: ", outputArray.includes('a'));
console.log("TS detects an string: " , outputStr.toUpperCase());

Encontr茅 una manera de hacerlo con arrow functions:

type IOverload = {
    (param: number): number[];
    (param: object): object[];
}

const overloadedArrowFunc: IOverload = (param: any) => {
    return [param, param];
}

let val = overloadedArrowFunc(4);

Como en la function refactorizada se defini贸 un input de tipo unknow, decid铆 probar pasandole un booleano que fue de los tipos de datos que no definimos en la sobrecarga y como resultado es muy interesante como TS sigue verificando los input seg煤n la diferentes opciones que declaramos previamente y como no hace match con ninguna entonces arroja una advertencia

Uno de los principales beneficios de las sobrecargas es la ayuda que nos brindan (a trav茅s del Intellisense) a la hora de programar:

Aqu铆 me recuerda mucho el Multiple Dispatch de Julia. En eso s铆 es m谩s poderos o mejor implimentado

Al estar escuhando los videos al 1.5 lo veo como normal, pero al colocarlo a 1 es como si la normal es super lenta

Encontr茅 esta manera de hacer ese ejercicio ya que cuando trato de usar unknown no me funciona sale error creo que es por la versi贸n pero no estoy seguro

interface StringArrayResult {
  kind: 'stringArray';
  value: string[];
}

interface StringResult {
  kind: 'string';
  value: string;
}

interface BooleanResult {
  kind: 'boolean';
  value: boolean;
}

type ParseStrResult = StringArrayResult | StringResult | BooleanResult;

export function parseStr(input: string): StringArrayResult;
export function parseStr(input: string[]): StringResult;
export function parseStr(input: number[]): BooleanResult;

export function parseStr(input: unknown): ParseStrResult {
  if (Array.isArray(input)) {
    return { kind: 'stringArray', value: input.join('') };
  } else if (typeof input === 'string') {
    return { kind: 'string', value: input.split(',') };
  } else if (typeof input === 'number') {
    return { kind: 'boolean', value: input > 0 };
  }

  throw new Error('Unsupported input type');
}

// Ejemplo de uso
const rtaArray = parseStr(['j', 'u', 'a', 'n']);
console.log('Aqui convertimos el array a string ', rtaArray.value);

const rtaArrayV2 = parseStr('j,u,a,n');
console.log('Aqui convertimos el string a array ', rtaArrayV2.value);
 

Entend铆 que una funci贸n puede ser sobre cargada para definirle que reciba o devuelva tipos de datos distintos seg煤n se requiera.

function parseStr(input: string): string[];
function parseStr(input: string[]): string;
function parseStr(input: number): boolean;

function parseStr(
 input: unknown
): unknown {

	if(Array.isArray(input)) { // asercion
		return input.join("") // string
	}else if(typeof input === 'string') {
		return input.split("") // string[]
	} else if(typeof input === 'number') {
		return true // boolean
	}
}

const rta1 = parseStr("Virus")
rta1.reverse()

const rta0 = parseStr(["V","i","r","u","s"])
rta0.toLowerCase()

lo vi en mucho c贸digo de terceros, pero nunca supe que se trataba de sobrecarga de funciones
la verdad, muy 煤til

Adem谩s de las funciones regulares, los m茅todos en las clases tambi茅n pueden sobrecargarse.

Codigo de las mejores practicas

First Case: uknnown is not at the end

declare function fn(x: unknown): unknown;
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
var myElement: HTMLDivElement;
var x = fn( myElement ); // x: string 


//Sol: keep the unknown at the end
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: unknown): unknown;
var myElement: HTMLDivElement;
var x = fn( myElement ); // x: string 

Second case: create unnesesary overloads

interface Example {
  diff(one: string): number;
  diff(one: string, two: string): number;
  diff(one: string,  two: string, three:boolean): number;
}

// Sol: Create one with option parameters
interface Example {
  diff(one: string, two?: string, three?: string): number;
}

Third case: create multiple lines, but at the end just return the same type (Moment in this case)

interface Moment {
  utcOffset(): number;
  utcOffset(b: number): Moment;
  utcOffset(b: string): Moment;
}

// solution: just use 1 union type 
interface Moment {
  utcOffset(): number;
  utcOffset(b: number | string): Moment;
}