Home > Software design >  TS: Type check for a deduced string which matches a type
TS: Type check for a deduced string which matches a type

Time:08-31

Let's say, I've:

type EventName = 'article:show' | 'article:hide';
const sendEvent = (eventName: EventName) => {
    return eventName;
}

const eventMap = {
    show: 'show',
    hide: 'hide'
};
const sampleEventName = `article:${eventMap.show}`;
sendEvent(sampleEventName);

Now I'm getting an error:

Argument of type 'string' is not assignable to parameter of type 'EventName'.

So can't TS figure out it's going to be a match with EventName? I know I can always cast string to EventName, but what's the best way here?

Live link: https://www.typescriptlang.org/play?ssl=12&ssc=1&pln=1&pc=1#code/C4TwDgpgBAogbhAdsAcgQwLbQLxQORoBOwAlgMYA2EAXAM4AWA9gO55QA RplN9JAEwh4A3ACgyjRLWBRaSAfCSzcACggJk6LNVibUmCAEoo2AHxQA3mKi2ohCMACuhRFA3LtEcQF8xEqRl3fQBZNDBTKxs7BhZdPFjWABpo234hePThMR9xSWlZWkwwKiUtQ0iAA25yKmoAEksPZDCwADpEn0rxeURFfVUijBKIMoMsI3EgA

CodePudding user response:

This transpiles successfully for me, using enums:

type EventName = 'article:show' | 'article:hide';
const sendEvent = (eventName: EventName) => {
    return eventName;
}

enum eventMap {
    show = 'show',
    hide = 'hide'
};

sendEvent(`article:${eventMap.show}`);

CodePudding user response:

You have to be a bit more specific when you define your two constants, otherwise, the type that's inferred by TS is very wide.

type EventName = 'article:show' | 'article:hide';

const sendEvent = (eventName: EventName) => {
    return eventName;
}

const eventMap = {
    show: 'show',
    hide: 'hide'
} as const; // <--- HERE
const sampleEventName = `article:${eventMap.show}` as const;  // <--- HERE

sendEvent(sampleEventName);  // <--- Now this works just fine

CodePudding user response:

You have to use the as const assertion. This tells TS that the types in the object are not a generic string, but instead a specific string. The same happens with the dynamic string that you are building.

type EventName = 'article:show' | 'article:hide';
const sendEvent = (eventName: EventName) => {
    return eventName;
}

const eventMap = {
    show: 'show',
    hide: 'hide'
} as const;
const sampleEventName = `article:${eventMap.show}` as const;

sendEvent(sampleEventName);

Playground

CodePudding user response:

You don't need to cast, some additional type declarations will do.

type EventMap = {
  [key: string]: 'show' | 'hide'
}

const eventMap: EventMap = {
    show: 'show',
    hide: 'hide'
};

const sampleEventName: EventName = `article:${eventMap.show}`;
sendEvent(sampleEventName);
  • Related