Skip to content

Error type constructorΒ #63315

@masaeedu

Description

@masaeedu

πŸ” Search Terms

"error type", "conditional type fallback", "partial type constructor"

βœ… Viability Checklist

⭐ Suggestion

It would be useful for type level computations to be able to produce a type error.

A lot of TypeScript code uses the conditional types and inferred types features to perform computations on types. Type level computation itself however is not very strongly typed, since there is no kind system. This limits the extent to which we can make guarantees about the input of a given type-level function, which means we often accept overly wide input, parse it using conditional and inferred types, and then dispatch the fallback case of the conditional type (which one often expects or hopes never arises in practice) to some meaningless or dummy type, usually never.

It's worth noting that never is not the type level analogue of an error: it's a meaningful type in the type system, and term level use of a computed type that has strayed into a never-producing fallback case can sometimes escape detection and result in term level misuse.

I think it would be more honest for evaluation of partial type level functions to fail with an author-determined type error, rather than just picking an arbitrary (usually uninhabited type), and hoping that this will cause enough problems downstream to be noticed when we enter the fallback case.

Another use for the ability to explicitly construct type errors would be to allow library authors to replace generic type errors like subsumption failures (often on complicated types) with meaningful type errors relevant to users of the library.

πŸ“ƒ Motivating Example

Right now the library author writes:

type Source<S extends string> =  S extends `${infer A} -> ${infer B}` ? A : never

And hopes the user understands the type error arising from:

const a: Source<"A => B"> = A
// => Type '"a"' is not assignable to type 'never'.(2322)

Of course things can get even worse with: const f: (x: Source<"A => B">) => Whatever.

With this feature the library author would write:

type Source<S extends string> =  S extends `${infer A} -> ${infer B}` ? A : TypeError<`Expected a string of the form "_ -> _", got ${S}`>

and immediately get a type error:

const a: Source<"A => B"> = A
// => Expected a string of the form "_ -> _", got "A => B"

There are also motivating examples to be had for rewriting complicated subsumption type errors into errors legible in the business logic of a library, but not motivating enough for me to make one up right now (the semantics need to be worked out a bit too).

Also I'm aware that you can do something like "extends ${string} -> ${string}" in this simple example, but in general it is not always possible to constrain the input to make the fallback case of the conditional unreachable.

πŸ’» Use Cases

  1. What do you want to use this for?

Conditional and inferred types, library-specific custom error messages

  1. What shortcomings exist with current approaches?

Unexpected or accidental use of type functions implemented with the (semantically incorrect) fallback never can lead to term level misuse of the library and bugs.

  1. What workarounds are you using in the meantime?

Using extends clauses and excessive type parameterization heavily to make type-level functions as total as possible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions