How to do Typesafe JSON with fields of Enum type?

Typescript will not cast it for you in this case. You will need to typecast it yourself

type ColorType = 'red' | 'blue' | 'yellow';

type IJsonType = {"color" : ColorType}


let validJson = {
   "color": "red" as ColorType
  // or
  // "color": "red" as const
}


let invalidJson = {
   "color": "chartreuse"
}


const data1: IJsonType = validJson;  // this MUST NOT error

const data2: IJsonType = invalidJson;  

data1 now valid


It's currently impossible in Typescript. There is an open issue/proposal for that: https://github.com/microsoft/TypeScript/issues/32063 (and a few similar ones).


The easiest way is to use as const, but if you import json - then I'm not sure it's possible.

Also amakhrov pointed to the current TS issue: https://github.com/microsoft/TypeScript/issues/32063

Until the issue has been fixed / implemented you can write a type assertion function to validate the type, check verifyColor below. It takes an argument and asserts it's IJsonTypeWithEnum.

// helps to detect flexible objects
export const isObject = (value: unknown): value is {[key in keyof any]: unknown} => { 
    return typeof value === 'object' && value !== null;
}

// solves issues with for key of
export const objectKeys = <T extends object, K extends keyof T>(value: T): Array<K> => {
    return <Array<K>>Object.keys(value);
};

// THAT'S THE ANSWER
// verifies colors
const verifyColor = (value: unknown): value is IJsonTypeWithEnum => {
    if (!isObject(value)) { 
        return false;
    }
    for (const key of objectKeys(ColorType)) {
        if (ColorType[key] === value.color) {
            return true;
        }
    }
    return false;
};


const validJson: unknown = { "id": 3.14159 };
const invalidJson: unknown = { "id": "3.14159" };
const validJsonEnum: unknown = { "color": "red" };
const invalidJsonEnum: unknown = { "color": "chartreuse" };

enum ColorType {
    red = 'red',
    blue = 'blue',
    yellow = 'yellow',
}

type IJsonType = {"id": number}
type IJsonTypeWithEnum = { "color": ColorType }

// asserting type
if (verifyColor(validJsonEnum)) { // add || 1 to check failure
    const c: IJsonTypeWithEnum = validJsonEnum; // no error anymore
}

// asserting type
if (verifyColor(invalidJsonEnum)) { // add || 1 to check failure
    const d: IJsonTypeWithEnum = invalidJsonEnum  // no error anymore
}

Try to use type assertion

const data: IJsonType = jsonData as IJsonType;