TypeScript: Hvordan forbedre en type der en av dens egenskaper sannsynligvis er null
Jeg prøver å forstå hvordan man skriver en funksjon som vil forbedre typen min og deretter returnere den.
Det fungerer for det sendte objektet selv og innenfor funksjonskroppen, men ikke utenfor. Videre synes jeg det er merkelig at selv om det fungerer innenfor funksjonskroppen, slutter det å fungere på selve return-setningen.
Min forventning ville være at TypeScript korrekt returnerer en raffinert/innsnevret type, med sjekkede egenskaper inkludert, ikke bare hovedobjektet.
Hva gjør jeg galt? Krever TypeScript mer hjelp, kanskje en generisk returtype?
Eksempel:
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
} | null;
function checkUserHasReferrer(user: User) {
if (!user) {
throw Error('No User');
}
if (!user.referrer) {
console.log(user.referrer) // referrer correctly narrowed to `referrer: null`
throw Error('No Referrer');
}
const referrer = user.referrer; // referrer correctly narrowed to `referrer: Referrer`
console.log(user.referrer). // same here, `referrer: Referrer`
return user; // <- Here however it's back to `referrer: Referrer | null`
}
let initialUser: User = null;
const user = checkUserHasReferrer(initialUser);
console.log(user.id); // <-- knows User is not `null`
console.log(user.referrer.id); // However it doesn't know referrer is not `null`
TypeScript-lekeplass
Svar #1
Du må bruke assertionsfunksjoner
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
}
function notNull(user: unknown): asserts user {
if (!user) {
throw Error('No User');
}
}
declare const user: User | null
notNull(user)
notNull(user.referrer)
console.log(user.id);
console.log(user.referrer.id); // ok
Lekeplass
Når du trenger å kaste en feil i TypeScript, er vanligvis en slik funksjon en god kandidat til å være assertion function
Eller, du kan bruke egendefinerte typeguard-funksjoner
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
}
const isValidUser = (user: User | null): user is User & { referrer: Referrer } =>
Boolean(user && user.referrer)
declare const user: User
if (isValidUser(user)) {
console.log(user.id);
console.log(user.referrer.id); // ok
}
Lekeplass
Svar #2
Dette er for øyeblikket en kjent begrensning i TypeScript. Se her eller her. TypeScript innskrenker korrekt typen til egenskapen, men typen til forelderen blir ikke endret, så den forblir bare User.
En mulig løsning uten typepåstander ville være å spre objektet når det returneres.
return {
...user,
referrer
};
Dette endrer returtypen til funksjonen til
{
referrer: Referrer;
id: string;
}
Lekeplass
