typeguards - Typescript tür koruyucusu çıkarımsız string literali


Böyle bir davranışın bir hata olup olmadığını anlamaya çalışıyorum?
guard(a: T|undefined) tanımladım ve ona "admin"|undefined türünde bir değer verdim ki bu açıkça string|undefined türünün alt türü. Ama guard filtreledikten sonra |undefined kısmını kaldırmadı.

Benim için zor, çünkü eğer if (user === 'Jack') return 'admin' kodunu if (user === 'Jack') return 'admin' as string; olarak değiştirirsem, tip koruması doğru çalışıyor ve string|undefined string oluyor.

function getPermissions(user: string){
    if (user === 'Jack') return 'admin';
    return undefined;
}
const admins = ['Mike', 'Joe'].map(e => getPermissions(e)); //type ("admin"|undefined)[]

function isDefined<T>(a: T | undefined): a is T {
    return a !== undefined;
}

const foundAdmins = admins.filter(isDefined); //type is ("admin"|undefined)[] but string[] expected

Bunu bir playground TypeScript playground'unda çalıştırabilirsiniz

Cevap #1

Sorun, genel genişletme "admin" den string e yapılıyor

Bunu aynı şekilde yapabilirsiniz, ama typescript@^5 ile

Oyun alanı

function getPermissions(user: string){
    if (user === 'Jack') return 'admin';
    return undefined;
}
const admins = ['Mike', 'Joe'].map(e => getPermissions(e)); //type ("admin"|undefined)[]

function isDefined<T>(a: T | undefined): a is T {
    return a !== undefined;
}

const foundAdmins = admins.filter(isDefined); 
//    ^?
// const foundAdmins: ("admin" | undefined)[]

function isDefinedWithConst<const T>(a: T | undefined): a is T {
    return a !== undefined;
}

const foundAdmins2 = admins.filter(isDefinedWithConst); 
//    ^?
// const foundAdmins2: "admin"[]
// dirty but works
const foundAdmins3 = admins.filter((e): e is 'admin' => isDefined<'admin'>(e)); 
//    ^?
// const foundAdmins3: "admin"[]

veya o özel durum için bir aşırı yükleme yap

function nonNullable<T>(v: T | null | undefined): v is T { return v != null }
interface Array<T> {
    filter(p: typeof nonNullable): NonNullable<T>[]
}
const foundAdmins4 = admins.filter(nonNullable);
//    ^?
// const foundAdmins4: "admin"[]