بررسی نوع - تایپاسکریپت در بررسی شیءهای تو در تو در نوع بازگشتی تابع شکست میخورد


تایپ اسکریپت در بررسی نوع بازگشتی اگر یک شیء تو در تو باشد شکست میخورد. کد زیر را در نظر بگیرید، تایپ اسکریپت حتی اگر متغیر result در تابع A به وضوح با نوع Output مطابقت نداشته باشد، خطایی صادر نخواهد کرد.

interface Input {
  a: string;
  b: number;
}

interface Output {
  c: Omit<Input, 'a'>;
  d: string;
}

function A(input: Input): Output {
  const result = { c: {a: 'aaa', b: 3}, d: 'okay' };

  // Expect to have an error since the result is not matched to Output
  // but there's not errors
  return result;
}

function B(input: Input): Output {
  const result = "{ c: {a: 'aaa', b: 3}, d: 'okay' }";

  // Work as expected
  return result;
}

// But access a property will give us error. 
const test = A({ a: 'a', b: 3 });
test.c.a

زمین بازی اینجاست.

نظری داری چرا این اتفاق افتاد؟

اگر ما به طور صریح به تایپاسکریپت میگفتیم که const result: Output=...، خطا دریافت میکنیم. اما مطمئن نیستم که این روش یک روش خوب باشد یا نه. آیا راه دیگری وجود دارد که آن را همانطور که انتظار داریم کار کند؟

پاسخ شماره ۱

نظری داری چرا این اتفاق افتاد؟

اگر ما بهطور صریح به تایپاسکریپت بگوییم که const result: Output=...، به ما خطا خواهد داد....

تایپاسکریپت بررسی نوع دقیقتری را برای لترالهای اشیاء اعمال میکند تا سایر عبارات (شامل ارجاعات متغیر). در این حالت، قسمت «دقیقتر» زمانی است که شما به طور صریح یک نوع برای result ارائه میدهید و این یک بررسی ویژگی اضافی است.¹ این یک بررسی عملی است زیرا معمولاً خطای برنامهنویس است، اما در حالت کلی ویژگیهای اضافی مشکلی ندارند (زیرا شکل شیء هنوز با نوع هدف مطابقت دارد، فقط یک زیرنوع است زیرا ویژگیهای بیشتری دارد).

همچنین اگر مستقیماً return را انجام دهید، این خطا را دریافت میکنید:

function A(input: Input): Output {
    return { c: {a: 'aaa', b: 3}, d: 'okay' };
}

اگرچه (به طور ذهنی) معمولاً کد اصلی شما را ترجیح میدهم چون اشکالزدایی آن آسانتر است.

این عمدتاً مسئلهای مربوط به سبک است، اما من مشکل را با انجام این کار بهعنوان یک روش عمومی هر زمان که ممکن باشد حل میکنم:

function A(input: Input) {
//                      ^−−−− No return type annotation
    const result: Output = { c: {a: 'aaa', b: 3}, d: 'okay' };
//                ^−−−−−−−−−− Explicit type

    return result; // <−−−−−− TypeScript infers return type
}

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

لینک زمین بازی


¹ هنگامی که من این را در آوریل ۲۰۲۱ مینویسم، مستندات TypeScript در حال گذراندن یک بازنویسی بزرگ است و صفحه لینکشده به عنوان «منسوخ» ذکر شده و به صفحه «جدید» ارجاع داده شده است، اما صفحه جدید هنوز بررسی ویژگیهای اضافی را توضیح نمیدهد. امیدوارم که به زودی این مشکل برطرف شود.