1

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!

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

3 Comments

This sounds like exactly what I want! I tried to implement it in my repository though, and I get this: Assertions require every name in the call target to be declared with an explicit type annotation. What am I missing?
I looked around and it seems related to the fact that my validateVariable function was built as an arrow function. I can work around that (wasn't using "this" there anyway), but is there a good reason why it doesn't work with arrow functions but it does with the function keyword?
Unfortunately this feature doesn't seem to be available until Typescript 3.7 and our repository is still on Typescript 3.6.3, so I can't use this. It seems to be the right way to do it though. I'll give you the green check, and let me know if you have a better option than mine for Typescript <= 3.6.3! Reference: typescriptlang.org/docs/handbook/release-notes/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.