حاشیهنویسی توابع مرتبه بالا در تایپاسکریپت


من دارم با یک کدبیس نسبتاً بزرگ جاوا اسکریپت کار میکنم، سعی دارم آن را به تایپ اسکریپت منتقل کنم و با مشکلی در حاشیهنویسی یک تابع مرتبه بالاتر خاص برخورد کردهام...

doStuff() fn1 را به عنوان آرگومان میپذیرد و آن را میبندد و یک تابع جدید بازمیگرداند که تمام آرگومانهای fn1 به جز اولین آرگومان را میپذیرد. چیزی شبیه این:

const fn1 = (arg_1, arg_2, arg_3, arg_n) => { return 'Hello World' }

const doStuff = (fn) => (...args) => {
    argument1 = getSomeStuffHere()
    return fn(argument1, ...args)
}

const test = doStuff(fn1)
let result = test('arg2', 'arg3', 'arg4')

قابل ذکر است که یک doStuff() وجود دارد و تعداد زیادی تابع fnX() با تعداد مختلفی از آرگومانها و ترکیبهای نوع مختلف که توسط آن بستهبندی میشوند. مهم است که توابع ایجاد شده توسط doStuff به درستی تایپ شده باشند، "any => any" کافی نخواهد بود!

بعد از کلی سر و کله زدن، بالاخره به این نتیجه رسیدم:

// just for testing simplified example
type MagicObj = {}
const myMagicObject = {}

type Wrapped<T, R> =
    T extends [MagicObj, any, any, any, any] ? (a: T[1], b: T[2], c: T[3], d: T[4]) => R :
    T extends [MagicObj, any, any, any] ? (a: T[1], b: T[2], c: T[3]) => R :
    T extends [MagicObj, any, any] ? (a: T[1], b: T[2]) => R :
    T extends [MagicObj, any] ? (a: T[1]) => R :
    T extends [MagicObj] ? () => R :
    unknown;

const doStuff = <T extends any[], R>(fn: (...args: T) => R): Wrapped<T, R> => (...args) => fn(myMagicObject, ...args)

// testing examples
const fn1 = (obj: MagicObj, p1: string, p2: boolean, p3: number): string => { return 'Hello World' }
const fn2 = (obj: MagicObj, p1: number, p2: string) => { return 'Hello Mars' }
const fn3 = (obj: MagicObj, p1: boolean) => { return 'Hello The Moon' }

const test1 = doStuff(fn1)
// const test1: (a: string, b: boolean, c: number) => string 
let result1 = test1('str', true, 123) 

const test2 = doStuff(fn2)
// const test2: (a: number, b: string) => string
let result2 = test2(123, 'str')

const test3 = doStuff(fn3)
// const test3: (a: boolean) => string
let result3 = test3(true)

این به نظر میرسد که نوعی کار میکند. جادوگری intellisense با نوعدهی VSCodes به من همان چیزی را نشان میدهد که برای متغیرهای test1، 2، 3 و result در پایین مثال انتظار داشتم/میخواستم، اما تابعی که توسط doStuff (...args) => fn(myMagicObject, ...args) بازگردانده میشود، مهم نیست که چگونه سعی کنم آن را حاشیهنویسی کنم، خطایی شبیه به Type '(...args: any[]) => any' is not assignable to type 'Wrapped<T, R>'.ts(2322) گزارش میدهد

ایدهای داری که چطور میتوان این کار را انجام داد؟

پاسخ شماره ۱

اگر متوجه شوم که شما چه کاری میخواهید انجام دهید، من doStuff() را به این شکل تایپ میکنم:

const doStuff = <T extends any[], R>(
  fn: (magicObject: MagicObj, ...args: T) => R
): ((...args: T) => R) => (...args) => fn(myMagicObject, ...args);

این نمایانگر یک تبدیل از هر تابعی است که اولین آرگومان آن از نوع MagicObj است به تابع دیگری از همان نوع اما با حذف آن پارامتر MagicObj. این باید برای هر تعداد پارامتر کار کند، با استفاده از تاپلهای باقیمانده.

این برای شما مناسب است؟ خوب، امیدوارم که کمک کند. موفق باشید!

لینک به کد