Roligt + Frustration med TypeScript Mappade Typer


Introduktion till problem

Jag har svårt att utveckla stark typning för en viss funktion som är skriven i TypeScript...

För illustrativa syften, låt oss säga att jag arbetar på Trafikverket och har en funktion registerVehicles som kan ta emot registreringsuppgifter för många fordon organiserade efter namn, och returnerar registreringsskyltarna för varje fordon.

interface RegistrationDetails {
    transmission?: 'manual' | 'automatic';
    wheelCount?: number;
    horsePower?: number;
}

const licensePlates = registerVehicles({
    sportsCar: {
        transmission: 'manual',
        wheelCount: 4,
        horsePower: 762
    },
    motorBike: {
        transmission: 'manual',
        wheelCount: 2
    },
    hoverBoard: {
        // All registration details are optional
    }
});

Funktionen returnerar ett objekt med namnen på varje fordon och deras nyregistrerade registreringsskyltar:

expect(licensePlates).to.eql({
    sportsCar: 'ABCD-1234',
    motorBike: 'SPDY-5678',
    hoverBoard: 'BTTF-2'
});

Funktionen existerar och fungerar perfekt, problemet är att få den starkt typad.

Lösningen måste uppfylla följande kriterier:

  1. Variabeln licensePlates bör implicit typas från resultatet av funktionen.

  2. Att försöka skicka en registreringsdetalj som inte finns bör ge fel vid kompilering.

    registerVehicles({
        car: {
            cowPower: 500 // <-- Spelling mistake, should be horsePower
        }
    })
    
  3. Att försöka få åtkomst till registreringsskylten på ett fordon som du inte registrerade borde ge fel vid kompilering:

    const licensePlates = registerVehicles({
        ferrari: {
            horsePower: 562
        }
    });
    
    alert(licensePlates.furrari); // <-- Spelling mistake, should be ferrari
    
  4. TypeScript bör veta att varje registreringsskylt är en sträng vid kompileringstid

    const licensePlates = registerVehicles({
        ferrari: {}
    });
    
    alert(licensePlates.ferrari * 5); // <-- Error, you can't multiple a string
    

Jag har kommit nära, men varje lösning jag försöker misslyckas till slut med att uppfylla åtminstone ett av ovanstående krav. Hjälp mig Stack Overflow-community, ni är mitt enda hopp!

Svar #1

Hjälpverktygstypen "Record" gör det du vill. Med den kan du mappa dynamiska egenskaper från en typ till en annan typ. (Prova det i Playground):

function registerVehicles<K extends string>(p: Record<K, RegistrationDetails> ):  Record<K, string> { 
    return null;
}

Typen K kommer att vara en strängliteraltyp, t.ex. "sportsCar" | "motorBike" | "hoverBoard".

Uppdatering: Det är inte väl dokumenterat. Men här är en länk till dokumentationen och jag hittade ett exempel här.

Relaterade artiklar