TemporaryFix
TemporaryFix

Reputation: 2186

How can I use a Record's value as an object key whose interface is the same type

I have a type defined that is an OR of strings(this cannot be an enum due to unrelated circumstances)

export type AnimalTypes =
    | 'dog'
    | 'cat'
    | 'donkey'
    | 'elephant';

I use this type as the value in an exported Record

export const PartyTypes: Record<string, AnimalTypes> = {
    DOG_PARTY: 'dog',
    CAT_PARTY: 'cat',
    DEMOCRAT_PARTY: 'donkey',
    REPUBLICAN_PARTY: 'elephant',
};

In a separate file I import the two above and make an interface with the AnimalType as the key

interface ElectionState {
    votesByParty: Record<AnimalTypes, PartyVotesCollection>;
}

When I try to make an object literal implementing this interface using the PartyTypes Record I get an error on the object key

export const initialState: ElectionState = {
    votesByParty: {
        PartyTypes.DOG_PARTY: {
            electionsWon: 0,
            voteCount: 0,
        },
    },
};

Type '{ PartyTypes: Record<string, AnimalTypes>; "": any; }' is not assignable to type 'Record<AnimalTypes, PartyVotesCollection>'. Object literal may only specify known properties, and 'PartyTypes' does not exist in type 'Record<AnimalTypes, PartyVotesCollection>'.ts(2322)

What can I do to use the value of a Record as the key in another object that inherits from an interface?

Playground

Upvotes: 0

Views: 766

Answers (1)

Federkun
Federkun

Reputation: 36924

Something to note:

export const initialState: ElectionState = {
    votesByParty: {
        PartyTypes.DOG_PARTY: 0,
    },
};

should be

export const initialState: ElectionState = {
    votesByParty: {
        [PartyTypes.DOG_PARTY]: 0,
    },
};

Also, the votesByParty type require all the parties ( unless you want to make a partial type out of it )

export const initialState: ElectionState = {
    votesByParty: {
        [PartyTypes.DOG_PARTY]: 0,
        [PartyTypes.CAT_PARTY]: 0,
        [PartyTypes.DEMOCRAT_PARTY]: 0,
        [PartyTypes.REPUBLICAN_PARTY]: 0,
    },
};

This is still erroring out, because PartyTypes.DOG_PARTY can be any kind of AnimalTypes, so we cannot guarantee that votesByParty will have all of them. After all, this:

export const PartyTypes: Record<string, AnimalTypes> = {
    DOG_PARTY: 'cat',
    CAT_PARTY: 'cat',
    DEMOCRAT_PARTY: 'cat',
    REPUBLICAN_PARTY: 'cat',
};

is still valid.

Because we need to be sure that votesByParty have all the parties in it, we need to change:

export const PartyTypes = {
    DOG_PARTY: 'dog',
    CAT_PARTY: 'cat',
    DEMOCRAT_PARTY: 'donkey',
    REPUBLICAN_PARTY: 'elephant',
} as const;

unless you want to remove the requirement of having all the parties in the object, in that case:

interface ElectionState {
    votesByParty: Partial<Record<AnimalTypes, number>>;
}

Upvotes: 1

Related Questions