ค้นหาเว็บไซต์

javascript - กำหนดฟังก์ชันเพื่อรับชื่อเมธอดของสตริงและพารามิเตอร์ที่มีชนิดเหมาะสม แล้วเรียกใช้เมธอดนั้น


ฉันกำลังมองหาการนิยามฟังก์ชันที่:

  1. ใช้คีย์ของโปรโตไทป์สตริงเป็นอาร์กิวเมนต์ ซึ่งชี้ไปที่ฟังก์ชัน
  2. ยังรับอาร์กิวเมนต์ที่จะใช้ในการเรียกฟังก์ชันดังกล่าว
  3. จากนั้น การรวบรวมทุกอย่างเข้าด้วยกัน จะเรียกใช้เมธอดโดยใช้ตัวอาร์กิวเมนต์ที่กำหนด

นี่ฟังดูเรียบง่ายพอสมควร และใน Javascript ธรรมดา สามารถนำไปใช้งานได้ง่ายมาก:

function callFunction(key, param) {
    const myImportantString = "password123"
    return myImportantString[key](param);
}

callFunction('slice', 8) // => '123'

สิ่งที่ฉันกำลังมีปัญหาคือการพยายามนำสิ่งนี้ไปใช้งานใน Typescript ฉันมีฟังก์ชันประเภทที่ดูเหมือนจะทำงานได้เมื่อลองแยกใช้ แต่ Typescript ดูเหมือนจะ ‘สับสน’ เมื่อรวมทั้งหมดเข้าด้วยกัน

นี่คือผู้ช่วยประเภทของฉัน ซึ่งฉันทดสอบแต่ละอันแล้วและไม่พบปัญหาใดๆ

// yields a union of all keys that point to a function that takes one argument
type Callable<R> = keyof {
    [K in keyof R]: R[K] extends ((arg: any) => any) ? K : never
}[keyof R];

// yields the type of the first argument expected by a function
type FirstArgument<T> = T extends ((arg: infer U) => any) ? U : never;

// yields the return type of a function
type ReturnType<T> = T extends (...args: any) => (infer U) ? U : never;

ด้วยสิ่งเหล่านี้ ดูเหมือนว่าจะสามารถกำหนดฟังก์ชันของฉันได้ดังนี้:

function callFunction<T extends Callable<String>>(key: T, param: FirstArgument<String[T]>): ReturnType<String[T]> {
    const importantString = 'password123';
    return importantString[key](param);
}

อย่างไรก็ตาม Typescript จะแจ้งข้อผิดพลาดภายใต้ importantString[key( ว่า:

นิพจน์นี้ไม่สามารถเรียกใช้งานได้ สมาชิกแต่ละตัวของชนิดสหภาพ '(() => IterableIterator) | ... 40 ตัวอื่น ๆ ... | ((index: number) => string | undefined)' มีลายเซ็น แต่ไม่มีลายเซ็นใดที่เข้ากันได้กับลายเซ็นอื่น ๆ

ที่น่าสนใจ คือ การแทนที่ String ด้วยออบเจ็กต์ทั่วไปและ importantString ด้วยพารามิเตอร์ดูเหมือนว่าจะใช้งานได้ ดังนี้:

function callFunction<Q extends {[key: string]: any}, T extends Callable<Q>>(object: Q, key: T, param: FirstArgument<Q[T]>): ReturnType<Q[T]> {
    return object[key](param);
}

// note the 'as String' needed to prevent `Argument of type 'string' is not assignable to parameter of type '{ [key: string]: any; }'.`
callFunction('password123' as String, 'slice', 8)

อย่างไรก็ตาม น่าสนใจที่การพยายามทำให้มัน ไม่ ทั่วไปต่างหากที่ทำให้มันพัง เหมือนตัวอย่างของฉันข้างต้น

นี่ดูเหมือนจะเป็นงานที่ค่อนข้างง่าย แต่ฉันก็ไม่รู้ว่าจะเริ่มยังไง คำแนะนำใด ๆ จะเป็นที่ชื่นชม