sanyooh
sanyooh

Reputation: 1631

Typescript: check if value is contained in type

I have problem with defined types and checking if a value is contained in that type.

Here is my example:

these are the types:

export type Key = 'features' | 'special';

export type TabTypes = 'info' | 'features' | 'special' | 'stars';

when the user changes a tab, it sends a string value from Type of TabTypes.

activeTabChanged(event: TabTypes) {
    this.activeTab: TabTypes = event;
    // it won't let me set the key here because key has a different type 
    // but the send event can be contained in type Key
    // how can I check if the send event from type TabTypes is contained in type Key
    this.key: Key = event;
}

is there a typescript way to check if a sent value with a type can equal a value from a different type?

Upvotes: 53

Views: 100289

Answers (6)

MikeT
MikeT

Reputation: 5500

most of the answer here assume you know what the union type is before hand so i will just cover the edge case wher the type is being determined Dynamically

export type DataKeysId = keyof typeof data;
export const Data: Record<DataKeysId , string> = data;

where data is a json file that has been imported in the form of { a:"123", b:"123", c:"123" }

so by running keyof typeof data we get a dynamic union type of "a"|"b"|"c"

however all other methods here would require the creation of an array ["a","b","c"] which would not be updated if you added d:"456" to the json file

a simple fix for this is to use Object.keys(data) to generate your dynamic value list to go with your dynamic type

Object.keys(data).include("d")

Upvotes: 0

jpthesolver2
jpthesolver2

Reputation: 1155

While Array.includes works, it has linear time complexity. Assuming that your list of tab types is unique (using a union type with repeated values wouldn't really make sense), it's faster to use a Set

const tabsSet = new Set(['info', 'features', 'special', 'stars'])
const isOfTypeTabs = (keyInput: string): boolean => tabsSet.has(keyInput)

console.log(isOfTypeTabs('info')) // true
console.log(isOfTypeTabs('foo')) /

Upvotes: -2

AntonOfTheWoods
AntonOfTheWoods

Reputation: 953

const TabValues = ['info', 'features', 'special', 'stars'] as const;
const TabValuesObj = TabValues.reduce(
  (acc, next) => ({ ...acc, [next]: null }),
  {} as Record<string, null>,
);
export type TabType = typeof TabValues[number];
export function isTabType(value: string): value is TabType {
  return value in TabValuesObj;
}

Here is an option that should also allow for very quick validation, even with large numbers of options in the type.

Upvotes: 3

Bruno Monteiro
Bruno Monteiro

Reputation: 4519

2019 Solution:

I had the same need and found an easier way to do that in another thread. In summary, what Patrick Roberts says in that link (updated with this question values) is:

Don't over-complicate it.

function isOfTypeTabs (keyInput: string): keyInput is TabTypes {
  return ['info', 'features', 'special', 'stars'].includes(keyInput);
}

See What does the `is` keyword do in typescript? for more information on why we don't just use a boolean return type.

Credits and full source here: https://stackoverflow.com/a/57065680/6080254

Upvotes: 31

Daniel Dror
Daniel Dror

Reputation: 2507

This answer to a similar question might be useful. it doesn't exactly answer your question, but it shows a similar way to achieve the desired result.

In short, you can use array for inclusion checks, and type for type safety:

const keys = <const> ['features','special'];
export type Key = typeof keys[number];
const tabTypes = <const> ['info' ,'features' ,'special', 'stars'];
export type TabTypes = typeof tabTypes[number];

activeTabChanged(event: TabTypes) {
    this.activeTab: TabTypes = event;
    // it won't let me set the key here because key has a different type 
    // but the send event can be contained in type Key
    // how can I check if the send event from type TabTypes is contained in type Key

    if (event in keys) {
        this.key: Key = event as Key;
    }
}

Upvotes: 2

kentor
kentor

Reputation: 18504

You could use a string enum.

export enum Keys = {
  Features = 'features',
  Special = 'special',
}

// Compare it
if (currentKey === Keys.Special) { console.log('Special key is set'); }

In order to check if your value is defined in the predefined Enum at all you can do:

if (currentKey in Keys) { console.log('valid key'); }

Upvotes: 6

Related Questions