Kako definirati funkciju koja prihvaća samo parametre koji mogu biti tip u TypeScriptu?
Želim definirati funkciju kao:
function isNumber(param: ???): param is number {
}
Ako parametar može biti broj, npr. number | string, number | boolean, može se proslijediti funkciji. Ako ne, npr. ako je njegov tip string ili boolean, doći će do grešaka pri kompajliranju.
Je li moguće definirati takav tip?
Odgovor #1
Konceptualno želite učiniti isNumber() generičkim u tipu T parametra param, ali želite da T bude donje ograničen tipom number. Ovo je suprotno uobičajenom extends ograničenju koje se smatra gornjom granicom. Nažalost, TypeScript izravno ne podržava generike s donjom granicom. To je zatraženo u microsoft/TypeScript#14520, i ako se ikada implementira, moglo bi koristiti super umjesto extends i izgledati kao
// NOT VALID TS (as of 4.7), DON'T DO THIS
function isNumber<T super number>(param: T): param is number {
return typeof param === "number"
}
Ali, kao što sam rekao, ne možemo to učiniti.
Umjesto toga, možemo donekle oponašati generike s donjom granicom korištenjem standardne gornje granice na uvjetnom tipu. Nešto poput T extends (U extends T ? unknown : never) ponašat će se donekle poput T super U. Ako je T supertip U, tada (U extends T ? unknown : never) evaluira u unknown, i tako je ograničenje (T extends unknown) zadovoljeno. U suprotnom, (U extends T ? unknown : never) evaluira u never, i tako ograničenje (T extends never) vjerojatno nije zadovoljeno (osim ako T sama nije never, što se na ovaj način ne može spriječiti).
U vašem slučaju zapravo smo u redu ako je T ili supertip ili podtip number, pa možemo napisati T extends (number extends T ? unknown : number) i na taj način, ako je T slučajno numerički literali tipa poput 14, sve će i dalje raditi.
Problem je u tome što kompajler ne razumije da je param vjerojatno nadtip number, pa ne možete vratiti tip predikata param is number. Umjesto toga, trebate napisati tip koji kompajler vidi kao dodijeljiv T (i number). Zato ćemo koristiti presjek param is T & number.
Eto ga:
function isNumber<T extends number extends T ? unknown : number>(
param: T
): param is number & T {
return typeof param === "number"
}
A hajde da to isprobamo:
isNumber("oops"); // error, Argument of type 'string' is
// not assignable to parameter of type 'number'.
isNumber(Math.random() < 0.5 ? "abc" : 123); // okay
isNumber(14); // okay
Izgleda dobro.
Poveznica na Playground za kod
