Веселье + разочарование с сопоставленными типами TypeScript

Введение в проблему

Я изо всех сил пытаюсь разработать сильную типизацию для конкретной функции, написанной на TypeScript...

Для иллюстрации предположим, что я работаю в Департаменте транспортных средств и у меня есть функция registerVehicles, которая может принимать регистрационные данные для многих транспортных средств, организованных по имени, и возвращает номерные знаки для каждого транспортного средства.

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

Функция возвращает объект с именами каждого транспортного средства и их вновь зарегистрированными номерными знаками:

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

Функция существует и отлично работает, проблема заключается в том, чтобы сделать ее строго типизированной.

Решение должно соответствовать следующим критериям:

  1. Переменная licensePlates должна быть неявно типизирована из результата функции.

  2. Попытка передать регистрационную информацию, которая не существует, должна привести к ошибке во время компиляции.

    registerVehicles({
        car: {
            cowPower: 500 // <-- Spelling mistake, should be horsePower
        }
    })
    
  3. Попытка получить доступ к номерному знаку транспортного средства, которое вы не зарегистрировали, должна привести к ошибке во время компиляции:

    const licensePlates = registerVehicles({
        ferrari: {
            horsePower: 562
        }
    });
    
    alert(licensePlates.furrari); // <-- Spelling mistake, should be ferrari
    
  4. TypeScript должен знать, что каждый номерной знак является строкой во время компиляции.

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

Я приблизился, но каждое решение, которое я пробую, в конечном итоге не соответствует хотя бы одному из вышеуказанных требований. Помогите мне сообщество Stack Overflow, вы моя единственная надежда!


person CodeAndCats    schedule 13.08.2017    source источник


Ответы (1)


Утилита типа "Запись" делает то, что вы хотите. С его помощью вы можете отображать динамические свойства одного типа на другой тип. (Попробуйте на игровой площадке):

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

Тип K будет строковым литералом, например. "sportsCar" | "motorBike" | "hoverBoard".

Обновление: это плохо документировано. Но вот ссылка на документацию, и я нашел пример здесь.

person Markus    schedule 13.08.2017
comment
@Magu, мне любопытно, есть ли у вас творческие идеи на stackoverflow.com/q/46596846/678505, которые я подумайте о том, чтобы вывести сопоставленные типы TS даже на следующий уровень :-) - person Michael Zlatkovsky - Microsoft; 07.10.2017