Home > Software design >  How can you extract the inferred string literal types for a function signature?
How can you extract the inferred string literal types for a function signature?

Time:10-10

I'm trying to understand what it looks like to move from Enums to string literals.

One hanging up point is functions which take advantage of those keys.

For example, if I use enums, I might have something like:

enum Role {
  Standard = "Standard",
  Admin = "Admin",
}

type Standard = {
  role: Role.Standard
  name: string
  age: number
}

type Admin = {
  role: Role.Admin
  name: string
  securityLevel: "Normal" | "Elevated" | "High"
}

type Employee = Admin | Standard

Then, I write a helper function that does something based solely on the role:

function doSomething(role: Role){ /*...*/ )

In this case, I will almost surely be pulling the role from employee, but notably, I'm only passing a string into the function.

Is it possible to do this with string literals? The best I can get is to pass in an object that has the role on it:

type Standard = {
  role: "Standard"
  name: string
  age: number
}

type Admin = {
  role: "Admin"
  name: string
  securityLevel: "Normal" | "Elevated" | "High"
}

type Employee = Admin | Standard

function doSomething(e: Pick<Employee, "role">){
  return e.role === "Admin"
}

doSomething({role: "Admin"}) // compiles
doSomething({role: "Executive"}) // doesn't compile

I'm looking for the way to write it as:

doSomething("Admin") // compiles
doSomething("Executive") // doesn't compile

Alternatively, if this is just not something that's ever needed ... I'd like to understand a bit better - I'm fairly confident I've used Enums as types in functions before.

Update A possible alternative would be to somehow extract the string literal and then use that in the definition of the types that make up Employee, e.g.,

type RoleStrings = "Standard" | "Admin"
type Standard = {
  role: /* what goes here to say it's the string "Standard" that's _tied_ to the type `RoleStrings`? */
  name: string
  age: number
}

type Admin = {
  role: /* what goes here to say it's the string "Admin" that's _tied_ to the type `RoleStrings`? */
  name: string
  securityLevel: "Normal" | "Elevated" | "High"
}

If I could do that, then this becomes trivial:

function doSomething(role: RoleStrings){/*...*/)

Update 2

I think I have a solution that I like:

type RoleStrings = Employee['role']
function doSomething(role: RoleStrings){/*...*/}

This accomplishes my goal of deriving the allowable strings and ensuring that the for the function is as direct as possible without requiring an Employee (even if it's a Pick) object.

CodePudding user response:

You can use keyof typeof to get the type that represents the union of keys:

function doSomething(role: keyof typeof Role){ /*...*/ }

playground

If you don't want to use enum at all, then you can just use a union:

function doSomething(role: "Standard" | "Admin"){ /*...*/ }

playground

CodePudding user response:

Given:

type Standard = {
  role: "Standard"
  name: string
  age: number
}

type Admin = {
  role: "Admin"
  name: string
  securityLevel: "Normal" | "Elevated" | "High"
}

type Employee = Admin | Standard

The cleanest answer I've found is:

type RoleStrings = Employee['role']
function doSomething(role: RoleStrings){/*...*/}
  • Related