Reputation: 1954
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
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
.
Upvotes: 1