Repository navigation
Non‑void returning assertion functions #40562
Copy link
Copy link
Open
Labels
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript
Description
Search Terms
- assertion function non-void return type
- assertion function non-void return
- assertion function generic return
- assertion function custom return
Suggestion
A way to type a function that is both an assertion function and returning a value that is not void.
Use Cases
This is necessary to correctly type Jest’s expect(…) matchers, which have a generic return type of R.
Examples
declare const expect: <T>(actual: T): JestMatchers<T>;
type JestMatchers<T> = JestMatchersShape<
Matchers<void, T>,
Matchers<
Promise<void>,
T extends PromiseLike<infer U>
? U
: Exclude<T, PromiseLike<any>>
>
>;
type JestMatchersShape<TNonPromise extends {} = {}, TPromise extends {} = {}> = {
resolves: AndNot<TPromise>;
rejects: AndNot<TPromise>;
} & AndNot<TNonPromise>
type AndNot<T> = T & { not: T };
interface Matchers<R, T = {}> {
toBe<E>(expected: E): R & (asserts T is E);
}
declare const foo: unknown;
expect(foo).toBe("foo");
foo; // $ExpectType "foo"// Some typings for engine262's Completion Record handling:
type UnwrapNormalCompletion<T>
= unknown extends T ? Value | undefined
: T extends NormalCompletion<infer V>
? (unknown extends V ? Value | undefined : V)
: T extends Completion ? never
: T;
/** @see https://tc39.es/ecma262/#sec-returnifabrupt */
export declare function ReturnIfAbrupt<T>(completion: T):
(UnwrapNormalCompletion<T>)
& (asserts completion is UnwrapNormalCompletion<T>);
/** @see https://tc39.es/ecma262/#sec-returnifabrupt-shorthands */
export { ReturnIfAbrupt as Q };
/**
* The type signature is the same, but `AssertNormalCompletion` causes an error to be thrown at runtime
* if `argument` is an AbruptCompletion, whereas `ReturnIfAbrupt` gets replaced with code that causes
* the caller to return the AbruptCompletion by engine262's build system:
*
* @see https://tc39.es/ecma262/#sec-returnifabrupt
* @see https://tc39.es/ecma262/#sec-returnifabrupt-shorthands
*/
declare function AssertNormalCompletion<T>(completion: T):
(UnwrapNormalCompletion<T>)
& (asserts completion is UnwrapNormalCompletion<T>);
export { AssertNormalCompletion as X };Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
jcalz commentedon Sep 15, 2020
duplicate of or related to #34636
ttencate commentedon Jul 11, 2021
Note that the duplicate #34636 (which got here first) has 39 👍 at the time of writing, and this one has only 9 👍. If these are used for prioritization, perhaps this one should be closed and the other should be reopened?
RebeccaStevens commentedon Nov 30, 2021
Here's a simpler example that doesn't require any 3rd party libraries:
Playground link.
natew commentedon May 20, 2022
One footgun this avoids is it can avoid unused variables:
RebeccaStevens commentedon May 20, 2022
Nate Wienert (@natew) If you never use
val, why define it? just call the function.[-]Non‑void returning assertion functions[/-][+]Non‑`void` returning assertion functions[/+]natew commentedon May 21, 2022
irwincollin commentedon May 7, 2024
Would love for this feature to exist. Not only would it be useful for some mutating functions like
Object.assignand the jest functions above, but I think it also enables some novel compile-time safe patterns for mutable structures that aren't possible to represent currently.For example, a state machine that can assert that a transition is changing the underlying object to a new state, and return information about the transition itself.
Imagine this example:
ITenthusiasm commentedon Jun 7, 2024
I agree that this would be a helpful feature. In addition to the
Object.assign()scenario that Collin Irwin (@irwincollin) mentioned above, there may be situations where a developer wants to mutate an array in place in order to avoid the performance overhead that comes from methods likeArray.map. (Array.mapwould consume additional memory unnecessarily since it always creates a new array, and this can be problematic if the array is very large.)The problem with returning something like
T & U(likeObject.assign()) is that such a return type won't communicate that the original variable which was passed to the function was mutated. And the problem withassertsis that it can't be used to return the mutated object for convenience.A syntax like
could be helpful. I would hope that this would be fairly simple since TypeScript already has a way to
assertand a way to return generic types. We'd just be adding 2 new keywords, and it would look familiar to what we have in other situations:ITenthusiasm commentedon Jun 7, 2024
This seems to be related to #17181, though it comes from the opposite angle. A library that rigorously identifies its mutating functions in the way described above could satisfy some of the requirements of that issue if I'm understanding correctly. (I only skimmed the issue.)
mkantor commentedon Jan 22, 2026
This is also related to #22865.
artur-klesun-st commentedon Mar 29, 2026
Another extremely big use case is that this would finally open the door for mutable object argument type, a concept from Rust that makes typescript type system so much inferior...
I guess you can kind of emulate it with an extra output mutable variable, but it would be awfully awkward.