Čudno ponašanje preopterećenja funkcija u Typescriptu
Pokušavam naučiti kako funkcijski overloadi rade u Typescriptu, ali ne razumijem. Pogledaj ovaj isječak (ts playground) (koristeći samo jednu deklaraciju da bi bilo što jednostavnije):
function foo(x: number): 42
function foo(x: number) {
return x
}
const y = foo(23)
Dvoje stvari nije u redu:
- nema TS pogreške na deklaraciji funkcije, čak i kada stvarni tip povratne vrijednosti jasno ne odgovara potpisu preopterećenja
- pretpostavljena vrijednost
yje42
Ako umjesto toga samo deklariram tip povratne vrijednosti u implementaciji funkcije, stvari rade (ili ne rade!) kako se očekuje:
function foo(x: number): 42 {
return x // -> Type 'number' is not assignable to type '42'.(2322)
}
const y = foo(4)
Odgovor #1
Hajdemo se pozabaviti prvim problemom:
nema TS pogreške na deklaraciji funkcije, čak i kada stvarni tip povratne vrijednosti jasno ne odgovara potpisu preopterećenja
Ako pogledate ovaj dokument (koji je od tada zastario, ali pretpostavljam da i dalje radi isto), oni kažu:
Kako bi prevoditelj odabrao ispravnu provjeru tipa, slijedi sličan proces kao i osnovni JavaScript. Gleda popis preopterećenja i, počevši od prvog preopterećenja, pokušava pozvati funkciju s danim parametrima. Ako pronađe podudaranje, odabire to preopterećenje kao ispravno preopterećenje. Iz tog razloga, uobičajeno je redoslijediti preopterećenja od najspecifičnijeg do najmanje specifičnog.
Moje je nagađanje da je isto kada pišete implementaciju, za svaki preopterećeni oblik koji deklarirate, pokušat će pozvati vašu funkciju s njim. Ne znam ima li to smisla. Pogledajmo primjer:
function foo(x: number): 42 // ok since 42 is compatible with number (number is wider than 42)
function foo(x: number) {
return x
}
const y = foo(23)
Ako uzmete potpis i pokušate pozvati svoju funkciju s njim, on je 'kompatibilan'. x je broj i 42 je također broj, a povratni tip naše implementacije kaže da je x broj.
Ako ovo učinim:
function foo(x: number): 42 // error: not compatible since 42 is not compatible with 32
function foo(x: number) {
return 32
}
Dobivam pogrešku. Još jednu za cestu:
function foo(x: number): 'text1'
function foo(x: string): 'text2'
function foo(x: number | string) {
return x // why is there no error?
}
//y is "text1" (no, it's a number!!!)
const y = foo(42)
//there is no error because if you take the signature, you can call your function with it -> x is a number so compatible with number | string and 'text1' is compatible with number | string
Pa zašto je to tako, možda se pitate? Ako to pogledate na sljedeći način, bit će lakše razmisliti o tome.
Ne
Mogu li pozvati svoj potpis s implementacijom svoje funkcije?
umjesto obrnuto
Ako uzmem jedan od mojih potpisa, mogu li s njim pozvati implementaciju svoje funkcije?
Nemojte gledati preopterećenja kao neku vrstu zahtjeva koje implementacija funkcije mora ispuniti. Jeste li primijetili da su pogreške na preopterećenjima, a ne na implementaciji funkcije? To je preopterećenje koje mora biti kompatibilno s implementacijom funkcije.
Druga točka:
izvedena vrijednost y je 42
Ako pređete mišem preko implementacije vaše funkcije, vidjet ćete da ona samo prikazuje prelijevanja.
Najnovija verzija dokumenta kaže:
Potpis implementacije nije vidljiv izvana. Prilikom pisanja preopterećene funkcije, uvijek biste trebali imati dva ili više potpisa iznad implementacije funkcije.
Oni navode uobičajenu pogrešku koju ljudi čine:
function fn(x: string): void;
function fn() {
// ...
}
// Expected to be able to call with zero arguments
fn();
Expected 1 arguments, but got 0.
Sama implementacija se ne računa kao preopterećenje.
Dakle, pri pozivanju funkcije vidi se samo preopterećenje:
const y = foo(23)
Dakle, imajući ovo na umu, koristite te preopterećenja mudro. Ostalo je vaša odgovornost. Usput, u vašem slučaju uporabe uopće ne trebate koristiti preopterećenja. Trebali biste ih koristiti samo kada imate barem dva od njih.
