typescript - Hvordan definere type for en funksjonscallback (som en hvilken som helst funksjonstype, ikke universell any) brukt som en metodeparameter
Akkurat nå har jeg typedefinisjon som:
interface Param {
title: string;
callback: any;
}
Jeg trenger noe som:
interface Param {
title: string;
callback: function;
}
men den andre blir ikke akseptert.
Svar #1
Den globale typen Function tjener dette formålet.
I tillegg, hvis du har til hensikt å påkalle denne callbacken med 0 argumenter og vil ignorere returverdien, passer typen () => void for alle funksjoner som ikke tar noen argumenter.
Svar #2
Typescript fra v1.4 har nøkkelordet type som erklærer et typealias (tilsvarende en typedef i C/C++). Du kan erklære callback-typen din slik:
type CallbackFunction = () => void;
som erklærer en funksjon som ikke tar noen argumenter og ikke returnerer noe. En funksjon som tar null eller flere argumenter av hvilken som helst type og ikke returnerer noe, ville være:
type CallbackFunctionVariadic = (...args: any[]) => void;
Da kan du for eksempel si,
let callback: CallbackFunctionVariadic = function(...args: any[]) {
// do some stuff
};
Hvis du vil ha en funksjon som tar et vilkårlig antall argumenter og returnerer hva som helst (inkludert void):
type CallbackFunctionVariadicAnyReturn = (...args: any[]) => any;
Du kan spesifisere noen obligatoriske argumenter og deretter et sett med ekstra argumenter (for eksempel en streng, et tall og deretter et sett med ekstra argumenter) slik:
type CallbackFunctionSomeVariadic =
(arg1: string, arg2: number, ...args: any[]) => void;
Dette kan være nyttig for ting som EventEmitter-håndterere.
Funksjoner kan typiseres så sterkt du vil på denne måten, selv om du kan bli revet med og støte på kombinatoriske problemer hvis du prøver å spikre alt med en typealias.
Svar #3
Som en oppfølging av Ryans svar, tror jeg at grensesnittet du ser etter er definert som følger:
interface Param {
title: string;
callback: () => void;
}
Svar #4
Du kan definere en funksjonstype i et grensesnitt på ulike måter,
- generell måte:
export interface IParam {
title: string;
callback(arg1: number, arg2: number): number;
}
- Hvis du ønsker å bruke egenskapssyntaks, så
export interface IParam {
title: string;
callback: (arg1: number, arg2: number) => number;
}
- Hvis du først erklærer funkjonstypen, så
type MyFnType = (arg1: number, arg2: number) => number;
export interface IParam {
title: string;
callback: MyFnType;
}
Bruk er veldig enkelt.
function callingFn(paramInfo: IParam):number {
let needToCall = true;
let result = 0;
if(needToCall){
result = paramInfo.callback(1,2);
}
return result;
}
- Du kan også erklære en funksjonstype-literal, som betyr at en funksjon kan akseptere en annen funksjon som sin parameter. En parameterisert funksjon kan også kalles som en tilbakekalling.
export interface IParam{
title: string;
callback(lateCallFn?:
(arg1:number,arg2:number)=>number):number;
}
Svar #5
Her er et eksempel på en funksjon som aksepterer en tilbakekalling
const sqk = (x: number, callback: ((_: number) => number)): number => {
// callback will receive a number and expected to return a number
return callback (x * x);
}
// here our callback will receive a number
sqk(5, function(x) {
console.log(x); // 25
return x; // we must return a number here
});
Hvis du ikke bryr deg om returverdiene til callback-funksjoner (de fleste vet ikke hvordan de skal bruke dem på en effektiv måte), kan du bruke void
const sqk = (x: number, callback: ((_: number) => void)): void => {
// callback will receive a number, we don't care what it returns
callback (x * x);
}
// here our callback will receive a number
sqk(5, function(x) {
console.log(x); // 25
// void
});
Merk, signaturen jeg brukte for callback-parameteren ...
const sqk = (x: number, callback: ((_: number) => number)): numberJeg vil si at dette er en TypeScript-mangel fordi vi forventes å oppgi et navn for tilbakemeldingsparametrene. I dette tilfellet brukte jeg _ fordi det ikke er brukbart inne i sqk-funksjonen.
Men, hvis du gjør dette
// danger!! don't do this
const sqk = (x: number, callback: ((number) => number)): numberDet er gyldig TypeScript, men det vil bli tolket som ...
// watch out! typescript will think it means ...
const sqk = (x: number, callback: ((number: any) => number)): numberIe, TypeScript vil tro at parameteren name er number og den implisitte typen er any. Dette er åpenbart ikke det vi hadde til hensikt, men ak, det er slik TypeScript fungerer.
Så ikke glem å oppgi parameterne når du skriver inn funksjonsparametrene dine... dumt som det kan virke.
Svar #6
Det finnes ulike måter å definere funksjonstyper på; likevel er noen bedre enn andre.
Selv om det er mulig å bruke Function, JavaScript-funksjonsobjektet, ikke gjør det.
Kilde: TypeScript ESLint-plugin anbefalt regel ban-types
Unngå Funksjon-typen, da den gir liten sikkerhet av følgende grunner:
- Det gir ingen typesikkerhet når verdien kalles, noe som betyr at det er lett å gi feil argumenter.
- Den aksepterer klassedeklarasjoner, som vil feile når de kalles, ettersom de kalles uten nøkkelordet new.
TypeScript støtter flere andre måter. For eksempel funksjonstypeuttrykk. Denne metoden ligner sterkt på pilfunksjoner.
Hvis argumenter og returverdier er kjent for å ha en bestemt form, bør de typeres. Dette bør være tilfelle for en callback.
For eksempel,
interface Param {
callback: (foo: string, bar: number) => void
}
Merk at disse typene kan være så komplekse som nødvendig, for eksempel ved å bruke objekttyper eller typer laget fra andre typer.
Hvis typene virkelig er ukjente, bør du foretrekke unknown fremfor any.
Kilde: TypeScript ESLint-plugin anbefalt regel no-explicit-any
Når
anybrukes, blir alle kompilatortypekontroller rundt den verdien ignorert.
Fra TS-dokumentasjonen,
unknowner den typesikre motparten tilany.
Dermed, ved å bruke spread-syntaks,
interface Param {
callback: (...args: unknown[]) => unknown
}
Svar #7
Det finnes fire abstrakte funksjonstyper, du kan bruke dem separat når du vet om funksjonen din vil ta et argument(er) eller ikke, og om den vil returnere data eller ikke.
export declare type fEmptyVoid = () => void;
export declare type fEmptyReturn = () => any;
export declare type fArgVoid = (...args: any[]) => void;
export declare type fArgReturn = (...args: any[]) => any;
som dette:
public isValid: fEmptyReturn = (): boolean => true;
public setStatus: fArgVoid = (status: boolean): void => this.status = status;
For bruk bare én type som hvilken som helst funksjonstype kan vi kombinere alle abstrakte typer sammen, slik:
export declare type fFunction = fEmptyVoid | fEmptyReturn | fArgVoid | fArgReturn;
bruk det da slik:
public isValid: fFunction = (): boolean => true;
public setStatus: fFunction = (status: boolean): void => this.status = status;
I eksemplet ovenfor er alt korrekt. Men brukseksemplet nedenfor er ikke korrekt sett fra synspunktet til de fleste kodeeditorer.
// you can call this function with any type of function as argument
public callArgument(callback: fFunction) {
// but you will get editor error if call callback argument like this
callback();
}
Riktig kall for redaktører er slik:
public callArgument(callback: fFunction) {
// pay attention in this part, for fix editor(s) error
(callback as fFunction)();
}
