Simian
Simian

Reputation: 864

Validating a tuple

I have a type Job

const days = ['Monday', 'Wednesday'] as const
type Day = typeof days[number]

const isValidDay = (value: string): value is Day => value in days

const statuses = ['start', 'end'] as const
type Status = typeof statuses[number]

const isValidStatus = (value: string): value is Status => value in statuses

type Job = [Day, Status]

I want to write a function to validate an argument conforms to type Job

Given I will use this validator like so

const main = (value: string) => {
  const job = value.split('/')
  if (!isValidJob(job)) {
    throw new Error('Not a job')
  }

  // ...
}

I would think isValidJob should be implemented something like

const isValidJob = (value: Array<string>): value is Job => {
  return value.length === 2 && isValidDay(value[0]) && isValidStatus(value[1])
}

But the following does not flag up any issues even though this validator does not assert that value conforms to Job, nor will value[1] ever be a number

const isValidJob = (value: any): value is Job => {
  return typeof value[1] === 'number'
}

So my question is, how should I validate job is type Job?

Upvotes: 1

Views: 831

Answers (1)

lawrence-witt
lawrence-witt

Reputation: 9354

A function which returns a type predicate only requires that a boolean be returned at runtime. That's just how they work - you are asserting that the validation provided is correct for that particular type. Your initial implementation of isValidJob looks like it will do this just fine. You could add an Array.isArray check if you wanted to type the value argument as any or unknown.

However, I don't think the previous two guards are correct. The in operator returns true for object properties and array indexes, not for elements in arrays. For instance:

isValidDay("Monday") // false
isValidDay("length") // true
isValidDay(0) // fails TS check but returns true

You could handle this with the Array.some method instead:

const isValidDay = (value: string): value is Day => days.some(d => d === value);

Upvotes: 2

Related Questions