Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fun + Frustration with TypeScript Mapped Types

Introduction to problem

I am struggling to develop strong typing for a particular function written in TypeScript...

For illustrative purposes, lets say I work at the Department of Motor Vehicles and have a function registerVehicles that can accept registration details for many vehicles organised by name, and returns the license plates for each vehicle.

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
    }
});

The function returns an object with the names of each vehicle and their newly registered license plates:

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

The function exists and works perfectly, the problem is getting it strongly typed.

The solution must meet the following criteria:

  1. The variable licensePlates should be implicitly typed from the result of the function.

  2. Trying to pass a registration detail that doesn't exist should error at compile time.

    registerVehicles({
        car: {
            cowPower: 500 // <-- Spelling mistake, should be horsePower
        }
    })
    
  3. Trying to access the license plate of a vehicle you didn't register should error at compile time:

    const licensePlates = registerVehicles({
        ferrari: {
            horsePower: 562
        }
    });
    
    alert(licensePlates.furrari); // <-- Spelling mistake, should be ferrari
    
  4. TypeScript should know each license plate is a string at compile-time

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

I've gotten close, but every solution I try ultimately fails to meet at least one of the above requirements. Help me Stack Overflow community, you're my only hope!

like image 935
CodeAndCats Avatar asked May 05 '26 03:05

CodeAndCats


1 Answers

The utility types "Record" does what you want. With it you can map dynamic properties from one type to another type. (Try it in the Playground):

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

The K type will be a String Literal Types eg. "sportsCar" | "motorBike" | "hoverBoard".

Update: It is not well documented. But here is a link to the Documentation and I found a example here.

like image 96
Markus Avatar answered May 06 '26 16:05

Markus