تایپاسکریپت - چگونه میتوانم یک مقدار به یک نوع شرطی اختصاص دهم؟


من دارم سعی میکنم یک تابع بنویسم طوری که پارامتر اول آن boolean باشد، و بسته به این که این آرگومان true یا false باشد، پارامتر دوم یک تابع باشد که یا یک string یا string[] را میپذیرد.

این تلاش من است:

type P<B extends boolean> = B extends true ? string[] : string

function callback<B extends boolean>(b: B, t: (f: P<B>) => void) {
    const file = 'file'
    if (b) {
        t([file]) // <-- Error: Argument of type 'string' is not assignable to parameter of type 'P<B>'.
    } else {
        t(file)   // <-- Error: Argument of type 'string[]' is not assignable to parameter of type 'P<B>'.
    }
    
}

callback(false, (f: string) => {})  // <-- No problem, resolves the correct argument type
callback(true, (f: string[]) => {}) // <-- No problem, resolves the correct argument type

این برای تعیین نوع آرگومانهای صحیح هنگامی که تابع فراخوانی میشود کار میکند. با این حال، داخل تابع، کامپایلر TypeScript به من خطا میدهد که نمیتواند نوع شرطی را به string یا string[] حل کند. راه درست برای انجام این کار چیست؟

لینک زمین بازی.

پاسخ شماره ۱

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

type f = { b: false, t: (f: string) => void };
type t = { b: true, t: (f: string[]) => void };
type fOrT = f | t;

function callback(x: fOrT) {
    const file = 'file'
    if (x.b) {
        x.t([file])
    } else {
        x.t(file)
    }
    
}

callback({ b: false, t: (f: string) => {} })
callback({ b: true, t: (f: string[]) => {} })

محل بازی تایپاسکریپت

پاسخ شماره ۲

شما میتوانید یک نوع را بر اساس یک متغیر دیگر با نوشتن تایپینگهای سفارشی محدود کنید، برای مثال

function instanceOfMyTYpe(textArray: string[] | string, flag: boolean): textArray is string {
    return flag;
}

let file = "test" as string | string[];
file.toUpperCase(); // doesn't work. Property 'toUpperCase' does not exist on type 'string | string[]'.
if (instanceOfMyTYpe(file, true)) file.toUpperCase(); // but this works
else file.forEach((text)=>text.toUpperCase()); // and so does this

این کار یک متغیر را به دیگری متصل نخواهد کرد، بنابراین فراخوانیهای نامعتبر تابع همچنان میتوانند رخ دهند. برای مثال، فراخوانی تابع با یک آرایه و فلگ true، باعث میشود برنامه شما فکر کند که آرایه در واقع یک رشته است، حتی اگر اینطور نباشد، بنابراین مراقب باشید.