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


در کد زیر، برای من دشوار است که بفهمم چرا تایپاسکریپت توانایی دارد نوع x را وقتی جداگانه دسترسی دارد به درستی محدود کند اما نه روی کل شیء. از آنجایی که o.x «خصوصیت x روی شیء o» است، به نظر ناهماهنگ میرسد که نوع «o.x» و نوع x همانطور که روی o مشاهده میشود ممکن است متفاوت باشند.

function checkPropType(o: {x: number | undefined, s: string}) {
  o.x ??= 5;

  takeNumber(o.x); // Great!

  takeOWithDefinedX(o) // T_T
  takeOWithDefinedX({...o, x: o.x}); // Works but ugly and wasteful.
}

function checkPropType2(o: {x: number | undefined, s: string}) {
  let {x} = o;
  x ??= 5;

  takeNumber(x); 

  takeOWithDefinedX(o) // T_T
  takeOWithDefinedX({...o, x}) // Works, but the destructuring makes it even more verbose overall
}

function takeNumber(x: number) {}
function takeOWithDefinedX(o: {x: number, s: string}) {}

https://tsplay.dev/NddKyN

هر دلیلی که باشد، به نظر میرسد فعلاً باید با آن برخورد کنیم. من دو روش ممکن برای انجام این کار نشان دادهام، هر دو شامل ایجاد یک شیء کاملاً جدید فقط برای رضایت کامپایلر TS هستند. خروجی JS نشان میدهد که این هم کامپایل نمیشود. takeOWithDefinedX(Object.assign(Object.assign({}, o), { x: o.x }));

آیا راه تمیزتری برای مدیریت این مشکل وجود دارد؟

پاسخ شماره ۱

شما میتوانید یک ابزار سفارشی ایجاد کنید که یک نوع و یک ویژگی را به عنوان آرگومانهای جنریک دریافت کند، و یک نوع نقشهای ایجاد کند که نوع مقدار ویژگی را به آن نوع تنظیم میکند و undefined را مستثنی میکند. سپس از آن استفاده کنید تا تأیید کنید که o نوعی است که نیاز دارید. این هیچ کد زمان اجرا اضافی ایجاد نمیکند (خروجی JS در سمت راست در لینک playground را بررسی کنید).

محیط آزمایشی TS

type RequiredProp<T, K extends keyof T> = T & Record<K, Exclude<T[K], undefined>>;

function takeNumber(x: number) {}
function takeOWithDefinedX(o: {x: number, s: string}) {}

function checkPropType(o: {x: number | undefined, s: string}) {
  o.x ??= 5;
  takeNumber(o.x);
  takeOWithDefinedX(o as RequiredProp<typeof o, 'x'>);
}

پاسخ شماره ۲

من احساس ناامیدی شما را درک میکنم، واقعاً شما یک اختصاص در کد خود دارید تا اطمینان حاصل شود که اکنون «x» شما تعریف شده است، اما فکر نمیکنم TypeScript بتواند تمام کد را برای شما بررسی کند؛

اینجا پاسخ رسمی است، اما کمی قدیمی است

پس شما ۳ گزینه دارید:

  1. TS را نادیده بگیر

    if it's non-complex and rarely used types
    

    takeOWithDefinedX(o به عنوان {x: عدد; s: رشته})

  2. از پاسخ @jsejcksn استفاده کنید

    همان ایده مانند «۱»،

    فقط یه ترفند کلی، گفتن «آره، آره، مطمئنم که همه ویژگیها رو بررسی کردم»

  3. ایجاد تابع تبدیل

    type IAll = { x: number; s: string } // don't use "I" for the prefix, can't come up with something short )))
    type IOpt = { x?: number | undefined; s: string };
    
    function ensure(opt: IOpt, x = 5): IAll {
      let a: IAll = {x, s: opt.s};
      if (opt.x) a.x = opt.x;
      return a;
    }
    
    function checkPropType(o: IOpt) {
      takeOWithDefinedX(ensure(o)) // -_0
    }
    
    function takeNumber(x: number) {}
    function takeOWithDefinedX(o: IAll) {}
    
    
    

تیاسپلی