validate
reflect
assert
unknown
TypeScript 3 adds an unknown type that must be inspected to be (safely) cast to another type. This manual type-checking code however is just boilerplate; it's easy to get wrong, easy to miss parts, and IMO adds absolutely no value for the programmer to have to type themselves. This boilerplate should be derivable for any non-function type, and several projects exist already that do this (or more); e.g., flow-runtime and io-ts.
This is a limited version of #1573 which suggested blanket runtime type-checking. I am suggesting a builtin operator like as that takes x: unknown as its first argument, a type T as its second, and produces x is T. Call this operator is, maybe.
Also desirable would be for error messages documenting why an unknown value does not validate; e.g. val does not have fields { x: number, y: number }. So the second part of this proposal is for a separate operator is! that is identical to is but throws a TypeError upon a type error.
I think that now that there is an unknown type, where its only use is for users to write mechanically derivable boilerplate to cast it to a known type, this is the logical next step.
I do not believe this is in opposition to the non-goal of adding/relying on runtime type-information. This only adds an extremely limited type reflection mechanism, motivated only by reducing boilerplate.
Any input data that's unknown; e.g. the output of a HTTP endpoint, or a websocket, some user input, JSON.parse, etc.
type Message
= { type: 'ping', time: number }
| { type: 'pong', time: number };
function onreceive(data: unknown) {
if (data is Message) {
...
}
}
should, roughly, generate
function onreceive(data) {
if (typeof data === 'object' && data.type === 'ping' && typeof data.time === 'number' ||
typeof data === 'object' && data.type === 'pong' && typeof data.time === 'number') {
...
}
}
Related #26064 #7481 #10421
I think the syntax might be against the design goals of having "just JavaScript" + types. But I guess your proposal could be turned into a quick fix for auto-generating type guards, like this:
```ts
type Message // hover here to auto-generate a typeguard
= { type: 'ping', time: number }
| { type: 'pong', time: number };
// this will get generated
function isMessage(arg: any): arg is Message {
return (typeof arg === 'object' && data.arg === 'ping' && typeof data.arg === 'number' ||
typeof data === 'object' && data.arg === 'pong' && typeof data.arg === 'number');
}
// so you can use it with an unknown variable:
function onreceive(data: unknown) {
if (isMessage(data)) {
// ...
}
}
As @AlCalzone noted, we do not intend to introduce new JS-expression syntax for type-only purposes. that would not be inline with our design goals.
Allowing some narrowing constructs to work on unknown to "prove" some aspects of the type is a valid option though. this is currently tracked by #25720, #10715, #25172, and #21732
@mhegazy TypeScript already has e.g. enums that mechanically generates JavaScript code that is a chore to write manually, but provides immense value. Given how boring and easy-to-mess-up the type-checking code actually is, I think it's at the same level of "tolerable design goal exception". If the problem is just that it's an expression, how about a new statement a la enum:
type Message = { ... };
typeguard isMessage(arg: unknown): arg is Message;
// function isMessage(arg:unknown): arg is Message { ... }
I just don't see what value is provided in having to write this stuff manually. Being able for tooling to generate the boilerplate like @AlCalzone suggests would be a decent and probably much less controversial solution.
For anyone finding this issue, this Stack Overflow thread has a few third-party tools that generate runtime checks based on interfaces. I don't know if any of the tools still work.
It's worth checking out typescript-is, which is a transformer that implements is.
This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
I also think this would be wonderful to have. This is one of the first problems you have to solve in most applications. TypeScript of course works wonderfully for type checking inside your own system. But in order to validate data coming in to your system you have to resort to using tools that either complicate your build process by adding some additional preprocessing step or using unofficial tools like typescript-is/ttypescript, or complicating the way you define your types such as with io-ts (which many times probably looks odd to many Typescript developers). Maybe the most likely approach is for the "custom transformers" to become more mature in Typescript?
Most helpful comment
@mhegazy TypeScript already has e.g. enums that mechanically generates JavaScript code that is a chore to write manually, but provides immense value. Given how boring and easy-to-mess-up the type-checking code actually is, I think it's at the same level of "tolerable design goal exception". If the problem is just that it's an expression, how about a new statement a la
enum:I just don't see what value is provided in having to write this stuff manually. Being able for tooling to generate the boilerplate like @AlCalzone suggests would be a decent and probably much less controversial solution.