typescript - راه مرسوم برای تعیین نوع نبود یک ویژگی چیست؟


آیا راهی مرسوم برای مشخص کردن a وجود دارد به طوری که یک ویژگی خاص قطعاً بر روی آن ظاهر نشود؟ من یک روش برای انجام این کار به این شکل پیدا کردهام:

type NoValue<T extends { value?: never, [key: string]: unknown }> = T
type Foo = NoValue<{}> // typechecks fine
type Bar = NoValue<{name: string}> // also typechecks fine
type Baz = NoValue<{ value: string }> // errors, as it should

با این حال، من کاملاً مطمئن نیستم که آیا این تمام موارد حاشیهای را پوشش میدهد یا نه. آیا روش استانداردی برای انجام این کار وجود دارد؟

پاسخ شماره ۱

هیچ راه "کانونیکال" برای ممنوع کردن رشتههای خاص از ظاهر شدن به عنوان کلیدهای شناخته شده یک نوع شی در تایپاسکریپت وجود ندارد، چرا که هیچ ویژگی مستندی که به طور خاص این کار را انجام دهد وجود ندارد. اما کاری که شما انجام میدهید احتمالاً رویکرد "استاندارد" است.

نزدیکترین کاری که میتوانید انجام دهید این است که از یک ویژگی اختیاری استفاده کنید که اجازه میدهد یک ویژگی غایب باشد، که نوع آن نوع غیرممکن never است که بهطور کلی نمیتوانید مقداری برای آن پیدا کنید. معقولترین راه برای تامین {value?: never} استفاده از یک شیء است که فاقد ویژگی value باشد. و اگر میخواهید مطمئن شوید که این تنها شرط است، میتوانید امضای شاخص فراگیر {[k: string]: any} را اضافه کنید. شما باید از نوع any به جای unknown استفاده کنید زیرا any اجازه میدهد انواع رابط بدون امضای شاخص صریح باشند، در حالی که unknown این اجازه را نمیدهد. بنابراین این به شما میدهد

type NoValue<T extends { value?: never, [key: string]: any }> = T

یا

function noValue<T extends { value?: never, [key: string]: any }>(t: T) { }

noValue({}); // okay
noValue({ a: 123 }); // okay
noValue({ a: 123, value: 456 }); // error!
// -------------> ~~~~~

اما، همانطور که شما اشاره میکنید، موارد حاشیهای وجود دارند. رایجترین موردی که ممکن است برخورد کنید این است

noValue({ value: undefined }); // okay, usually

جایی که undefined به عنوان یک نوع ویژگی معتبر برای همه ویژگیهای اختیاری در نظر گرفته میشود. شما میتوانید گزینه کامپایلر --exactOptionalPropertyTypes را فعال کنید تا از کارکردن این موضوع جلوگیری کنید، اما تنها با این هزینه که سختگیریای اضافه میشود که بخشی از مجموعه استاندارد گزینههای کامپایلر --strict نیست. بسیاری از افراد استفاده از --exactOptionalPropertyTypes را آزاردهنده میدانند. این موضوع به خود شما بستگی دارد.


یک مورد حاشیهای کمتر رایج: شما در واقع میتوانید گاهی اوقات به نظر برسد که مقداری از نوع never به دست آوردهاید، به شرط آنکه این بدان معنا باشد که کامپایلر مطمئن است زمان اجرای جاوااسکریپت قبل از رسیدن شما به آنجا، یک استثناء پرتاب خواهد کرد. برای مثال:

const v = {
    get value(): never {
        throw new Error("I DON'T LIKE YOU")
    }
};
noValue(v); // okay

بنابراین کامپایلر مشاهده میکند که v از نوع {value: never} است. دلیلش این است که اگر واقعاً تلاش کنید v.value را ارزیابی کنید، یک استثناء در زمان اجرا رخ خواهد داد. بنابراین به جای اینکه undefined در v.value وجود داشته باشد، یک بمب آنجاست. این بسیار غیرمعمول است اما از نظر فنی ممکن است، بنابراین 🤷♂️.


و در نهایت، هیچ چیزی نمیتواند مانع از آن شود که کامپایلر تشخیص دهد آیا یک مقدار دارای ویژگی value است یا نه، زیرا شما همیشه میتوانید یک نوع را به «ایمن» به یک نوع مادر گسترش دهید، و به دلیل اینکه ویژگیهای اختیاری به عنوان قابل انتساب به یک نوع بدون ویژگی مشخص در نظر گرفته میشوند و از آن قابل انتساباند. (برای نمونه، این نظر در ms/TS#42479 را ببینید). بنابراین این به شما چیزی شبیه به

const w = { a: 123, value: "abc" };
const x: {a: number} = w; // okay
noValue(x); // okay!

جایی که کامپایلر به طور صریح به فراموش کردن ویژگی value روی x دستور داده شده است، حتی اگر شیء قطعاً یکی داشته باشد. و سپس noValue(x) موفق میشود زیرا تا جایی که کامپایلر میداند، x هیچ ویژگی value ندارد.


ممکن است هشدارها و موارد حاشیهای دیگری هم وجود داشته باشد، اما اینها موارد اصلی هستند. اساساً شما نیاز دارید که بر اساس موارد استفاده خود آزمایش کنید، زیرا برای برخی افراد این مشکلات کاملاً بیضرر هستند و برای برخی دیگر موانع جدی محسوب میشوند. من میگویم که تمام کاری که واقعاً میتوانیم در تایپاسکریپت انجام دهیم این است که یک ویژگی شیء را تشویق به عدم استفاده کنیم، برخلاف اینکه واقعاً آن را ممنوع کنیم.

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

پاسخ شماره ۲

type NothingCanBeAValue = { [K: keyof any]: never };

const iHaveSomeValue: {} = { foo: 42 };

const objectAboveIsAssignableTo: NothingCanBeAValue = iHaveSomeValue;

// no type error

راهی برای انجام دادن آن وجود ندارد.