Poznajemy to i owo dotyczące funkcji w TS. Bez zbędnych wstępów, zaczynajmy.
Ok, najpierw upewnijmy się, że pamiętamy składnię pisania funkcji w TS:
function first<T>(arr: T[]){
return arr[0];
}
const last = <T>(arr: T[]) => {
return arr[arr.length - 1]
}
Ok, jeszcze jedna – generyczna strzałkowa z return type:
const makeArr = <T>(el: T): [T] => {
return [el];
}
Może wydawać się oczywiste dla kogoś, kto dłużej koduje, ale prawda jest taka, że bez dobrego zrozumienia składni, samej składni, nigdy do niczego nie dojdziemy.
Ok, przykład z internetu (taki sobie) na interfejs dla argumentów i return type:
interface Args {
name: string;
age: number;
}
interface Return {
name: string;
age: number;
doubledAge: number
}
function ageDoubler({name, age}: Args): Return {
return {
name,
age,
doubledAge: age * 2,
}
}
Natomiast argsy rzadko kiedy są obiektem. Zazwyczaj to tablica. Musimy też wiedzieć, że funkcja może mieć własny interfejs. Tak!
Przykład z dokumentacji:
interface SearchFunc {
(source: string, subString: string): boolean;
}
A teraz implementacja:
let mySearch: SearchFunc;
mySearch = function (source: string, subString: string): boolean {
let result = source.search(subString);
return result > -1;
};
A teraz coś mega – przykład interfejsu funkcji, która poza swoim ciałem ma jeszcze jakieś dodatkowe pola:
interface FuncWithDescription {
(...args: any[]): any;
description: string
}
Tak to wygląda:
const sum123: FuncWithDescription = function(a, b) {
return a + b
}
sum123.description = 'A function that sums two numbers'
console.log(sum123.description);
Nie da się tego przypisać za jednym razem, wszystkiego, ale musi być i ciało funkcji i description. Bez description będziemy mieli błąd. Nie tylko przy próbie wypisywania description.
Takie coś (wyobraźmy sobie czerwone podkreślenie pod sum123):
const sum123: FuncWithDescription = function(a, b) {
return a + b
}
// sum123.description = 'A function that sums two numbers'
// console.log(sum123.description);
Zaowocuje nam błędem: Property 'description’ is missing in type '(a: any, b: any) => any’ but required in type 'FuncWithDescription’.
Mamy Paremeters, utility type, który podaje nam parametry funkcji:
function handleRequest(url: string, method: 'GET' |'POST'){
}
let req2: Parameters<typeof handleRequest> = ["www.google.com", 'GET'];
handleRequest(...req2);
Wyjaśnialiśmy to dogłębnie w poprzedniej lekcji oraz dlaczego to jest potrzebne. Mamy też kolejny utility type, jakim jest ReturnType:
function getObj(){
return {name: "John", age: 30 }
};
let retType: ReturnType<typeof getObj> = {name: "Jane", age: 20};
On nam zwraca typ, jaki zwraca dana funkcja, typ zwracany przez tę funkcję.
Warto jeszcze wspomnieć, że do tworzenia typów funkcji nie musimy używać interfejsów. Mogą to być typy, słówko kluczowe type:
type voidFunc = () => void;
const f1: voidFunc = () => {
return true;
};
Interfejs pasuje, jak już wspominaliśmy, gdy ta funkcja ma mieć jeszcze jakieś ekstra pola:
interface FuncWithDescription {
(...args: any[]): any;
description: string
}
Natomiast to cały czas jest interfejs dla funkcji, co wnioskujemy po tym, że tam nie ma nazwy metody:
interface FuncWithDescription {
(...args: any[]): any;
description: string
}
let objWithDescription : FuncWithDescription = {
description: 'myObj',
sum123(a,b) { return a+b} //error
//Object literal may only specify known properties,
//and 'sum123' does not exist in type 'FuncWithDescription'
}
Dla obiektu z metodami jest składnia podobna, tylko metody są nazwane:
interface ObjectWithMethod {
name: string;
sayHi(): void;
};
let myObjWithMethod: ObjectWithMethod = {
name: 'Jane',
sayHi(){
console.log("hi");
}
}
Postarajmy się dobrze zrozumieć składnię, bo bez tego ani rusz…