TypeScript зависимые строковые литеральные свойства и индексация - PullRequest
1 голос
/ 27 апреля 2020

Аналогично Typescript: тип свойства, зависящего от другого свойства в том же объекте Мне нужен тип, в котором свойства являются зависимыми.

const attributes = {
    physical: {
        traits: {
            strength: 1,
            dexterity: 1,
            stamina: 1,
        }
    },
    social: {
        traits: {
            charisma: 1,
            manipulation: 1,
            appearance: 1,
        }
    },
    mental: {
        traits: {
            perception: 1,
            intelligence: 1,
            wits: 1,
        }
    }
};

type AttributeTrait =
    | {
        category: 'physical';
        trait: keyof typeof attributes.physical.traits;
    }
    | {
        category: 'social';
        trait: keyof typeof attributes.social.traits;
    }
    | {
        category: 'mental';
        trait: keyof typeof attributes.mental.traits;
    };

const action: AttributeTrait = {
    category: 'social',
    trait: 'manipulation'
}

function increment(action: AttributeTrait) {
    attributes[action.category].traits[action.trait]++; // error 7053
}

В функции action.trait набирается как:

(свойство) черта: "сила" | "ловкость" | "выносливость" | "Харизма" | "манипуляция" | "внешний вид" | "восприятие" | "интеллект" | "wits"

, поэтому его нельзя использовать для индексации traits.

Как мне решить эту проблему?

1 Ответ

1 голос
/ 27 апреля 2020

У меня нет небезызвестного и безопасного типа решения для вас. Ваш AttributeTrait - это то, что я называю коррелированным типом записи . Это дискриминационное объединение, где некоторый фрагмент кода безопасен для каждого члена объединения, но компилятор не может видеть, что это безопасно для объединения в целом, потому что он теряет связь корреляции между category и trait свойств.

Если вы напишите избыточный код, ошибка исчезнет:

function incrementRedundant(action: AttributeTrait) {
    switch (action.category) {
        case "physical":
            attributes[action.category].traits[action.trait]++;
            return;
        case "social":
            attributes[action.category].traits[action.trait]++;
            return;
        case "mental":
            attributes[action.category].traits[action.trait]++;
            return;
    }
}

Но, как ни старайтесь, вы не можете свернуть эти случаи в одну строку кода и получить компилятор для проверки безопасности для вас. Вот почему я подал microsoft / TypeScript # 30581 , и одна из причин, по которой я подал microsoft / TypeScript # 25051 . Поскольку вы не можете попросить компилятор обрабатывать одну строку , как если бы она была записана в операторе switch / case, лучшее, что я могу придумать, это использовать введите assertion , чтобы сообщить компилятору, что вы знаете лучше, чем он.

Один из способов сделать это - немного l ie и сообщить компилятору, что attributes на самом деле имеет все trait с на всех category объектах:

function increment(action: AttributeTrait) {
    (attributes as
        Record<AttributeTrait["category"], {
            traits: Record<AttributeTrait["trait"], number>
        }>
    )[action.category].traits[action.trait]++;
}

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


Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...