TypeScript - محدود کردن نوع enum به یک عضو واحد


enum Unit {
    Kilometer,
    Meter,
}

interface Measurement {
    distanceUnit: Unit
    distance: number
}

interface MetersMeasurement {
    distanceUnit: Unit.Meter
}

function someCalculation(item: MetersMeasurement){
    return;
}

function main(){
    const item : Measurement = {
        distance: 100,
        distanceUnit: Unit.Meter
    }

    if(item.distanceUnit !== Unit.Meter) return;
    
    someCalculation(item)
                 // ^^^^ Types of property 'distanceUnit' are incompatible.     
                 //      Type 'Unit' is not assignable to type 'Unit.Meter'.
}

زمین بازی

  1. اگر MetersMeasurement و Measurement همان فیلدها را داشتند، میتوانستیم از یک پراپدیکت نوع برای محدود کردن آن استفاده کنیم. اما مشکل من این است که MetersMeasurement تنها یک زیرمجموعه از فیلدهای Measurement را خواهد داشت. بنابراین یک پراپدیکت نوع کار نخواهد کرد [playground].

  2. ما همچنین میتوانیم نوع distanceUnit در اینترفیس MetersMeasurement را به Unit تغییر دهیم. و سپس بررسی if برای متر را داخل تابع someCalculation منتقل کنیم. اما فرض کنید من نمیخواهم این کار را انجام دهم. نمیخواهم این مسئولیت را به تابع بدهم.

  3. ما میتوانیم یک شیء جدید از نوع MetersMeasurement بر اساس شیء اصلی ایجاد کنیم و سپس آن را به someCalculation() ارسال کنیم. اما این شبیه یک راهحل موقت (و کد اضافی) به نظر میرسد چون نتوانستیم نوع را محدود کنیم [playground].

آیا راهی وجود دارد که نوع را به درستی محدود کرد بدون اینکه قوانین شکسته شود؟ (بدون any یا تبدیل نوع)

پاسخ شماره ۱

شما میتوانید از یک نگهبان نوع استفاده کنید تا بررسی کنید که آیا مورد MetersMeasurement است یا Measurement

enum Unit {
    Kilometer,
    Meter,
}

interface Measurement {
    distanceUnit: Unit
    distance: number
}

interface MetersMeasurement {
    distanceUnit: Unit.Meter
}

function someCalculation(item: MetersMeasurement){
    return;
}

function isMeasurement(obj: Measurement): obj is Measurement {
  return (obj as Measurement).distance !== undefined;
}

function main(){
    const item = {
        distance: 100,
        distanceUnit: Unit.Meter
    }

    if(isMeasurement(item)) return;
    
    someCalculation(item)
}

پاسخ شماره ۲

این را امتحان کن:

enum Unit {
    Kilometer,
    Meter,
}

type Distance = {
    distance: number;
}

type MetersMeasurement = {
    distanceUnit: Unit.Meter;
} & Distance

type KilometersMeasurement = {
    distanceUnit: Unit.Kilometer;
} & Distance

type Measurement = MetersMeasurement | KilometersMeasurement;


/*
 * return km
 */
function meterCalculation(item: MetersMeasurement) {
    return item.distance * .001;
}

function main(){
    const item : Measurement = {
        distance: 100,
        distanceUnit: Unit.Meter
    }

    if (item.distanceUnit == Unit.Meter) {
        meterCalculation(item);
    }
}

زمین بازی