typescript - گارد نوع برای فیلدهای اینترفیس در شاخه else


interface Test<T> {
    field: string | T;
}

function isString<T>(test: Test<T>): test is Test<string> {
    return typeof test.field === "string";
}

function f<T>(test: Test<T>) {
    if (isString(test)) {
        const a = test.field; // the type of a is string
    } else {
        const b = test.field; // the type of b is string | T
    }
}

در کد بالا، در شاخه if، a از نوع string است، که درست است. با این حال، در شاخه else، b از نوع string | T است.

حتی اگر یک بررسی برای T اضافه کنم، همان نتیجه را میگیرم:

function isT<T>(test: Test<T>): test is Test<T> {
    return typeof test.field !== "string";
}

function f<T>(test: Test<T>) {
    if (isString(test)) {
        const a = test.field; // the type of a is string
    } else if (isT(test)) {
        const b = test.field; // the type of b is string | T
    }
}

چه کاری میتوانم انجام دهم تا b را صریحاً به T تبدیل نکنم، درست همانطور که نیازی به تبدیل a به string ندارم؟

پاسخ شماره ۱

مشکل این است که محافظ نوع تعریفشده توسط کاربر نوع Test<T> را بررسی میکند، اما آنچه شما میخواهید نوع field است.

شما شاخهبندی خود را اینگونه دارید:

function f<T>(test: Test<T>) {
    if (isString(test)) {
        // We have a `Test<string>`
        const a = test.field;
    } else {
        // We do not have a `Test<string>`
        const b = test.field;
    }
}

در شاخه if، ما یک Test<string> داریم، و ویژگی field آن یک اتحاد از string | string است (که فقط یک string است). نوع آن به این شکل است:

interface Test<string> {
    field: string | string;
}

در شاخه else، ما یک Test<SomeNonString> داریم و ویژگی field آن ترکیبی از string | SomeNonString است. نوع آن به این شکل به نظر میرسد:

interface Test<SomeNonString> {
    field: string | SomeNonString;
}

در آن شاخهی else، ما نیاز به رفع ابهام داریم، زیرا field هنوز یک نوع اتحادی است. تا زمانی که رابط Test<T> field را به عنوان یک نوع اتحادی از string | T تعریف کند، ما به آزمون بعدی نیاز خواهیم داشت هر زمان که T برابر با string نباشد.

در اینجا یک نمونه است:

function isFieldString<T>(field: string | T): field is string {
    return typeof field === "string";
}

function f<T>(test: Test<T>) {
    if (isString(test)) {
        const s = test.field; // const s: string
    } else if (isFieldString(test.field)) {
        const s = test.field; // const s: string
    } else { 
        const t = test.field; // const t: T
    }
}