האם פונקציות עם טיפוס חזק כפרמטרים אפשריות ב-TypeScript?
ב-TypeScript, אני יכול להכריז על פרמטר של פונקציה כטיפוס Function. האם יש דרך "בטוחה מבחינת טיפוס" לעשות זאת שאני מפספס? לדוגמה, שקול את זה:
class Foo {
save(callback: Function) : void {
//Do the save
var result : number = 42; //We get a number from the save operation
//Can I at compile-time ensure the callback accepts a single parameter of type number somehow?
callback(result);
}
}
var foo = new Foo();
var callback = (result: string) : void => {
alert(result);
}
foo.save(callback);
הקריאה חוזרת של השמירה אינה בטוחה טיפוסית, אני נותן לה פונקציית קריאה חוזרת שבה הפרמטר של הפונקציה הוא מחרוזת אבל אני מעביר לה מספר, וזה מתרגם ללא שגיאות. האם אני יכול להפוך את פרמטר התוצאה בשמירה לפונקציה בטוחה טיפוסית?
גרסה מקוצרת: האם קיים מקביל ל- delegate של .NET ב-TypeScript?
תשובה #1
בטח. סוג של פונקציה מורכב מסוגי הפרמטרים שלה וסוג הערך שהיא מחזירה. כאן אנחנו מבהירים שסוג הפרמטר callback חייב להיות "פונקציה שמקבלת מספר ומחזירה סוג any".
class Foo {
save(callback: (n: number) => any) : void {
callback(42);
}
}
var foo = new Foo();
var strCallback = (result: string) : void => {
alert(result);
}
var numCallback = (result: number) : void => {
alert(result.toString());
}
foo.save(strCallback); // not OK
foo.save(numCallback); // OK
אם אתה רוצה, אתה יכול להגדיר כינוי סוג כדי לכלול את זה:
type NumberCallback = (n: number) => any;
class Foo {
// Equivalent
save(callback: NumberCallback) : void {
callback(42);
}
}
תשובה #2
להלן מקבילים ב-TypeScript של כמה נציגים נפוצים ב-.NET:
interface Action<T>
{
(item: T): void;
}
interface Func<T,TResult>
{
(item: T): TResult;
}
תשובה #3
type FunctionName = (n: inputType) => any;
class ClassName {
save(callback: FunctionName) : void {
callback(data);
}
}
זה בוודאי מתאים לפרדיגמת התכנות הפונקציונלי.
תשובה #4
אני מבין שהפוסט הזה ישן, אבל יש גישה קומפקטית יותר שהיא מעט שונה ממה שהתבקש, אך עשויה להיות חלופה מועילה מאוד. ניתן למעשה להכריז על הפונקציה בתוך השורה כאשר קוראים למתודה (במקרה זה ל-save() של Foo). זה ייראה משהו כזה:
class Foo {
save(callback: (n: number) => any) : void {
callback(42)
}
multipleCallbacks(firstCallback: (s: string) => void, secondCallback: (b: boolean) => boolean): void {
firstCallback("hello world")
let result: boolean = secondCallback(true)
console.log("Resulting boolean: " + result)
}
}
var foo = new Foo()
// Single callback example.
// Just like with @RyanCavanaugh's approach, ensure the parameter(s) and return
// types match the declared types above in the `save()` method definition.
foo.save((newNumber: number) => {
console.log("Some number: " + newNumber)
// This is optional, since "any" is the declared return type.
return newNumber
})
// Multiple callbacks example.
// Each call is on a separate line for clarity.
// Note that `firstCallback()` has a void return type, while the second is boolean.
foo.multipleCallbacks(
(s: string) => {
console.log("Some string: " + s)
},
(b: boolean) => {
console.log("Some boolean: " + b)
let result = b && false
return result
}
)
השיטה multipleCallback() שימושית מאוד לדברים כמו קריאות רשת שעשויות להצליח או להיכשל. שוב בהנחה של דוגמת קריאת רשת, כאשר multipleCallbacks() נקראת, ניתן להגדיר התנהגות גם להצלחה וגם לכישלון במקום אחד, מה שתורם להבנה טובה יותר של הקוד בעתיד עבור הקוראים שלו.
באופן כללי, לפי ניסיוני, הגישה הזו נוטה להיות תמציתית יותר, פחות מבולגנת, ובהירה יותר בכללי.
בהצלחה לכולם!
