typescript - Kan jag skapa en typ som 'strängar som eventuellt är odefinierade'


Obs: jag har --strictNullChecks aktiverat

Jag har en funktion som denna:

export function ensure<T, F extends T>(maybe: T | undefined, fallback: F): T {
  if (isDefined<T>(maybe)) {
    return maybe
  }

  if (fallback === undefined) {
    throw new Error('Could not ensure a value; please supply a fallback for the given variable')
  }

  return fallback
}

Jag använder det för att bli av med undefined-delen av ett eventuellt odefinierat värde.

const user = ensure(possiblyUndefinedUser, defaultUser)

Jag skulle vilja ha följande,

const user = ensure(certainlyDefinedUser, defaultUser)

att vara ett kompileringstid-fel. Det vill säga jag vill att det ska vara olagligt att använda denna funktion med ett värde som kompilatorn tror har ett värde. (detta kommer att tvinga mig att skriva mina funktioner korrekt, se nedan)

Jag har testat ett antal variationer på de generiska argumenten, för att få en typ som T | undefined där värden av typen T inte kan tilldelas den. Jag inser att detta kanske inte är möjligt, men jag tänkte att jag skulle rådfråga communityn istället för att fortsätta slita på egen hand.

Mitt mål här är: om jag vill skicka ett argument genom en funktion som isNil eller ensure, då vill jag bli tvungen att ändra argumenttypen till att vara valfri. Om ett argument inte är valfritt, då bör jag vara 100% säker på att det inte finns något behov av att kontrollera om det är null.

Svar #1

Vi kan använda en villkorlig typ för att utlösa ett fel på den andra parametern om den första inte kan vara undefined.

function isDefined<T>(v: T) : v is Exclude<T, undefined> {
    return !!v;
}

export function ensure<T, F extends (undefined extends T ? T : 'First param can\'t be undefined')>(maybe: T, fallback: F): Exclude<T, undefined> {
    if (isDefined<T>(maybe)) {
        return maybe
    }

    if (fallback === undefined) {
        throw new Error('Could not ensure a value; please supply a fallback for the given variable')
    }
    //We need a type assertion here as F tehnically might not extend T
    return fallback as any;
}

declare var defaultUser: string|undefined;
declare var possiblyUndefinedUser: string| undefined
const user = ensure(possiblyUndefinedUser, defaultUser) //ok

declare var certainlyDefinedUser: string;
const user2 = ensure(certainlyDefinedUser, defaultUser); //error

I Playground-länken måste du manuellt välja strikta nullkontroller.