Patrick Da Silva
Patrick Da Silva

Reputation: 1954

Typescript and type inference: issue with validation

I currently have a function that does something similar to this (I wrote a minimal-working example for the sake of the discussion):

interface Variable {
  someMethod: () => void
}

const validateVariable(variable: Variable | undefined) {
  if(!variable) {
    throw new Error('Variable is undefined!')
  }
}

const doSomething = async (): void => { 
  // maybeGetSomething returns a Variable or undefined
  // depends on the database, so both cases are definitely possible
  const variable: (Variable | undefined) = await maybeGetSomething()

  validateVariable(variable)

  variable.someMethod()
}

but Typescript complains that variable might be undefined. I don't like the idea of putting the code of validateVariable in doSomething because in my case the validation is a non-trivial function I need to be able to re-use. It also feels stupid to define a new variable just so that Typescript doesn't complain about its type, since after validation it can only have the Variable type anyway (code can't make it through the line with validateVariable(variable) unless it hasn't thrown, in which case the variable has the appropriate type).

What would be a good way of doing this? I'm open to changing the structure of my code since I am still learning a lot about Typescript, I am flexible about that!

Upvotes: 1

Views: 808

Answers (1)

Nicholas Tower
Nicholas Tower

Reputation: 85102

By giving validateVariable a special return type, you can tell typescript that if the function returns (as opposed to throwing), then the variable must be defined:

function validateVariable(variable: Variable | undefined): asserts variable is Variable {
  if(!variable) {
    throw new Error('Variable is undefined!')
  }
}

If you do that, then after you call validateVariable(variable) the type on variable will be narrowed to just Variable.

Playground link

Upvotes: 1

Related Questions