(Post-)Kantian epistemology/metaphysics is about trying to see what we can know of the structure and behavior of the world prior to (without) making empirical reference to it. Intuitively, the distinction between static analysis and dynamic analysis seems to parallel the relationship between rationalist/idealist/a priori epistemology and empiricist/materialist epistemology.
Hegelian philosophy has also been taken up by some mathematicians and 'formalized' in terms of type theory and category theory. Lawvere did some work on 'categorifications' of Marx and Hegel, and I guess some HoTT people have also recently taken an interest in Hegel. You can find a little bit of background and references to other sources here: https://ncatlab.org/nlab/show/Hegel%27s+%22Logic%22+as+Modal...
A huge pet peeve of mine is libraries appropriate existing topics as their name.
Eleventy is a great name. When I search it I get nearly 100% Eleventy related results. Hugo is a not great name; if I search it and I get Hugo the movie, Hugo boss the fashion line, Hugo insurance company, etc. Next.js is an okay name, at least they explicitly include .js in the name and branding and all over their website to make it a bit more notable and manageable.
Hegel is not a great name IMO. The search space is dominated by the well renowned philosopher, and if the library does get very popular, it begins to take away from what people are usually searching for with "Hegel". Hegel.js would be a slight upgrade, but it is only referred to as "Hegel" in the main repo and website.
I understand why marketing would insist, but "Hegel attempts to prevent runtime TypeErrors with a strong type system" sounds like "we haven't been paying attention to the JS landscape for the last few years and didn't know TypeScript already existed"? And if it's just a better TS tool chain... that'd be worth calling out.
const numbers: Array<number> = [];
// HegelError: Type "Array<number>" is incompatible with type "Array<number | string>"
const numbersOrStrings: Array<string | number> = numbers;
The very first example is already an odd choice, given that <number> is type-compatible with <string|number>. Errors should be flagged once we try to call functions on the elements, with calls on number[...] validated based on <number>, and calls on numbersOrStrings[...] validated based on the shared API between <string> and <number> (which is a tiny API surface, but there is one).
numbersOrStrings[1] = "Hello, TypeError!";
Why is this not an error based on the incorrect assignment we already had? "HegelError: cannot assign to alias numberOfStrings of numbers, numbers is of type <number>".
// HegelError: Property "toFixed" does not exist in "Number | undefined"
numbers[1].toFixed(1);
Heck even this error is weird: we declared numbers as an array of <number>, not an array of <number|undefined>. If you're going to be that strict, track list sizes and go "HegelError: Property numbers[1] is undefined, which is type-incompatible with <number>".
It's because it is not proven that element with the index 1 exists. Even TypeScript enforces bounds checking these days.
As for the first one - as i understand, this will spoil the original array, as it is assigned by reference; so by writing the string into `numbersOrStrings`, you also write it into `numbers`, thus violating the type.
The line says no such thing, in fact it says the opposite. From the project page on GitHub, Hegel wants to have zero runtime type checking and have it all be proved statically at compile time.
Then what does "type safety at runtime" even mean? TypeScript is surely also type safe at runtime then, even if the type system is unsound: that is, after all, its whole value proposition.
I noticed that the syntax choices differed ever so slightly from Typescript - given the project is 4 years old & TS is 10, this seemed an odd choice for some of the more arbitrary syntax decisions.
But then I realised it seems to follow Facebook's flow syntax a little more closely. I wonder if there's any shared heritage.
The syntax is simpler. Less operators and keywords. That has benefits for the implementation, and I think better compatibility with the type annotations proposal for ecmascript. It might also be easier to learn if you aren't already familiar with typescript.
Interesting, we’re the opposite: use enums where possible. We think it adds more clarity to the reader.
Besides, sometimes number values make more sense then string values. Especially when you want to do bitwise operations, which are not possible on string unions.
That's an interesting point about using number values- I have to say I've never needed to do a bitwise operation in JavaScript, though I know some high-performance projects like physics engines and the TypeScript compiler itself use them
Still, I'd prefer:
const MyNumericEnum = {
One: 0b001,
Two: 0b010,
Three: 0b100
} as const
type MyNumericEnum = typeof MyNumericEnum[keyof typeof MyNumericEnum]
Just for explicitness. But that's my subjective opinion
Enums aren't simply strings, though they can be reified as such in JS if you assign string values to them. It's more appropriate to think of them as symbols (from a type perspective)- seeing `Direction.North` is a whole lot more semantically meaningful than the string "north" in code.
More than that, refactoring tooling Just Works with enums. On the other hand, if you're passing strings around everywhere and attempt to refactor a type that is a union of strings, there's plenty of cases where refactoring tooling can't help you, and you get to manually clean up the errors.
> seeing `Direction.North` is a whole lot more semantically meaningful than the string "north" in code
This is subjective, so we can agree to disagree, but personally assuming I have typechecking/autocomplete I really prefer knowing exactly what I have (a string), and not hiding it behind an abstraction
And, if you ever just want the values enumerated for whatever reason, you can always do it yourself explicitly:
const Direction = {
North: 'North',
South: 'South',
East: 'East',
West: 'West'
} as const
type Direction = keyof typeof Direction
Compared with the above, the special enum syntax just creates unnecessary magic in my opinion; it even breaks TypeScript's rule of not having any runtime footprint
> More than that, refactoring tooling Just Works with enums
1. When I typed open-quote in front of `=`, it intellisensed/autocompleted on the possible strings
2. I was able to right-click 'A' in the type definition, click "Rename...", and then it updated both the type and the 'A' literal value with the new value
type MyEnum = 'a' | 'b' | 'c';
function test(val: MyEnum) {
return val;
}
// refactors
test('a');
// does not refactor
const testVal = 'a';
test(testVal);
The simple fact is that- as far as the type system is concerned, enums are more than simple strings, and they are useful for those differences.
As for the readability, it gets a little more interesting when you are interacting with libraries or APIs outside of your control; you end up with hyphenated things, underscored, and other oddities that may not be a one-to-one match between a logical, easily readable key and the underlying value it represents.
Sure- the auto-refactor won't work here, only the typecheck, and that's the desired behavior in this case because the intended use of 'a' is not necessarily known at that point in the code. Which is the point you're making I guess, though I have to say, in practice this is an unusual case that I never really see; usually if I'm creating an enum-string it's directly in a function call, typed object literal, etc.
> as far as the type system is concerned, enums are more than simple strings
enum MyEnum {
Foo,
Bar
}
function test(val: MyEnum) {
return val;
}
test(MyEnum.Foo)
test('Foo')
I was surprised to find you were right about this; I expected the above to pass checks, but it doesn't. TypeScript actually does treat the enum as more than just its underlying value. So that's interesting- and that's the first reason I've really seen to use these
But I think I still prefer plain values, for myself. There's just something that feels right and pure about using plain JSON-like JavaScript values, and then overlaying types on top of them that have no effect on their behavior and don't obscure their underlying representation. You know exactly what you've got, it's maximally duck-typeable, it's unchanged when it gets (de)serialized, it all just feels better to me
>though I have to say, in practice this is an unusual case that I never really see
They happen once in a blue moon and when they do, you hate your life for a day. Maybe a week.
Plain values also have the problem of losing context. This primarily hits those who don't know the code. That's the whole selling point of OOB and related abstractions, anyway.
Both of the above happening in tandem isn't that rare and a realistic risk factor for big projects.
I'm not sure why I would? TypeScript would still lead me straight to the problem via type incompatibilities; it just wouldn't perform the fix for me automatically in this one specific case
Here, TypeScript infers that it is of type "string", and knows the value, so it is perfectly happy accepting it to ALSO be of type MyEnum. This is the problem- refactoring MyEnum means that testVal is not automatically updated, because the type is really "string" that just-so-happened to duck-type into MyEnum at the function call site.
On the other hand, if MyEnum were actually an enum rather than a union of strings, this problem wouldn't have happened.
I feel like if you're going to use Typescript, you should accept that you're really just using Javascript with extra steps. If you want to use a language with enums, use one - there are many - but none of them are Javascript.
...I think that's the point of Typescript, no? Being able to write in almost-Javascript with one extra step (that most IDEs take care of automatically anyway) is still nicer than having to learn an entirely different language and toolchain that then compiles down to JS in the end.
For web dev, having a single language everywhere is really nice.
I think the problem trying to build a new type system for JS in the current ecosystem is that so much of the value of the current solutions are provided by their deep IDE integration.
Having a great type checker is great, but if I don’t get any of the IDE functionality etc, it’s losing half the value.
What's really missing is a seamless ability to add runtime checks to TypeScript. Otherwise, between Flowtype and TypeScript, static type checking is a solved problem in JS.
Hegel is basically a less mature version of Flow. I preferred Flow over TS for the longest time, but let's face it – TS won in terms of absolute adoption, with very little prospect for anyone to take over.
It is! Zod is great. I don’t mean to undersell its value. But it’s more of a reference manual of how to effectively use the language features it uses, than something novel filling in gaps in the language.
I don't understand what this means. Any sufficiently valuable general purpose library (which zod is) is a candidate for first-class inclusion in the language if it is useful for a sizeable majority of the language's audience.
Zod is filling a gap in the language - runtime type checking. It is not the only one doing so, but the fact that there are several such solutions clearly indicates that there is a general need for it.
> I don't understand what this means. Any sufficiently valuable general purpose library (which zod is) is a candidate for first-class inclusion in the language if it is useful for a sizeable majority of the language's audience.
The language doesn’t have, or at least makes not having a primary goal, any runtime. “The” language is JavaScript. TypeScript is just annotations of it. Really good annotations. So good that you can build them out of reliable runtime checks which are type guards, without even writing a single type definition except to infer from them. Zod is filling a gap in JavaScript. TypeScript is facilitating that.
However, what is or isn't the goal of a language is subject to change. Libraries like Zod are trying to filling a gap in js, sure. But we are arguing that that gap is better filled by the typescript - if that requires expanding its goal or doing things that don't fit in current scope - so be it.
As to why it is better: Currently using zod requires that the author of the types uses zod's API. If they didn't anticipate that the types they are writing would need runtime checks, or prefer some other schema validation library than the one you prefer - as a consumer you need to now redefine all of these types using zod and keep them in sync. Compiler can help, sure but its still quite some work.
If typescript supported this natively, you could import some random library and use its types for runtime validation - that is not possible today, and it can't be possible until
ts has first class runtime type validation.
People would also not have to learn the ts syntax and then also the zod api to achieve it.
Languages like dart demonstrate that this is practical.
Fp-ts adds some amazing functional tools. Io-ts, which is built off of it, adds great run time type validation.
I can define types using io-ts, infer a true typescript type from that to put in my d.ts files to get full ts type checking, and also have run time type checks, all from the same single definition.
The initial learning curve is admittedly a little steep, but once you have it down it's a breeze to use, and delightful.
https://deepkit.io/ may be of interest to you! It deeply patches the TS type compiler to make all types visible at runtime, enabling a lot of annotation-style workflows and dependency injection possible completely within the type annotation system: https://docs.deepkit.io/english/runtime-types.html
For a less intrusive solution, https://github.com/jquense/yup is a great library to reach for whenever you're defining the shape of a network-transmitted object and don't want to introduce compilation stages.
There’s a seamless ability to add runtime checks in TypeScript and has been for a long time: type guards. It’s not built into the type system because no one actually wants that in TS or any other language, even when they think they do, at least without control over when it’s used. That is type guards.
What everyone who asks for runtime checking actually wants is:
- define once
- declaratively
- with clear validation and deserialization at boundaries where data types aren’t known
- without overhead for internal types which are well known
- without an additional build step like codegen
That is type guards! They just have to be granular and composable, and well tested at a granular and composable level. Libraries like zod and io-ts have shown how to do it. You can just use them, or many others like them, you don’t need anything new. Or you can pretty trivially build your own custom solution if they don’t scratch your particular itch.
You aren’t missing seamless runtime checks. You’re just not using them.
The GPs request is a very fair one. Typeguards are 10% of starting the solution.
What everyone wants: Autogenerated type guards by class.
What everyone gets: “Remains out of scope.” and need to use other libraries. The compiler can do this significantly better (more ergonomic, less time, etc) than 3rd party libs. TS devs really should reconsider this feature.
> What everyone wants: Autogenerated type guards by class.
I’m quite sure people are asking for that, but I’m quite sure they’d regret wanting it if they got it. To the extent it’s desirable, it’s a thing that should be in TC39, not in TypeScript. The wailing and gnashing of teeth over when or whether or how type coercions happen is already mythical for the underlying language. Adding it to a compiler that’s increasingly not used to compile but only to type check is gonna add ten years of gripes about JS tooling nonsense to every single discussion.
They have considered this feature. Type guards are the solution and they’re a really good solution given the circumstances.
Flow had [flow-runtime](https://github.com/gajus/flow-runtime), which was nice. There were/are a few half-baked solutions in TS ecosystem, but nothing as mature as flow-runtime was. However, I wish that TS supported this natively.
Hmm, I was reading the doc[1], and it seems like type guards aren't run-time checks though; they're a way to write a function that checks a given type and then narrows that type to another more specific type.
I think what OP might've meant by run-time checks is auto-generating code that wraps every unpredictable/non-guaranteed boundary with a run-time type check. So if you have some data with the "any" type data being turned into a type or a use of the "as" keyword (e.g. `someType as anotherType`), then add a run-time check that throws an error at that boundary (instead of the code erroring out elsewhere, or resulting in incorrect behavior). Something like
> Hmm, I was reading the doc[1], and it seems like type guards aren't run-time checks though; they're a way to write a function that checks a given type and then narrows that type to another more specific type.
That’s a runtime type check! Any equivalent you might bake into TypeScript would do the exact same thing, but it would do it rigidly in ways people don’t expect (being strict about input types, ie “do what I say not what I mean) or rigidly in ways people don’t expect (“do what I mean as long as you mean exactly what we allow”). Again, fine grained type guards as implemented by zod/io-ts/etc solve this problem and either give you all you need already or demonstrate exactly how you can use existing language features to achieve exactly what OP and anyone else asking for runtime types wants.
Type guards in TypeScript are not sound: the language makes no effort to prevent you from defining an incorrect type guard that, when used, narrows your variables to incorrect types. Your mistake won’t be caught at compile or at runtime (until the variable is actually used in a way that’s incompatible with its declared type, which may be much later).
Type guards (X is number) are different from type assertions (X as number). The former are user defined runtime checks - if they are not correct, then the developer who wrote them is at fault. Moreover, they are typechecked, so you couldn't have a type guard that claims to return a number but actually returns an array, at least not without bypassing the Typescript checker some other way.
Type assertions match what you describe better - they are ways to override the compiler's opinion about the type of a variable or expression. They are, as you say, unsafe in the sense that the compiler has no way of knowing if this assertion is true or not.
> they are typechecked, so you couldn't have a type guard that claims to return a number but actually returns an array, at least not without bypassing the Typescript checker some other way
Good point, I wasn't 100% sure on that and should have checked myself. I'd say there's still a lot of value in having type guards as explicit places that can be heavily checked, e.g. with unit tests, to form strong boundaries to the rest of the code you right.
Type guards are the primitives one needs to satisfy the compiler. No one wants to hand roll a JSON parser using those type guards. Yes, we have libraries like io-ts and zod. Having all those different libraries means my own library may export zod schema’s while your application uses runtypes.
I understand that adding something like zod is out of scope for tsc. However, type guards alone are not the full answer either.
There is a typescript library called runtypes which offers really great runtime type checking. It’s a vanilla TypeScript library, so it doesn’t require any modifications to your tool chain. It provides library functions for defining types that are very similar to how you’d define the types in typescript. Once you define the types via library functions, the library has offers access to the underlying TypeScript type using typeof and also provides checked JSON deserialization methods.
Yes, runtypes is not the only option. But the point is that TypeScript’s type system is powerful enough to implement runtime type checking. I’m not sure what the benefit would be to adding the concept as a first-class citizen in the language.
If the typescript compiler included hygienic macros then you could implement something like Rust's serde crate. Once you have that, then you can write normal looking typescript interfaces paired with a macro that implements runtime parsing of an unknown value. For example:
import macro { Deserialize } from 'ts-serde'; // I made up "import macro" syntax
import { parse } from 'ts-serde';
#[Deserialize] // I borrowed this syntax from Rust
interface Foo {
bar: string;
baz: number;
qux: Record<string, string | null>;
}
Now I can do something like:
const response = await fetch('https://www.example.com');
const foo = parse<Foo>(await response.json());
This is a much nicer typescript world to live in. I can use vanilla interfaces (or even types). I do not have to learn a separate syntax for runtime checks (such as zod, io-ts, runtypes, etc). If I want to switch from one macro implementation, my scope of changes is the annotation on the interface and the "parse" function. That is much, much easier than switching from io-ts to zod.
I wonder if this will be something that functional libraries like Ramda [1] or Sanctuary [2] will be able to benefit from.
One of the reasons these libraries don't work so well with TS is that it doesn't have ML-style whole program inference and hence doesn't work so well with patterns like currying. Hegel seems more capable in that regard.
77 comments
[ 0.22 ms ] story [ 149 ms ] thread(Post-)Kantian epistemology/metaphysics is about trying to see what we can know of the structure and behavior of the world prior to (without) making empirical reference to it. Intuitively, the distinction between static analysis and dynamic analysis seems to parallel the relationship between rationalist/idealist/a priori epistemology and empiricist/materialist epistemology.
Hegelian philosophy has also been taken up by some mathematicians and 'formalized' in terms of type theory and category theory. Lawvere did some work on 'categorifications' of Marx and Hegel, and I guess some HoTT people have also recently taken an interest in Hegel. You can find a little bit of background and references to other sources here: https://ncatlab.org/nlab/show/Hegel%27s+%22Logic%22+as+Modal...
Eleventy is a great name. When I search it I get nearly 100% Eleventy related results. Hugo is a not great name; if I search it and I get Hugo the movie, Hugo boss the fashion line, Hugo insurance company, etc. Next.js is an okay name, at least they explicitly include .js in the name and branding and all over their website to make it a bit more notable and manageable.
Hegel is not a great name IMO. The search space is dominated by the well renowned philosopher, and if the library does get very popular, it begins to take away from what people are usually searching for with "Hegel". Hegel.js would be a slight upgrade, but it is only referred to as "Hegel" in the main repo and website.
As for the first one - as i understand, this will spoil the original array, as it is assigned by reference; so by writing the string into `numbersOrStrings`, you also write it into `numbers`, thus violating the type.
Turns out Hegel makes it a goal to be provably correct in respect to types, whereas TypeScript makes tradeoffs for productivity.
But then I realised it seems to follow Facebook's flow syntax a little more closely. I wonder if there's any shared heritage.
Not sure if this provides me enough benefit to move from TS. On the contrary it removes “non JS” features like enums. I can’t live without enums..
Besides, sometimes number values make more sense then string values. Especially when you want to do bitwise operations, which are not possible on string unions.
Still, I'd prefer:
Just for explicitness. But that's my subjective opinionMore than that, refactoring tooling Just Works with enums. On the other hand, if you're passing strings around everywhere and attempt to refactor a type that is a union of strings, there's plenty of cases where refactoring tooling can't help you, and you get to manually clean up the errors.
This is subjective, so we can agree to disagree, but personally assuming I have typechecking/autocomplete I really prefer knowing exactly what I have (a string), and not hiding it behind an abstraction
And, if you ever just want the values enumerated for whatever reason, you can always do it yourself explicitly:
Compared with the above, the special enum syntax just creates unnecessary magic in my opinion; it even breaks TypeScript's rule of not having any runtime footprint> More than that, refactoring tooling Just Works with enums
I just entered the following in my editor:
1. When I typed open-quote in front of `=`, it intellisensed/autocompleted on the possible strings2. I was able to right-click 'A' in the type definition, click "Rename...", and then it updated both the type and the 'A' literal value with the new value
As for the readability, it gets a little more interesting when you are interacting with libraries or APIs outside of your control; you end up with hyphenated things, underscored, and other oddities that may not be a one-to-one match between a logical, easily readable key and the underlying value it represents.
Sure- the auto-refactor won't work here, only the typecheck, and that's the desired behavior in this case because the intended use of 'a' is not necessarily known at that point in the code. Which is the point you're making I guess, though I have to say, in practice this is an unusual case that I never really see; usually if I'm creating an enum-string it's directly in a function call, typed object literal, etc.
> as far as the type system is concerned, enums are more than simple strings
I was surprised to find you were right about this; I expected the above to pass checks, but it doesn't. TypeScript actually does treat the enum as more than just its underlying value. So that's interesting- and that's the first reason I've really seen to use theseBut I think I still prefer plain values, for myself. There's just something that feels right and pure about using plain JSON-like JavaScript values, and then overlaying types on top of them that have no effect on their behavior and don't obscure their underlying representation. You know exactly what you've got, it's maximally duck-typeable, it's unchanged when it gets (de)serialized, it all just feels better to me
They happen once in a blue moon and when they do, you hate your life for a day. Maybe a week.
Plain values also have the problem of losing context. This primarily hits those who don't know the code. That's the whole selling point of OOB and related abstractions, anyway.
Both of the above happening in tandem isn't that rare and a realistic risk factor for big projects.
I'm not sure why I would? TypeScript would still lead me straight to the problem via type incompatibilities; it just wouldn't perform the fix for me automatically in this one specific case
Do you mean it doesn't compile, because it doesn't. You need to write either
or The latter passes typechecking with invalid values, both change automatically for me if I refractor the symbol values in VS Code.On the other hand, if MyEnum were actually an enum rather than a union of strings, this problem wouldn't have happened.
Less typing and easier to read than your solution
Using enums is not really that different from using type aliases. It’s a nicer way to write string/number values.
For web dev, having a single language everywhere is really nice.
Having a great type checker is great, but if I don’t get any of the IDE functionality etc, it’s losing half the value.
Hegel is basically a less mature version of Flow. I preferred Flow over TS for the longest time, but let's face it – TS won in terms of absolute adoption, with very little prospect for anyone to take over.
Zod is filling a gap in the language - runtime type checking. It is not the only one doing so, but the fact that there are several such solutions clearly indicates that there is a general need for it.
The language doesn’t have, or at least makes not having a primary goal, any runtime. “The” language is JavaScript. TypeScript is just annotations of it. Really good annotations. So good that you can build them out of reliable runtime checks which are type guards, without even writing a single type definition except to infer from them. Zod is filling a gap in JavaScript. TypeScript is facilitating that.
However, what is or isn't the goal of a language is subject to change. Libraries like Zod are trying to filling a gap in js, sure. But we are arguing that that gap is better filled by the typescript - if that requires expanding its goal or doing things that don't fit in current scope - so be it.
As to why it is better: Currently using zod requires that the author of the types uses zod's API. If they didn't anticipate that the types they are writing would need runtime checks, or prefer some other schema validation library than the one you prefer - as a consumer you need to now redefine all of these types using zod and keep them in sync. Compiler can help, sure but its still quite some work.
If typescript supported this natively, you could import some random library and use its types for runtime validation - that is not possible today, and it can't be possible until ts has first class runtime type validation.
People would also not have to learn the ts syntax and then also the zod api to achieve it.
Languages like dart demonstrate that this is practical.
I can define types using io-ts, infer a true typescript type from that to put in my d.ts files to get full ts type checking, and also have run time type checks, all from the same single definition.
The initial learning curve is admittedly a little steep, but once you have it down it's a breeze to use, and delightful.
Previous discussion: https://news.ycombinator.com/item?id=31663298 - it's downright mindblowing that all this seems to be the work of primarily a single developer.
For a less intrusive solution, https://github.com/jquense/yup is a great library to reach for whenever you're defining the shape of a network-transmitted object and don't want to introduce compilation stages.
What everyone who asks for runtime checking actually wants is:
- define once
- declaratively
- with clear validation and deserialization at boundaries where data types aren’t known
- without overhead for internal types which are well known
- without an additional build step like codegen
That is type guards! They just have to be granular and composable, and well tested at a granular and composable level. Libraries like zod and io-ts have shown how to do it. You can just use them, or many others like them, you don’t need anything new. Or you can pretty trivially build your own custom solution if they don’t scratch your particular itch.
You aren’t missing seamless runtime checks. You’re just not using them.
What everyone wants: Autogenerated type guards by class.
What everyone gets: “Remains out of scope.” and need to use other libraries. The compiler can do this significantly better (more ergonomic, less time, etc) than 3rd party libs. TS devs really should reconsider this feature.
https://github.com/microsoft/TypeScript/issues/15265
I’m quite sure people are asking for that, but I’m quite sure they’d regret wanting it if they got it. To the extent it’s desirable, it’s a thing that should be in TC39, not in TypeScript. The wailing and gnashing of teeth over when or whether or how type coercions happen is already mythical for the underlying language. Adding it to a compiler that’s increasingly not used to compile but only to type check is gonna add ten years of gripes about JS tooling nonsense to every single discussion.
They have considered this feature. Type guards are the solution and they’re a really good solution given the circumstances.
Flow had [flow-runtime](https://github.com/gajus/flow-runtime), which was nice. There were/are a few half-baked solutions in TS ecosystem, but nothing as mature as flow-runtime was. However, I wish that TS supported this natively.
I think what OP might've meant by run-time checks is auto-generating code that wraps every unpredictable/non-guaranteed boundary with a run-time type check. So if you have some data with the "any" type data being turned into a type or a use of the "as" keyword (e.g. `someType as anotherType`), then add a run-time check that throws an error at that boundary (instead of the code erroring out elsewhere, or resulting in incorrect behavior). Something like
[1] https://www.typescriptlang.org/docs/handbook/advanced-types....
That’s a runtime type check! Any equivalent you might bake into TypeScript would do the exact same thing, but it would do it rigidly in ways people don’t expect (being strict about input types, ie “do what I say not what I mean) or rigidly in ways people don’t expect (“do what I mean as long as you mean exactly what we allow”). Again, fine grained type guards as implemented by zod/io-ts/etc solve this problem and either give you all you need already or demonstrate exactly how you can use existing language features to achieve exactly what OP and anyone else asking for runtime types wants.
https://www.typescriptlang.org/docs/handbook/release-notes/t...
Type assertions match what you describe better - they are ways to override the compiler's opinion about the type of a variable or expression. They are, as you say, unsafe in the sense that the compiler has no way of knowing if this assertion is true or not.
Sadly this isn't true: https://tsplay.dev/WvGArw
I understand that adding something like zod is out of scope for tsc. However, type guards alone are not the full answer either.
https://github.com/colinhacks/zod
You’re welcome.
(I use this library every day. It has changed my life.)
https://github.com/ar-nelson/spartan-schema
It does the same thing as Zod, but is much smaller/simpler and its types always have a JSON representation.
[1]: https://github.com/JSMonk/hegel/issues/355#issuecomment-1075...
One of the reasons these libraries don't work so well with TS is that it doesn't have ML-style whole program inference and hence doesn't work so well with patterns like currying. Hegel seems more capable in that regard.
[1] https://ramdajs.com/
[2] https://github.com/sanctuary-js/sanctuary
What a shame
Unfortunately the was in Ukraine stopped the developer to work on the project