typescript - آیا روشی وجود دارد که بتوانم نوع بازگشتی تابع ژنراتور را دریافت کنم؟


من میخواهم نوع بازگشتی تابع ژنراتور را بدست آورم.
برای مثال

function* test(){
  yield 'a';
  yield 123;

  return true;
}

// what I'm trying to get
type A = GetGeneratorReturn<test>; // -> A has type boolean

جایی که اکنون هستم.

type B = ReturnType<ReturnType<typeof test>['next']>['value'];
// B has type `true | "a" | 123`

(من فکر کردم اگر اولین آیتم از نوع Union B را بگیرم، میتوانم نوع بازگشتی را به دست آورم)

پاسخ شماره ۱

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

فعلاً به نظر میرسد که تایپهای ژنراتور در تایپاسکریپت تنها به شما دسترسی به اتحاد غیرتفکیکشدهای از نوعهای yield و نوع return میدهند. و متأسفانه، نمیتوانید به ترتیب یک نوع اتحاد تکیه کنید. این یک جزئیات پیادهسازی کامپایلر است؛ از آنجا که X | Y معادل Y | X است، کامپایلر آزاد است که یکی را به دیگری تغییر دهد. و گاهی هم این کار را انجام میدهد:

function* test() {
  yield 'a';
  yield 123;
  return true;
} // function test(): IterableIterator<true | "a" | 123>

function* oops() {
  yield true;
  yield 123;
  return 'a'
} // function oops(): IterableIterator<true | "a" | 123>

نوع بازگشتی oops() دقیقاً همان نوع بازگشتی test() است. این بدان معناست که ایده شما برای استخراج عنصر اول/آخر یک اتحادیه امکانپذیر نیست.

ممکن است راهی وجود داشته باشد که کمک کند، به ویژه اگر بتوانید با تعریف ژنراتور بازی کنید و مشکلی با وجود برخی نوعهای شبحی نداشته باشید:

type Returnable<T> = { __returnedType?: T } & T
const ret = <T>(t: T) => t as Returnable<T>;
type GeneratorReturn<T extends (...args: any) => IterableIterator<any>> =
  ReturnType<T> extends IterableIterator<infer I> ?
  NonNullable<Extract<I, Returnable<unknown>>['__returnedType']> : never

function* test() {
  yield 'a';
  yield 123;
  return ret(true); // only use ret() here
} 

function* oops() {
  yield true;
  yield 123;
  return ret('a'); // only use ret() here
} 

type TestRet = GeneratorReturn<typeof test>; // true
type OopsRet = GeneratorReturn<typeof oops>; // "a"

که تا حدی کار میکند، اما ممکن است چیزی نباشد که بخواهید دنبال کنید. خب، متأسفم که نمیتوانم جواب بهتری پیدا کنم. موفق باشید!

پاسخ شماره ۲

شما اکنون میتوانید به این شکل عمل کنید:

type GeneratorReturnType<T extends Generator> = T extends Generator<any, infer R, any> ? R: never;
type TestType =  GeneratorReturnType<ReturnType<typeof test>>; // bool

زمین بازی