تایپاسکریپت - چگونه میتوان امضای شاخص را بدون باز کردن اینترفیس تنظیم کرد


این کد را در نظر بگیرید:

interface MyInterface {
  foo: string
  bar: string
  baz: string
}

const myObj: MyInterface = {
  foo: "foo",
  bar: "bar",
  baz: "baz"
};

Object.keys(myObj).forEach(obj => {
  obj = myObj[obj];
});

وقتی حالت سختگیرانه را فعال میکنم، این خطا را دریافت میکنم: TS7017: عنصر به طور ضمنی نوع 'any' را دارد زیرا نوع 'MyInterface' هیچ امضای نمایهای ندارد.

به نظر میرسد سادهترین راهحل این باشد:

interface MyInterface {
  [key: string]: string;
  foo: string
  bar: string
  baz: string
}

با این حال این برای هر ویژگی رشتهای در اشیاء MyInterface باز میشود.

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

type ValidEnteries = "foo" | "bar" | "baz";

type Alternative = {
  [key in ValidEnteries]: string
}

در حالی که این برای من درست به نظر میرسد، مشکل اصلی با نشانگر ایندکس گمشده برمیگردد.

آیا راهی وجود دارد که هم یک امضای شاخص داشته باشیم و هم شیء را به تعداد مشخصی خواص محدود کنیم؟

پاسخ شماره ۱

اگر همه چیزی که میخواهید انجام دهید گرفتن ویژگیهای موجود رابط است، نیازی به امضای ایندکس ندارید. میتوانید هر شیئی را با یک رشته ایندکس کنید تا زمانی که آن رشته یک کلید شناخته شده از شیء باشد. بنابراین این کار میکند:

 myObj['bar'] // 'bar' can be checked as a key of MyInterface so it's ok 

مشکل این است که Object.keys(myObj)، string[] را بازمیگرداند و نه Array<keyof T>. سادهترین راه حل استفاده از یک تأیید نوع است تا به کامپایلر اطلاع دهد که خروجی keys یک آرایه از کلیدهای MyInterface است.

Object.keys(myObj).forEach(obj => {
    let d = myObj[obj as keyof MyInterface];
});
// OR
(Object.keys(myObj) as Array<keyof MyInterface>).forEach(obj => {
    let d = myObj[obj];
});

یا اگر با این که تابع keys همیشه Array<keyof T> را برمیگرداند راحت هستید، میتوانید اعلامیهی جهانی را گسترش دهید

declare global { /// Needed if you are in a module otherwise omit this and the closing }
    interface ObjectConstructor {
        keys<T extends object>(o: T) : Array<keyof T>
    }
}

// Now works without any extra casting 
Object.keys(myObj).forEach(obj => {
    let d = myObj[obj];
});