DEV Community

ben hultin
ben hultin

Posted on • Edited on

TS: Gain Control of Primitive String Types

Let's say you know what kind of values you expect to receive for a certain property / parameter, you want to lock down that property so it will only accept certain values. There are a number of ways we can do that with Typescript. This can be achieved simply using pipe (|) as in 'cat' | 'dog'.

const PET_TYPES = {
  cat: 'cat',
  dog: 'dog'
}

interface Pet {
  species: 'cat' | 'dog'
}

const myPet: PET = {
  // incompatible types, type string does not match type 'cat' | 'dog'
  species: PET_TYPES.cat, 
  // valid
  species: 'cat'  
}
Enter fullscreen mode Exit fullscreen mode

We want to be able to use PET_TYPES though in our object and interface. Let us make use of 'as const' after PET_TYPES to make the properties 'readonly'.

const PET_TYPES = {
  cat: 'cat',
  dog: 'dog'
} as const

interface Pet {
  species: keyof typeof PET_TYPES;
}

const myPet: PET = {
  // valid
  species: PET_TYPES.cat 
}
Enter fullscreen mode Exit fullscreen mode

There are some other ways we can also defining string values as well.

type PetType = 'cat' | 'dog';

interface Pet {
  species: PetType;
}

const myPet: PET = {
  // this is not valid as we are trying to use PetType like a value, but it is a type
  species: PetType.cat
  // valid
  species: 'cat'
}
Enter fullscreen mode Exit fullscreen mode

By doing these approaches we are give more definition to our string values when we know that a property will be one of only a few different values. When making use of 'as const' on the of our constant objects then we make the properties within all readonly which allows the Typescript compiler to treat 'cat' and 'dog' as well read-only and thus not a type string.

The major benefit of moving the definition of 'cat' and 'dog' value to PET_TYPE is we are now able to make the values for re-use and making changing of the values into one place in our app which is DRY (Don't Repeat Yourself).

Thanks for reading!

Top comments (0)