type-fest
type-fest copied to clipboard
Suggestion: `PropagateNever`
Proposal
A type PropagateNever
would allow for better API typing to indicate error conditions.
Namely, in the case where there is deeply nested never
in an object, the type would propagate that up to the top of said object. Thus, the branch would be removed from parent unions, or propagate to the root type itself.
Usage:
type A = PropagateNever<never> // Type: never
type C = PropagateNever<{ x: never }> // Type: never
type B = PropagateNever<[boolean, never]> // Type: never
type D = PropagateNever<string | number | { x: never }> // Type: string | number
A practical use-case:
In the type-fest type Jsonify
, a deeply nested bigint
value will only replace that value with never
. This is the expected behavior because Jsonify
is for type manipulation. However, if applied to a actual function, let's call it jsonify
, propagating the never
to the top would more clear to a user that the function will throw.
declare function jsonify<T>(value: T): Jsonify<T>;
declare function jsonifyStrict<T>(value: T): PropagateNever<Jsonify<T>>;
declare const input: { field: bigint };
const result1 = jsonify(input) // Type: { field: never }
const result2 = jsonifyStrict(input) // Type: never
Furthermore, typescript does not do analyze branches for deeply nested never
s, only top level ones. For example, with typescript compiler option noImplicitReturns
set to true, jsonify
gives an error in the following case, but jsonifyStrict
does not:
function f(x: boolean) {
if (x) {
return x;
}
jsonify({ x: 0n });
}
Implementation
I have not yet implemented this type, but expect it to be possible, and will update this comment when time allows me to do so.
Looks like a useful type to me. Can you think of any other use-cases for it?
I really like this idea, I see quite a few cases in internal type management, especially in those that are recursive. If you want I can try to do it!?
@skarab42 Go for it 👍