35

In React I can restrict a variable to subset of values, like

PropTypes.oneOf(['Home', 'About']),

How do I do that in TypeScript?

PS: I am not using TypeScript with React.

Yevhen Horbunkov
  • 14,965
  • 3
  • 20
  • 42
aWebDeveloper
  • 36,687
  • 39
  • 170
  • 242

4 Answers4

41

You can combine static strings (or any regular type) by defining a union type:

type SomeType = 'Home' | 'About';

Or within an interface:

interface SomeType {
  prop : 'Home' | 'About';
}

And of course you can combine other types as well:

type SomeType = string | boolean;
lumio
  • 7,428
  • 4
  • 40
  • 56
  • 4
    TypeScript's manual refers to these as [union types](https://www.typescriptlang.org/docs/handbook/advanced-types.html). – Joe Clay May 09 '18 at 08:48
  • What if the type is an interface itself. For example, `grid?: Grid;` and `export type Grid = { xs?: ColValueType[], sm?: ColValueType[], md?: ColValueType[], lg?: ColValueType[] }` – HalfWebDev Sep 06 '19 at 12:52
  • `keyof InterfaceType` – lumio Jun 19 '20 at 07:25
7

You can use enum.

Enums allow us to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases.

enum vs union-type

  • Union types are a compile time concept
  • Enums are real objects that exist at runtime
  • You can iterate over an enum
  • ... see this question

Example with enums:

enum PostStatus {
    DRAFT = "DRAFT",
    READY = "READY",
    PUBLISHED = "PUBLISHED",
}


class Post {
    constructor(private status: PostStatus) {
        this.status = status;
    }
}

const myPost = new Post(PostStatus.DRAFT);

console.log(myPost);

function doStuff(postStatus: PostStatus) {
    switch (postStatus) {
        case PostStatus.DRAFT: 
            console.log('Still working on it');
            break;
        case PostStatus.PUBLISHED:
            console.log('Done.');
        break;
        default:
            console.log('Other ...');
    }
}

Example with union type:

type PostStatus = "DRAFT" | "READY" | "PUBLISHED";


class Post {
    constructor(private status: PostStatus) {
        this.status = status;
    }

}

const myPost = new Post("DRAFT");
console.log(myPost);

function doStuff(postStatus: PostStatus) {
    switch (postStatus) {
        case "DRAFT": 
            console.log('Still working on it');
            break;
        case "PUBLISHED": 
            console.log('Done.');
            break;
        default:
            console.log('Other ...');
    }
}
Mohamed Ramrami
  • 12,026
  • 4
  • 33
  • 49
6

Only one type among several one is a union type, and in your case a union of string literal.

You can convert an array of string literals into a union of string literals as follow:

If you do have a const array or string you can define a type:

const menuList = ["Home", "About"] as const;
type menuName = typeof menuList[number] // "Home" | "About"

If you do already have a type with the array just do:

type menuList = ["Home", "About"];
type menuItem = menuList[number] // "Home" | "About"
Flavien Volken
  • 19,196
  • 12
  • 100
  • 133
0

Here is a solution using an enum type, react proptypes and typescript props

export enum AppPage {
  Home = 'Home',
  About = 'About',
}
export MyComponentProps = {
  page: AppPage
}
export MyComponentPropTypes = {
  page: PropTypes.oneOf<AppPage>(Object.values(AppPage)).isRequired
}

export MyComponent = (props:MyComponentProps) => {
  return <div></div>
}

MyComponent.propTypes = MyComponentPropTypes
useless
  • 1,876
  • 17
  • 18