TypeScript. סינון תכונות פונקציונליות ו-'לא ניתן לקרוא לביטוי שסוגו חסר חתימת קריאה.'
אני מיישם פונקציה call שמקבלת אובייקט ושם של נכס ואז מפעילה את הפונקציה שנמצאת באובייקט שהועבר בנכס שהועבר.
function call<
T,
K extends {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T],
>(t: T, func: K) {
t[func]();
}
נראה ש-ts מרוצה מזה ומסנן נכסים בצורה נכונה (אין prop, יש רק do):
אבל זה גם מציג שגיאה בשורה שבה אני מפעיל את הפונקציה: t[func]();:
Cannot invoke an expression whose type lacks a call signature.
Type '{}' has no compatible call signatures.
מצאתי בעיה דומה [Type deduction using mapped types and generics] אך היא נסגרה כתוספת של בעיה אחרת [Call signatures of union types]. ככל הנראה הבעיה היא שהפרמטר הגנרי השני הוא למעשה סוג איחוד של כל שמות המאפיינים שמספקים את התנאי.
האם יש דרכים אחרות לתקן זאת חוץ מההמרה ל-any?
נ.ב: מגרש משחקים
תשובה #1
טייפסקריפט לא יוכל להבין שהסינון שלך של התכונות אומר ש-T[K] תמיד יהיה פונקציה. אתה יכול להפוך מעט את ההגבלה שלך, כדי לציין ש-T מרחיב משהו שיש לו תכונות מסוג ()=>void
function call<
T extends { [P in K]: ()=> void },
K extends keyof T
>(t: T, func: K) {
t[func]();
}
class Test {
prop: string = "";
doStuff(): void {}
}
call(new Test(), "prop") // error
call(new Test(), "doStuff") // ok
ראוי לציין, עם זאת, שאולי כדאי לשקול שימוש בהצהרה (assertion) בפונקציה המקורית שלך, הגרסה למעלה, למרות שהיא נמנעת מהצהרות ובטוחה מבחינת טיפוסים, אינה עוזרת ל-IntelliSense להבין אילו ערכים אפשריים ניתן להקצות לפרמטר השני, אז במקום זאת:
אתה מקבל את זה:
בעוד שכשמחים להימנע מהצהרות בדרך כלל, במקרה זה אנו בטוחים שהסוג יהיה נכון בפועל כי יש לנו מגבלות על המפתח, כך שגרסה זו אמורה להיות בסדר גם כן:
function call2<
T,
K extends {
[K in keyof T]: T[K] extends ()=> void ? K : never
}[keyof T],
>(t: T, func: K) {
(t[func] as any as ()=> void)();
}


