Gaurav Soni
Gaurav Soni

Reputation: 411

Declaring object type in typescript

Need to declare a object type. Getting below mentioned error:

 type CarKeys = "mercedes" | "mercedes-sedan" | "mercedes-hatch";

    interface Car { color: string; } 

    const KEY_MAP = { mercedes: 'mercedes', mercedes_sedan: 'mercedes-sedan', mercedes_hatch: 'mercedes-hatch'};

        const carType:Record<CarKeys, Car> = {
         [KEY_MAP.mercedes]:{ color: 'red'},
         [KEY_MAP.mercedes_sedan]: {color: 'yellow'},
         [KEY_MAP.mercedes_hatch]: {color: 'black'}
        };

TypeScript is throwing following error :

Type'{[x: string]: { color: string }}' is missing the following properties from type 'Record': mercedes, mercedes-sedan, mercedes-hatch`

Upvotes: 4

Views: 2462

Answers (3)

Maciej Sikora
Maciej Sikora

Reputation: 20162

First of all when using TypeScript you don't need to use Key -> Value map + String literal types. You can choose one of both, as it will ensure the type safety. Also approach with using Enum instead of standard JS object will be more appropriate as it will solve and the value and the type in one language construct. Consider the solution with using Enum only:

interface Car { color: string; } 

enum KEY_MAP {
    mercedes = 'mercedes',
    mercedes_sedan = 'mercedes_sedan',
    mercedes_hatch = 'mercedes_hatch'
}

const carType: Record<KEY_MAP, Car> = {
    [KEY_MAP.mercedes]:{ color: 'red'},
    [KEY_MAP.mercedes_sedan]: {color: 'yellow'},
    [KEY_MAP.mercedes_hatch]: {color: 'black'}
}

Also solution with only string literal is fully valid and type safe. Consider:

type CarKeys = "mercedes" | "mercedes_sedan" | "mercedes_hatch";

interface Car { color: string; } 

const carType: Record<CarKeys, Car> = {
    mercedes:{ color: 'red'},
    mercedes_sedan: {color: 'yellow'},
    mercedes_hatch: {color: 'black'}
} // carType cannot be created with wrong key or value type

String literal type should not be considered as 'magic string' as it is watched by compiler and type safe. We don't need to create some middle structure to use it directly.

And last not least. You can fix your implementation directly by specifing correct type for KEY_MAP.

type CarKeys = "mercedes" | "mercedes_sedan" | "mercedes_hatch";

interface Car { color: string; } 

const KEY_MAP: { [P in CarKeys]: P } = { mercedes: 'mercedes', mercedes_sedan: 'mercedes_sedan', mercedes_hatch: 'mercedes_hatch'};

const carType:Record<CarKeys, Car> = {
    [KEY_MAP.mercedes]:{ color: 'red'},
    [KEY_MAP.mercedes_sedan]: {color: 'yellow'},
    [KEY_MAP.mercedes_hatch]: {color: 'black'}
}; // no issues

The core thing is { [P in CarKeys]: P }. We are saying that our object has exact single item from CarKeys as a key and as a value.

Upvotes: 3

brunnerh
brunnerh

Reputation: 185445

There is an error in your code, the values in KEY_MAP do not actually match the values specified in CarKeys. Also, the semi-colons in carType need to be commas.

To fix the problem at hand you can use as const. In your code the resolved type of KEY_MAP is:

const KEY_MAP: {
    mercedes: string;
    mercedes_sedan: string;
    mercedes_hatch: string;
}

Specify it like this:

const KEY_MAP = {
    mercedes: 'mercedes',
    mercedes_sedan: 'mercedes_sedan',
    mercedes_hatch: 'mercedes_hatch'
} as const;

Then the resolved type will be:

const KEY_MAP: {
    readonly mercedes: "mercedes";
    readonly mercedes_sedan: "mercedes_sedan";
    readonly mercedes_hatch: "mercedes_hatch";
}

Which should compile.

Upvotes: 3

hackape
hackape

Reputation: 19997

Two things, first you wrote type CarKeys = "mercedes" | "mercedes_sedan" | "mercedes_hatch", all with underscore but values in KEY_MAP have hyphen, that's a mismatch you might want to fix.

Second, by default TS will infer typeof KEY_MAP to be { mercedes: string; ... } instead of { mercedes: "mercedes", ... }.

This means typeof [KEY_MAP.mercedes] is actually string, not the "mercedes" string literal, thus typeof carType is actually { [key: string]: Car }, that's why the error.

To fix it, you can add as const to the end of KEY_MAP:

const KEY_MAP = {
  mercedes: 'mercedes',
  mercedes_sedan: 'mercedes-sedan',
  mercedes_hatch: 'mercedes-hatch'
} as const;

Or, you can turn it to a enum

enum KEY_MAP {
  mercedes = 'mercedes',
  mercedes_sedan = 'mercedes-sedan',
  mercedes_hatch ='mercedes-hatch'
}

Upvotes: 5

Related Questions