TypeScript - نوع شرطی که یک اتحاد را محدود میکند
متأسفم بهخاطر عنوان مبهم، اما سؤال من اینجا از جایی ناشی میشود که واقعاً نمیفهمم TS در این موقعیت چه کار میکند، بنابراین مطمئن نبودم چه چیزی را نام ببرم. اساساً سؤال این است--چرا این کد شکست میخورد:
type Broken<T extends string | number> = T extends string ? T : null
let foo:Broken<string>
let value: string
foo = value
let value2: string | number
// this error makes sense to me: value2 could be a number
foo = value2
if(typeof value2 === "string")
{
// and it makes sense to me this works, because now we know they are both strings
foo = value2
}
function testBroken<T extends string | number>(value: T ){
let bar: Broken<T>
// i guess i expect this error, but i'm not sure i understand why TS says the error
// is that "string" is not assignable to Broken<T>
bar = value
if(typeof value === "string"){
// Why is this an error? if "bar" is narrowed to string don't we know that Broken<T>
// is also "string"?
bar = value
}
}
زمین بازی اینجا
به طور خاصتر، چرا انتسابها به bar خطا ایجاد میکنند (به خصوص مورد دوم) اما انتساب دوم به foo نه. من فکر میکنم این موضوع به توزیع نوع در نوعهای شرطی مربوط میشود، اما نمیتوانم کاملاً آن را درک کنم.
کسی میتواند توضیح دهد چه خبر است اینجا؟ (توجه: این سوال بیشتر دربارهی فهمیدن اینکه TS اینجا چه میکند است، نه پیدا کردن یک راهحل جایگزین برای تایپها).
پاسخ شماره ۱
این در حال حاضر یک محدودیت طراحی در TypeScript است. کامپایلر ارزیابی انواع شرطی که به پارامترهای نوع جنریک هنوز مشخص نشده بستگی دارند را به تعویق میاندازد و بنابراین واقعاً نمیتواند تأیید کند که چیزی قابل انتساب به چنین انواعی است یا خیر. در مورد شما، Broken<T> یک نوع شرطی است و در داخل پیادهسازی testBroken()، پارامتر نوع جنریک T که به آن بستگی دارد مشخص نشده است (فقط زمانی مشخص میشود که call کنید testBroken()). بنابراین Broken<T> اساساً برای کامپایلر غیرشفاف است و نمیتواند ببیند که value قابل انتساب به آن است، حتی اگر بتوانستید T یا value را به یک زیرنوع از string محدود کنید.
به احتمال زیاد موضوع قدیمی در GitHub که مربوط به این است، microsoft/TypeScript#33912 میباشد. این موضوع پیشنهاد میکند که کامپایلر باید از تحلیل جریان کنترل استفاده کند تا value و بنابراین احتمالاً T را به یک زیرنوع از string محدود کند و سپس از آن برای محدود کردن Broken<T> به T استفاده کند. اگر میخواهید این اتفاق بیفتد، ممکن است بخواهید به آن موضوع بروید و به آن 👍 بدهید. تا زمانی که این پشتیبانی نشود، فقط راهحلهای موقت وجود دارند.
از آنجا که سؤال مربوط به راهحلهای جایگزین نیست، من به جزئیات زیادی نمیپردازم، اما به طور خلاصه همیشه میتوانید از تصدیق نوعها برای خاموش کردن هشدارها استفاده کنید:
function testBrokenAssertion<T extends string | number>(value: T) {
let bar: Broken<T>
if (typeof value === "string") {
bar = value as Broken<T>
}
}
و شما حتی میتوانید این تأییدها را به یک تابع کمکی همانطور که در microsoft/TypeScript#33912 نشان داده شده است بازسازی کنید:
function testBrokenHelper<T extends string | number>(value: T) {
let bar: Broken<T>
bar = conditionalProducingIf(value, isString, () => value, () => null) // okay
}
لینک زمین بازی به کد
