[ Conteúdo ] Functions overload { TypeScript }
Introdução
Hoje venho falar sobre um tópico que pode ser muito confuso para quem tá começando, ou se você é mais experiente e só deu um "skip" para um próximo assunto pois não entendeu este aqui. Functions overload é uma possibilidade muito interessante e fléxivel com vantagens e desvantagens no TypeScript
Observações: Estarei falando especificamente do TypeScript e apenas. Se existir uma forma semelhante, ou este tópico esteja presente em outros linguagens, mas com abordagens diferentes, eu não entrarei a fundo. Cobrirei o tópico apenas e unicamente sobre o TypeScript. Sem mais delongas.
O que é Function overload?
É difícil definir isso, pois é algo que se entende mais fazendo, do que falando. Mas vamos lá, darei minha própria definição sobre: Functions Overloads são uma técnica que nos permite reaproveitar funções de uma maneira específica, porém, com suas flexibilidades.
Mas o que é exatamente essa técnica que reaproveita as funções? Primeiro, vou deixar alguns pontos importantes: De fato, reaproveita uma mesma função, entretanto, pode ser mais argumentativo, ou até mais lento. Segundo: É uma técnica que é mais como uma alternativa, então, provavelmente deve ter algo ou uma forma semelhante ou melhor de fazer isso. Algumas vezes, pode ficar a seu critério.
Sintaxe:
A sintaxe é a mesma do dia a dia, nada de muito novo. Irei da um exemplo, porém, não sinta-se na necessidade de entender em primeiro instância:
function teste(n: number): number;
function teste(n: string, x:string, z:string): string;
function teste(n: number | string, x?:string, z?:string):number | string {
if(typeof n === 'string' && typeof x === "string" && typeof z === "string") {
console.log("it's a string!")
return "my String Returned!"
} else {
console.log("it's a number!")
return "My number returned!"
}
}
teste(10)
teste("string1", "string2", "string3")
Se você tem um bom olho, notou que as três funções tem o mesmo nome, mas, como assim? Isso não deveria estar errado? Se fosse em javaScript, estaria errado, provavelmente (não testei). Se colar o código no playground do TypeScript neste link aqui: Playground vai ver que não tem nenhum erro.
vou abstrair todo esse código, que eu mesmo criei justamente para explicar neste artigo.
Primeiro de tudo, vamos começar olhando para a primeira linha do código.
function teste(n: number): number;
Nós temos uma função com o nome "teste" que tem um único argumento do tipo number
e que retorna number
também.
Já na segunda linha temos algo parecido:
function teste(n: string, x:string, z:string): string;
Uma função com o mesmo nome com três argumentos cujo tem todos o tipo string
, que retorna uma string
.
A parte boa começa aqui:
function teste(n: number | string, x?:string, z?:string):number | string {
if(typeof n === 'string' && typeof x === "string" && typeof z === "string") {
console.log("it's a string!")
return "my String Returned!"
} else {
console.log("it's a number!")
return "My number returned!"
}
}
Uma função com o mesmo nome, só que com diferenças. Primeiro a função tem uma lógica dentro do bloco, diferente das outras, e tem um unio
de retorna para uma string
e number
.
Ok! Mas eu não entendi ainda o que é tudo isso! Eu fiz isso, para você olhar o código por parte, e conseguir visualizar melhor o código, ao invés de ver como um todo e tomar um susto.
As duas primeira linhas do código, são functions overload
e a terceira linha é uma function signature
. Todas as três são intimamente relacionadas.
A primeira função basicamente fala para o typeScript: "Olha, a função "teste" pode receber um único paramêtro do tipo number
e que retorne number
também, se não for assim, lançará um erro."
Já a segunda função diz para o TypeScript: "Meu chapa, além da possibilidade de cima, considere que a função teste pode também receber três paramêtros do tipo string
e que retorne uma string
. Se por acaso for diferente desde padrão, retorne um erro caso não se encaixe no meu padrão, ou no padrão de cima"
Já a terceira função signature, é como o carinha que vai implementar a lógica, com base no que as funções pedem.
Tá difícil ainda de entender? Então vamos analisar o código mais uma vez:
function teste(n: number): number;
function teste(n: string, x:string, z:string): string;
function teste(n: number | string, x?:string, z?:string):number | string {
if(typeof n === 'string' && typeof x === "string" && typeof z === "string") {
console.log("it's a string!")
return "my String Returned!"
} else {
console.log("it's a number!")
return "My number returned!"
}
}
teste(10)
teste("string1", "string2", "string3")
Vamos focar na função signature, que á terceira função. Ela é responsável, como eu disse acima, por fazer as coisas acontecerem.
Observe que o primeiro paramêtro n
é declaro como sendo number
e string
nas funções de overload. Na function signature, para o TypeScript não reclamar, fazemos a únião desde tipos afirmando para o TypeScript que o valor do paramêtro ou é do tipo number
ou do tipo string
.
Se você tem dúvidas sobre o que é união de tipos, então talvez este seja um tópico muito avançando para você, vã na documentação e procure por
every types
,Narrowing
e também se encontra presente em diversas outras partes da documentação.
Entendemos o que ocorre no primeiro paramêtro, então não é díficil de entender os outros dois que são x
e z
, que se olhar bem, são paramêtros opicionais. Esses dois são paramêtros opcionais por porquê na primeira funções overload, só tem um argumento presente, o que não é a mesma coisa pra a segunda função que possui os três argumentos, sacou?
Resumido, os paramêtros são opcionais, pois no primeiro caso eu tenho apenas 1 e não 3 paramêtros. Se eu não colosse os paramêtros como opcionais, então quando eu quisesse que a função caisse na primeira possibilidade, iria pedir 3 paramêtro, e iria lançar um typeErro pois foi determinado que deveria ser apenas 1.
A função pode retorna number
ou string
, dependendo do caso. Isso porque a primeira função overload tem um retorno do tipo number
, e a segunda do tipo string
. Se você já tem uma experiência com TypeScript, sabe que vai da um erro, caso não especique bem o retorno.
Dentro do bloco do código tem uma lógica que vai ser executada com base no valor recebido nos paramêtros:
function teste(n: number | string, x?:string, z?:string):number | string {
if(typeof n === 'string' && typeof x === "string" && typeof z === "string") {
console.log("it's a string!")
return "my String Returned!"
} else {
console.log("it's a number!")
return "My number returned!"
}
}
Aqui o TypeScript entende que a condicional é um
Typeguard
que basicamente, tenta saber o valor de retorno com precisão, para mais detalhes consulte a documentação na parte deNarrowing
Agora, que já expliquei tudo detalhadamente, já ficou bem longo inclusive, vamos para a conclusão:
function teste(n: number): number;
function teste(n: string, x:string, z:string): string;
function teste(n: number | string, x?:string, z?:string):number | string {
if(typeof n === 'string' && typeof x === "string" && typeof z === "string") {
console.log("it's a string!")
return "my String Returned!"
} else {
console.log("it's a number!")
return "My number returned!"
}
}
teste(10)
teste("string1", "string2", "string3")
Quando a gente chama a função teste(10)
, vai cair na primeira possibilidade, ou seja, o TypeScrip vai entender que você tá querendo essa posibilidade aqui:
function teste(n: number): number;
Depois que chamar a função com um argumento do tipo number, vai executar o bloco de código e vai cair na segunda condição, do else
.
O mesmo se aplica para a chamada de: teste("string1", "string2", "string3")
que tem 3 argumento, todos do tipo string, e cai exatamente na segunda função de overload:
function teste(n: string, x:string, z:string): string;
Notou que é basicamente, chamar a função com os argumento correspondentes? O papel das funções de overload é "configurar" ou melhor, "especificar" quais são os possíveis argumentos que a função pode receber. Lembrano que podem ter muitos outros tipoes de funções overloads e mais de duas funções. Só não esquece de Typar tudo certinho.
Há, se vocè tentar fazer isso aqui: teste("teste")
vai resultar em um erro, pois não tem nenhu function overload que recebe um único paramêtro do tipo strig. Se tentar fazer isso aqui: teste("string1", "string2")
também vai da erro pois não tem nenhuma função que recebe apenas dois argumentos do tipo string
. Só existe funções que recebe um único paramêtro do tipo number
, ou recebe três argumentos do tipo string
, apenas.
Conclusão:
Alguma dúvida pessoal? Comentem!