This is not true. There are a number of ways that TypeScript's type-checking can be subverted at runtime, particularly when dealing with APIs that return JSON. You have to trust that the API has returned exactly what you are expecting, or write your own very detailed validation scripts. It's similar to the sorts of testing one would do in vanilla JavaScript without TypeScript, but now executing all the time at runtime.
As a developer, in the case of an API change that violated my assumptions, I would personally prefer my applications to fail-hard at the point of the API call, rather than to have my scripts run merrily on and only error in some other code far away from the root-cause when one of those assumptions fails.
However, I have a lot of hope that runtime type checking based on auto-code generation around TypeScript's interfaces could be developed in a future version of TypeScript.
This would be a big (but exciting) departure from the current TS goal of "Impose no runtime overhead on emitted programs." [1]
Recently, I've been using Zod [2] and find it to be a satisfying equivalent: you define a schema, and then you get both a TS type AND a JS parser/validator (which works as a TS typeguard).
TypeScript is an optionally typed language. It's core to the design of the language that the static type system is purely statically typed and doesn't come into play at runtime.
You could argue that that's not the best kind of language for users. I wouldn't disagree. I work on Dart which used to be optionally typed but now has a fully sound type system with runtime checks.
But that's orthogonal to whether browsers should support TypeScript directly. If they supported a statically typed language that had the runtime checks to be sound, that might be a great language, but it wouldn't be TypeScript.
I wouldn't say it's core to the design of the language. I'd say it's a key design decision for the development of the compiler, but those are two different things.
Also, I don't understand what you mean by "it wouldn't be TypeScript". Languages change. They change all the time. Did adding nullish coallescing before it became availalbe in JavaSciprt?
It's also not true that TypeScript is purely static and doesn't have any runtime component. There are a bunch of helper sort of functions that TypeScript can optionally include, so there is some precedent for having runtime-oriented code generated by TypeScript, rather than just eliding type information after successful static checking.
So perhaps there could be a syntax for imposing runtime-checking as an optional element. Something like:
interface Point {
x: number;
y: number;
}
async function getPointFromAPI(): Promise<Point> {
const request = await fetch("/api/points/current");
const point = await request.json<Checked<Point>>();
return check point;
}
Type `Checked<T>` would signal to the compiler that type information for T needs to be made available at runtime, and the `check` keyword would perform the check and "unwrap" the type to a bare reference to T.
I don't know what it would look like, but it would be a huge value add.
> I wouldn't say it's core to the design of the language.
It is absolutely core to the language. TypeScript's core value proposition is that you can take vanilla JavaScript and use it from TypeScript without any overhead, incrementally migrate to TS, or maintain a heterogeneous codebase as long as you want.
If you take away seamless zero-overhead JS interop, the language you have is radically different from TypeScript. To the degree that any language has any identity at all, that would be a pretty fundamental change in its identity. Like taking objects from Java or pointers from C.
To the best of my knowledge, no one has figured out how to have a language that allows mixing dynamic and static typing without either massive runtime overhead or giving up soundness. You basically have three options:
1. Allow dynamic types to flow into statically typed code
2. Soundness inside the statically typed code
3. Tolerable runtime overhead when using dynamic code from static code
But you only get to pick two. TypeScript, Dart 1.0 and other optionally typed languages give you 1 and 3 at the expense of 2. Dart 2.0 and other statically typed languages give you 2 and 3 at the expense of 1. Gradually typed languages like Typed Racket give you 1 and 2 at the expense of 3 (and are rarely used in practice because of it).
You're asking for TypeScript to just add 2. People have been trying to figure out how to get all three for decades but no one has succeeded yet [1].
No, I'm not. You're way overthinking this and your attitude is weirdly gatekeepy. I'm asking TypeScript to implement a shortcut feature for the tedious, boilerplate code we already write to use type guards to inspect objects from APIs before passing them on.
I'm not sure where the "gatekeeping" accusation comes from. In your original comment, you wrote:
> There are a number of ways that TypeScript's type-checking can be subverted at runtime, ... As a developer, in the case of an API change that violated my assumptions, I would personally prefer my applications to fail-hard at the point of the API call
I interpreted that to mean that you would prefer TypeScript's type system to be sound: If a function expects a Foo, you want a guarantee that you'll never get into the body of the function at runtime with an argument whose type isn't Foo.
I can understand that that seems like a fairly simple request. But when you dig into soundness, you discover that it is anything but. Optionally-typed languages like TypeScript are unsound by design because soundness is a difficult requirement with very severe trade-offs around interop, runtime performance, and usability. Making TypeScript sound would give you a language that felt very little like TypeScript does today.
As a developer, in the case of an API change that violated my assumptions, I would personally prefer my applications to fail-hard at the point of the API call, rather than to have my scripts run merrily on and only error in some other code far away from the root-cause when one of those assumptions fails.
However, I have a lot of hope that runtime type checking based on auto-code generation around TypeScript's interfaces could be developed in a future version of TypeScript.