Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type-level throw #26400

Closed
4 tasks done
bcherny opened this issue Aug 12, 2018 · 2 comments
Closed
4 tasks done

Type-level throw #26400

bcherny opened this issue Aug 12, 2018 · 2 comments

Comments

@bcherny
Copy link

bcherny commented Aug 12, 2018

Please close if dup - I didn't see this come up with search.

Search Terms

type, level, throw, exception, error, manual, compile, time

Suggestion

Type-level throw operator. Especially useful when combined with conditional types.

Use Cases

I define the following typesafe head function:

function head<T extends any[], R = T[0] extends never ? void : T[0]>(array: T): R {
  return array[0]
}

let a = head([1,2,3]) // number
let b = head([]) // void
let c = b + 5 // Error: Operator '+' cannot be applied to types 'void' and '5'.

I'd like to signal to the user that this is a compile error. I can do it with a hack like this:

declare class CantReadHeadOfEmptyArrayError {}

function head<T extends any[], R = T[0] extends never ? CantReadHeadOfEmptyArrayError : T[0]>(array: T): R {
  return array[0]
}

let a = head([1,2,3]) // number
let b = head([]) // CantReadHeadOfEmptyArrayError
let c = b + 5 // Error: Operator '+' cannot be applied to types 'CantReadHeadOfEmptyArrayError' and '5'.

The problems with this approach are:

  1. Returning the phantom CantReadHeadOfEmptyArrayError type is a hack.
  2. The error is delayed until I try to do something illegal with b.

My intention here is to throw, in order to signal to the user as early as possible that they're doing something illegal. It might look something like this:

function head<
  T extends any[],
  R = T[0] extends never
    ? throw 'Cant read head of empty array'
    : T[0]
>(array: T): R {
  return array[0]
}

let a = head([1,2,3]) // number
let b = head([]) // Error: Cant read head of empty array
let c = b + 5 // Error: Operator '+' cannot be applied to types 'never' and '5'.

This is a bit of a crazy idea, and I'm not sure how this jives with TS's design philosophy: on one hand, eager errors are helpful for debugging (the same way that mandatory function param types are); on the other hand, I'm not sure what other cases this might be useful for. Documenting this idea here to see if anyone has other use cases.

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. new expression-level syntax)
@yortus
Copy link
Contributor

yortus commented Aug 12, 2018

Covered by #23689?

@bcherny
Copy link
Author

bcherny commented Aug 12, 2018

That’s exactly it. Nice sleuthing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants