تایپاسکریپت: تنها تایپ کردن آرگومانها یا نوع بازگشتی یک تابع


من چندین تابع دارم که آرگومانهای مختلفی میپذیرند، اما همگی یک تابع با همان امضا را برمیگردانند:

const createSum5 = () => (c: number) => c + 5;
const createMultiplyN = (n: number) => (c: number) => n * c;
const createWordsSum = (word: string) => (c: number) => word.length + c;

نوع بازگشتی همیشه (c: number) => number است. آیا راهی وجود دارد که بتوانم این مقدار بازگشتی را برای همه توابع تایپ کنم، بدون اینکه آرگومان را تایپ کنم؟ به طوری که بتوانم آنها را به این شکل کوتاه کنم بدون از دست دادن ایمنی نوع:

createSum5: /* Something here */ = () => c => c + 5;

من اینها را امتحان کردهام، اما:

1. این راهحل نوع ایمنی را از دست میدهد: type NumericArg=(...args: any) => (c: number) => number;

۲. میتوانم پیشاپیش امضای تابع هر تابع را بنویسم:

type Nfunction = (c: number) => number;
type createSum5Type = () => Nfunction;
...
const createSum5: createSum5Type = () => c => c + 5;

اما این خستهکننده خواهد بود. من میخواهم تایپاسکریپت بهطور خودکار آرگومانها را استنتاج کند، همانطور که اگر نوعها را مشخص نکنم، انجام میدهد.

راه دیگری وجود دارد؟ آیا میتوان کار معکوس را انجام داد (تابعهایی با همان امضا مشخص کرد اما اجازه داد تایپاسکریپت نوع بازگشتی را استنتاج کند؟)

ویرایش شماره ۱: این یک مثال است وقتی که من دربارهی عکس آن صحبت میکنم:

const func1: /* Something here */ = num => '#'.repeat(5);
const func2: /* Something here */ = num => num * num;

هر دو تابع func1 و func2 دارای امضای یکسان (num: number) هستند اما نوع بازگشتی متفاوتی دارند. من میخواهم تایپاسکریپت استنباط کند که هر بار که func1 را فرا میخوانم، نوع بازگشتی آن یک string است، اما با func2 نوع بازگشتی یک عدد است.

ویرایش #2:

مورد استفاده من مرتبط نیست، اما به هر حال آن را اضافه میکنم. من در حال توسعه یک برنامه React-redux با استفاده از Thunks هستم. هر thunk همیشه یک تابع با امضای (dispatch, getState) => void برمیگرداند اما ممکن است پارامترهای مختلفی را بپذیرد. پس از کمی تحقیق، این نسخه کمحرفترین نسخهای است که توانستم پیدا کنم:

const onFetchUser=(userIds: number[]) : ThunkFunction=dispatch => { ... }.

اگر راهی وجود داشت که تایپاسکریپت بتواند آرگومانها را استنباط کند اما اجازه دهد که نوع بازگشتی را تعیین کنم (که سوال من اینجاست)، میتوانستم آن را به این صورت راحتتر بخوانم:

const onFetchUser: /* چیزی اینجا */=(userIds: number[]) => dispatch => {...}.

پاسخ شماره ۱

مطمئن نیستم که این تنها گزینه باشد، اما یک گزینه این است که از یک تابع کمکی استفاده کنید که یک پارامتر عمومی دارد که نوع واقعی تابعی که منتقل میشود را دریافت کند اما همچنین نوع بازگشتی (c: number)=> number را اعمال کند.

function fn<T extends (...a: any[]) => (c: number) => number>(o: T) {
  return o;
}
const createSum5 = fn(() => c => c + 5)
const createMultiplyN = fn((n: number) => c => n * c);
const createWordsSum = fn((word: string) => c => word.length + c);

من باور ندارم گزینهی دیگری وجود داشته باشد، تایپاسکریپت به طور کلی اجازهی استنتاج جزئی برای متغیرها را نمیدهد (یا به طور مشخصتر، استنتاج محدود شده)، این فقط با یک تابع میتواند انجام شود.

پاسخ شماره ۲

تایپاسکریپت از کلیدواژه infer پشتیبانی میکند، که به شما امکان میدهد انواع آرگومانها و/یا نوع بازگشتی تابع را حفظ کنید.

برای نوعهای شرطی، به این صورت خواهد بود:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

اطلاعاتی درباره infer در اینجا وجود دارد: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html

بهروزرسانی:

شما میتوانید این کار را فقط با استفاده از یک تابع عمومی انجام دهید:

type numFunc<T> = (arg: T) => (c: number) => number; 

const createSum5: numFunc<void> = () => (c: number) => c + 5;
const createMultiplyN: numFunc<number> = (n: number) => (c: number) => n * c;
const createWordsSum: numFunc<string> = (word: string) => (c: number) => word.length + c;

const createSumString: numFunc<number> = () => (c: number) => 'Hello';  //error