Published on

TypeScript: How to get type from an Array

Table of Contents

When working with TypeScript, you may come across a situation where you had an array list of possible values and wanted to convert them to a specific type. That would be useful to enforce a typed check in a function for example like getUsersbySubscription below.

So it might seem reasonable to manually create a union by re-using those values like type SubscriptionType.

Problem: Manual type creation to mirror arrays' values

ts-prob.tsx
  //long list maybe retrived from a DB
  const SUBSCRIPTION_TYPE_LIST = ['PREMIUM', 'CLASSIC', 'PLATINUM']

  //we need to re-write the lists containt twice manually here in order to create a type
  type SubscriptionType = 'PREMIUM' | 'CLASSIC' | 'PLATINUM'

  const getUsersbySubscription = (sub: SubscriptionType): number => {
    let users = 0
    switch (sub) {
      case 'PLATINUM':
        users = 50
        break
      case 'PREMIUM':
        users = 15
        break
      case 'CLASSIC':
        users = 5
        break
    }
    return 0
  }

  const subscriptionPlan1 = 'CLASSIC'
  const subscriptionPlan2 = 'GOLDEN'

  //we want to enforce type checking
  console.log(getUsersbySubscription(subscriptionPlan1)) // Logs: 5
  //console.log(getUsersbySubscription(subscriptionPlan2)) //🚫 (TS2345): Argument of type '"GOLDEN"' is not assignable to parameter of type 'SubscriptionType'.

Solution: Using const assertion

By using const assertion we ensure array literals become readonly tuples. So when we want to extract type from an array would be like:

without any type assertion we would get

ts-sol.tsx
//long list maybe retrived from a DB
const SUBSCRIPTION_TYPE_LIST = ['PREMIUM', 'CLASSIC', 'PLATINUM']

type SubscriptionType = typeof SUBSCRIPTION_TYPE_LIST // ❌ string []

But with const assertion only we are a step closer by not yet there:

ts-sol.tsx
//long list maybe retrived from a DB
const SUBSCRIPTION_TYPE_LIST = ['PREMIUM', 'CLASSIC', 'PLATINUM'] as const

type SubscriptionType = typeof SUBSCRIPTION_TYPE_LIST // ❌ readonly ["PREMIUM", "CLASSIC", "PLATINUM"]

Eventually we need to construct a type that assembles the union of those values and in order to fix the previous we need the array version of that typeof, so

ts-sol.tsx
//long list maybe retrived from a DB
const SUBSCRIPTION_TYPE_LIST = ['PREMIUM', 'CLASSIC', 'PLATINUM'] as const

type SubscriptionType = typeof SUBSCRIPTION_TYPE_LIST[number] // βœ… type SubscriptionType = "PREMIUM" | "CLASSIC" | "PLATINUM"

Finally, we can have our initial code re-written as follows:

ts-sol.tsx
  //long list maybe retrived from a DB
  const SUBSCRIPTION_TYPE_LIST = ['PREMIUM', 'CLASSIC', 'PLATINUM'] as const

  type SubscriptionType = typeof SUBSCRIPTION_TYPE_LIST[number] // βœ… type SubscriptionType = "PREMIUM" | "CLASSIC" | "PLATINUM"

  const getUsersbySubscription = (sub: SubscriptionType): number => {
    let users = 0
    switch (sub) {
      case 'PLATINUM':
        users = 50
        break
      case 'PREMIUM':
        users = 15
        break
      case 'CLASSIC':
        users = 5
        break
    }
    return 0
  }
  const subscriptionPlan1 = 'CLASSIC'
  const subscriptionPlan2 = 'GOLDEN'

  //we want to enforce type checking
  console.log(getUsersbySubscription(subscriptionPlan1)) // Logs: 5
  //console.log(getUsersbySubscription(subscriptionPlan2)) //🚫 (TS2345): Argument of type '"GOLDEN"' is not assignable to parameter of type 'SubscriptionType'.