I am trying to create a function like this:
function assert<T>(condition: T, msg?: string): T & asserts condition {
if (!condition) {
throw new Error(msg)
}
return condition
}
The goal is to return condition
unmodified, preserving its type, but also informing the compiler that the returned value is truthy. This would allow it to be used as a standalone statement, but also unobtrusively inside an expression:
const success: bool = true
// ... some code that might set success = false
assert(success)
const element: HTMLElement = assert(document.getElementById('foo'))
Unfortunately, the above syntax does not work (playground). Is there a way to express this in TypeScript?
I can't find any official documentation on assertion functions except the Release notes for TypeScript 3.7, and those only demonstrate assertion functions that implicitly return void
.
The best I can come up with is a workaround for object types only, because those can never be falsy:
function assert<T extends object>(condition: T | null | undefined, msg?: string): T {
if (!condition) {
throw new Error(msg)
}
return condition
}
It's hard to find because it's not explicitly in the docs, but we do have authoritative sources explicitly stating that assertion function implicitly return void
, so the answer is no, assertion functions cannot return a value.
An asserts return type predicate implies that the returned value is of type void, and there is no provision for returning values of other types.
Anders Hejlsberg
(this is straight from the horse's mouth; Anders Hejlsberg wrote the code so he knows!)
This is by design, according to the PR introducing this features:
A function call is analyzed as an assertion call or never-returning call when
- the call occurs as a top-level expression statement, and
- the call specifies a single identifier or a dotted sequence of identifiers for the function name, and
- each identifier in the function name references an entity with an explicit type, and
- the function name resolves to a function type with an asserts return type or an explicit never return type annotation.
The first item states that the call must occur as a top-level statement, this means that the call can't be nested in an expression (such as a parameter to another function or in an assignment). This means that effectively, even if you could return from an assertion function, using it in a position where the return value could be use, the function would not be analyzed as an assertion but rather as a regular function call. So it is best to implicitly just say that an assertion function returns void. This is also outlined in the PR:
An asserts return type predicate implies that the returned value is of type void, and there is no provision for returning values of other types.
Titian Cernicova-Dragomir
(Titian is a contributor
on the Typescript project)
Some examples of community sources stating that return types can not be specified for assertion functions.
Where type guards must return a boolean, assertion functions must return void
Stephan Meijer
5.2.2: Assertion signature: asserts «arg» is «type»
function assertIsString(value: unknown): asserts value is string { if (typeof value !== 'string') { throw new Error(); // assertion error } }
Assertion signature: asserts value is string
Result: void, exception
Axel Rauschmayer
Sadly, I could not find an authorative answer in the docs...
Talks about type assertions but does not mention assertion functions specifically.
Mentions assertion functions. Does not show any way to specify a return type, but also does not explicitly say they cannot have one.
Does not explicitly mention it. But does not give any examples of assertion functions that return a value. Guard functions always implicitly return a truthy or falsy (booleany?) value and it seems Assertion functions always implicitly return void
, but it's not said outright here.
Does not demonstrate any assertion functions that return anything, but again does also not explicitly state that it's not possible...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With