Remix.run Logo
epolanski 13 hours ago

> TypeScript is a wonderfully advanced language though it has an unfortunately steep learning curve

An extremely steep one.

The average multi-year TypeScript developer I meet can barely write a basic utility type, let alone has any general (non TypeScript related) notion of cardinality or sub typing. Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.

Too many really stop at the very basics.

And even though I consider myself okay at TypeScript, the gap with the more skilled of my colleagues is still impressively huge.

I think there's a dual problem, on one side type-level programming isn't taken seriously by the average dev, and is generally not nurtured.

On the other hand, the amount of ideas, theory, and even worse implementation details of the TypeScript compiler are far from negligible.

Oh, and it really doesn't help that TypeScript is insanely verbose, this can easily balloon when your signatures have multiple type dependencies (think composing functions that can have different outputs and different failures).

pcthrowaway 12 hours ago | parent | next [-]

> Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.

To be clear, an array flat type:

    type FlatArr<Arg extends unknown[]> = Arg extends [infer First, ...(infer Rest)] ?
      First extends unknown[] ?
        [...First, ...FlatArr<Rest>] :
        [First, ...FlatArr<Rest>] :
      [];
is far from basic Typescript. The average Typescript dev likely doesn't need to understand recursive conditional types. It's a level of typescript one typically only needs for library development.

Not only have I never been expected to write something like this for actual work, I'm not sure it's been useful when I have, since most of my colleagues consider something like this nerd sniping and avoid touching/using such utilities, even with documentation.

wk_end 12 hours ago | parent | next [-]

If I saw that in a PR I would push very hard to reject; something like that is a maintenance burden that probably isn’t worth the cost, and I’ve been the most hardcore about types and TypeScript of anyone of any team I’ve been on in the past decade or so.

Now, that said, I probably would want to be friends with that dev. Unless they had an AI generate it, in which case the sin is doubled.

probabletrain 11 hours ago | parent | next [-]

I think there’s a difference between what’s expected/acceptable for library code vs application code. Types like this might be hard to understand, but they create very pleasant APIs for library consumers. I’ve generally found it very rare that I’ve felt the need to reach for more complex types like this in application code, however.

RXJS’s pipe function has a pretty complex type for its signature, but as a user of the library it ‘just works’ in exactly the type-safe way I’d expect, without me having to understand the complexity of the type.

pjerem 2 hours ago | parent | prev | next [-]

I’d say it depends. I always advocate for code that is easy to read and to understand, but in extremely rare conditions, hard to read code is the better solution.

Especially when it comes to signatures in Typescript, complex signatures can be used to create simple and ergonomic APIs.

But anyway you shouldn’t be allowed to push anything like this without multiple lines of comments documenting the thing. Unreadable code can be balanced with good documentation but I rarely saw this unfortunately.

ibejoeb 11 hours ago | parent | prev | next [-]

If it's correct, it's not a maintenance nightmare, and it will alert you to problems later when someone wants to use it incorrectly.

If you're writing first-party software, it probably doesn't matter. But if you have consumers, it's important. The compiler will tell you what's wrong all downstream from there unless someone explicitly works around it. That's the one you want to reject.

locknitpicker 3 hours ago | parent [-]

> If it's correct, it's not a maintenance nightmare, and it will alert you to problems later when someone wants to use it incorrectly.

You're confusing things. It is a maintenance nightmare because it is your job to ensure it is correct and remains correct in spite of changes. You are the one owning that mess and held accountable for it.

> If you're writing first-party software, it probably doesn't matter. But if you have consumers, it's important.

Yes, it is important that you write correct and usable code. That code doesn't fall on your lap though and you need to be the one writing and maintaining it. Whoever feels compelled to write unintelligible character soup that makes even experienced seasoned devs pause and focus is failing their job as a software engineer.

spankalee 11 hours ago | parent | prev | next [-]

What's the alternative? Have incorrect types for the function? That's not better.

epolanski 10 hours ago | parent | next [-]

The alternative is what shows in the comment: go on HN and tell the world you think TS and JS are crap and it's not worth your time, while writing poor software.

wk_end 10 hours ago | parent | prev [-]

To answer this we probably need more details, otherwise it's gonna be an XY Problem. What is it that I'm trying to do? How would I type this function in, say, SML, which isn't going to allow incorrect types but also doesn't allow these kinds of type gymnastics?

spankalee 9 hours ago | parent [-]

We don't have to deal in hypotheticals - we have a concrete example here. There's a method, array.flat() that does a thing that we can correctly describe in TypeScript's type system.

You say you would reject those correct types, but for what alternative?

It's hugely beneficial to library users to automatically get correctly type return values from functions without having to do error-prone casts. I would always take on the burden of correct types on the library side to improve the dev experience and reduce the risk of bugs on the library-consumption side.

wk_end 9 hours ago | parent [-]

There's nothing I can do about the standard JavaScript library, but in terms of code I have influence over, I very simply would not write a difficult-to-type method like Array.prototype.flat(), if I could help it. That's what I mean by an XY Problem - why are we writing this difficult-to-type method in the first place and what can we do instead?

Let's suppose Array.prototype.flat() wasn't in the standard library, which is why I'm reviewing a PR with this gnarly type in it. If I went and asked you why you needed this, I guess you'd say the answer is: "because JavaScript lets me make heterogenous arrays, which lets me freely intermix elements and arrays and arrays of arrays and... in my arrays, and I'm doing that for something tree-like but also need to get an array of each element in the structure". To which I'd say something like "stop doing that, this isn't Lisp, define an actual data type for these things". Suddenly this typing problem goes away, because the type of your "flatten" method is just "MyStructure -> [MyElements]".

pcthrowaway 8 hours ago | parent | next [-]

Sure, if you're living fully in your own application code, and you don't need to consume things from an API you don't control, it's easy to live in a walled garden of type purity.

I can recognize that most people are going to go for inaccurate types when fancier semantics are necessary to consume things from the network.

But we also have the real world where libraries are used by both JS devs and TS devs, and if we want to offer semantics that idiomatic for JS users (such as Array.prototype.flat()) while also providing a first-class experience to TS consumers, it is often valuable to have this higher-level aptitude with the TS type system.

As mentioned earlier, I believe 90% of TS devs are never in this position, or it's infrequent enough that they're not motivated to learn higher-level type mechanics. But I also disagree with the suggestion that such types should be avoided because you can always refactor your interface to provide structure that allows you to avoid them; You don't always control the shape of objects which permeate software boundaries, and when providing library-level code, the developer experience of the consumer is often prioritized, which often means providing a more flexible API that can only be properly typed with more complex types.

kaoD 2 hours ago | parent | prev | next [-]

> Suddenly this typing problem goes away, because the type of your "flatten" method is just "MyStructure -> [MyElements]".

How is that less maintenance burden than a simple Flatten type? Now you have to construct and likely unwrap the types as needed.

And how will you ensure that you're flattening your unneeded type anyways? Sure you can remove the generics for a concrete type but that won't simplify the type.

It's simple. It's just recursive flattening an array in 4 lines. Unlikely to ever change, unlike the 638255 types that you'd have to introduce and maintain for no reason.

There are many reasons not to do that. Say your business logic changes and your type no longer needs one of the alternatives: you are unlikely to notice because it will typecheck even if never constructed and you will have to deal with that unused code path until you realize it's unused (if you ever do).

You made code harder to maintain and more complex for some misguided sense of simplicity.

2 hours ago | parent | prev | next [-]
[deleted]
ffsm8 6 hours ago | parent | prev | next [-]

> MyStructure -> [MyElements]

Right, from the structure you get an array with one element which is likely an union type from that naming.

Honestly, you sound more like your arguing from the perspective of a person unwilling to learn new things, considering you couldn't even get that type correct.

To begin with, that flat signature wasn't even hard to understand?

wk_end 6 hours ago | parent | next [-]

What I wrote would be a syntax error in TypeScript (no name for the argument, wrong arrow), not a function that returns array with one element; I used Haskell-ish notation instead of TypeScript's more verbose "(structure: MyStructure) => MyElement[]".

I thought it was clear enough that I was being informal and what I meant was clear, but that was admittedly probably a mistake. But to infer an implication from that that I'm "unwilling to learn new things" is a non sequitur and honestly kind of an unnecessarily dickish accusation.

lovich 6 hours ago | parent | prev [-]

Brah, If you have a type with that many characters in it that isn’t a super long string name, it’s not easy to understand unless you are the 1% of 1% when it comes to interpreting this specific language.

On top of that I fully agree with the poster you’re responding to. In general application code that’s and extremely complicated type, generally done by someone being as clever as can be. And if the code you’ve written when you’re being as clever as possible has a bug in it, you won’t be clever enough to debug it.

geoffmanning 8 hours ago | parent | prev [-]

This. 1000%.

8note 12 hours ago | parent | prev | next [-]

looking back at them is also real hard to debug. you dont get a particularly nice error message, and a comment or a test would tell better than the type what the thing should be looking like

12 hours ago | parent | prev [-]
[deleted]
epolanski 10 hours ago | parent | prev | next [-]

The version I was thinking when I wrote the comment is simpler

    type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T
> The average Typescript dev likely doesn't need to understand recursive conditional types.

The average X dev in Y language doesn't need to understand Z is a poor argument in the context of writing better software.

furyofantares 9 hours ago | parent | next [-]

> The average X dev in Y language doesn't need to understand Z is a poor argument in the context of writing better software.

It's a good response to the claim that we'd be surprised at how many would fail to do this, though.

NooneAtAll3 9 hours ago | parent | prev [-]

as a person that never touched JS and TS... what's the difference between the two answers?

granzymes 9 hours ago | parent | next [-]

For one, the simple answer is incomplete. It gives the fully unwrapped type of the array but you still need something like

  type FlatArray<T extends unknown[]> = Flatten<T[number]>[]
The main difference is that the first, rest logic in the complex version lets you maintain information TypeScript has about the length/positional types of the array. After flattening a 3-tuple of a number, boolean, and string array TypeScript can remember that the first index is a number, the second index is a boolean, and the remaining indices are strings. The second version of the type will give each index the type number | boolean | string.
ameliaquining 9 hours ago | parent | prev | next [-]

First one flattens a potentially-nested tuple type. E.g., FlatArr<[number, [boolean, string]]> is [number, boolean, string].

Second one gets the element type of a potentially-nested array type. E.g., Flatten<number[][]> is number.

For what it's worth, I've never needed to use either of these, though I've occasionally had other uses for slightly fancy TypeScript type magic.

pcthrowaway 4 hours ago | parent | prev [-]

The answer above actually gets the type union of all non-array elements of a multi-level array.

In other words

    Flatten<[1,[2,'a',['b']]]>
will give you a union type of 1, 2, 'a', and 'b'

    const foo: Flatten<[1,[2,'a',['b']]]> = 'b'; // OK
    const bar: Flatten<[1,[2,'a',['b']]]> = 'c'; // Error: Type '"c"' is not assignable to type '1 | 2 | "a" | "b"'
Technically the inference is unnecessary there, if that's you're goal:

     type Flatten<T> = T extends Array<unknown> ? Flatten<T[number]> : T
I don't really consider this the type of flattening an array, but `Array<Flatten<ArrType>>` would be. And this would actually be comparable to the builtin Array.prototype.flat type signature with infinite depth (you can see the typedef for that here[1], but this is the highest level of typescript sorcery)

My solution was for flattening an array with a depth of 1 (most people using Array.prototype.flat are using this default depth I'd wager):

    console.log(JSON.stringify([1,[2, [3]]].flat()));
    > [1,2,[3]]
The type I provided would match those semantics:

    // 'readonly' added to the 'extends' sections to work on "as const" (readonly) arrays
    type FlatArr<Arg extends unknown[] | readonly unknown[]> = Arg extends readonly [infer First, ...(infer Rest)] ?
      First extends readonly unknown[] ?
        [...First, ...FlatArr<Rest>] :
        [First, ...FlatArr<Rest>] :
      [];

    const flatten = <Arr extends unknown[] | readonly unknown[]>(arr: Arr): FlatArr<Arr> => arr.flat() as FlatArr<Arr>
    const someArr = [1,[2,'a',['b', ['c']]]] as const; // const someArr: readonly [1, readonly [2, "a", readonly ["b", readonly ["c"]]]]
    const someArr2 = flatten(someArr);                 // const someArr2: [1, 2, "a", readonly ["b", readonly ["c"]]]

    
[1]: https://github.com/microsoft/TypeScript/blob/main/src/lib/es...
miki123211 11 hours ago | parent | prev | next [-]

I recently had to write a Promise.all, but using an object instead of an array.

That was... non-trivial.

hdjrudni 10 hours ago | parent | next [-]

If it's what I'm thinking, that one isn't too bad. I wrote it awhile back:

    export async function promiseAll<T extends Record<string, Promise<any>>>(promises: T): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
        const keys = Object.keys(promises) as Array<keyof T>;
        const result = await Promise.all(keys.map(key => promises[key]));
        return Object.fromEntries(result.map((value, i) => [keys[i], value])) as { [K in keyof T]: Awaited<T[K]> };
atsjie 3 hours ago | parent [-]

I'd call that bad pretty bad.

Without internet or AI I wouldn't attempt writing anything like that.

ruined 9 hours ago | parent | prev [-]

rejoice https://github.com/tc39/proposal-await-dictionary

kaoD 12 hours ago | parent | prev [-]

For those unfamiliar with TS, the above is just...

    function flat([head, ...tail]) {
      return Array.isArray(head)
        ? [...flat(head), ...flat(tail)]
        : [head, ...flat(tail)]
    }
...in TS syntax.
tomsmeding 11 hours ago | parent [-]

Well, it is the type of that, in TS syntax. Few are the statically-typed languages that can even express that type.

not_kurt_godel 7 hours ago | parent [-]

Java: List<Object>

Python: list[Any]

...what am I missing?

rjh29 3 hours ago | parent | next [-]

You're missing the specialisation of Object/Any. For example Array.flat called with [int, [bool, string]] returns a type [int, bool, string]. Admittedly this is somewhat niche, but most other languages can't express this - the type information gets erased.

sli 4 hours ago | parent | prev [-]

You're missing the input type, essentially. Those are just array types. The TypeScript type signature more of a function type, it expresses flattening a n-dimensional array (input type) into a flat array (output type).

afavour 12 hours ago | parent | prev | next [-]

> Too many really stop at the very basics.

I don’t think that means it has a steep learning curve. It just means the basics suffice for a ton of TypeScript deployments. Which I personally don’t see as the end of the world.

vosper 12 hours ago | parent [-]

Yes, to me this is a biggest feature of Typescript: A little goes a long way, while the advanced features make really cool things possible. I tend to think of there being two kinds of Typescript - Application Typescript (aka The Basics, `type`, `interface`, `Record`, unions etc...) and Library Typescript which is the stuff that eg Zod or Prisma does to give the Application Typescript users awesome features.

While I aspire to Library TS levels of skill, I am really only a bit past App TS myself.

On that note I've been meaning to the the Type-Level Typescript course [0]. Has anyone taken it?

https://type-level-typescript.com/

madeofpalk 11 hours ago | parent | prev | next [-]

> Too many really stop at the very basics.

As someone who knows slightly more than the basics, and enough to know about the advanced stuff that I don't know about, this is the correct place to stop.

I would much rather restructure my javascript than do typescript gymnastics to fit it into the type system.

IshKebab 2 hours ago | parent | next [-]

I agree. The advanced stuff mostly exists in order to allow writing type annotations for JavaScript libraries that have APIs that are very dynamic.

If you're purely writing Typescript then you mostly don't need it.

IceDane 11 hours ago | parent | prev [-]

[flagged]

ervine 11 hours ago | parent [-]

You can restructure your JS to avoid some crazy verbose TS though, sometimes. I think that's the point they were making. Why be so hostile?

mook 8 hours ago | parent | prev | next [-]

It's also terribly documented. As an example, I don't think `satisfies` is in the docs outside of release notes. There's lots more stuff like that, which makes using it kind of frustrating.

Exoristos 3 hours ago | parent | prev | next [-]

"There basics," well understood and judiciously applied, is where the bulk of TypeScript's value lies.

locknitpicker 15 minutes ago | parent [-]

> "There basics," well understood and judiciously applied, is where the bulk of TypeScript's value lies.

Yes, precisely. OP is also completely oblivious to the fact that TypeScript is designed to help developers gradually onboard legacy JavaScript projects and components, which definitely don't require arcane and convoluted type definitions to add value.

knallfrosch 4 hours ago | parent | prev | next [-]

Typescript types often devolve into

keyof typeof[number] ? uppercase (myNum)

On the other hand, there isn't much Omit, readonly, mutable in existing code bases, so devs have nowhere to learn but documentation.

Then the ground shifts again and –what should be basic stuff – enums are banned, because erasableSyntaxOnly makes life so much easier.

dzonga 10 hours ago | parent | prev | next [-]

typescript is largely a result of solving a non-existent problem. Yeah JS is finicky & has foot-guns, however they're ways around those foot guns that don't involve typescript.

Rich Hickey in 10 Years of Clojure & Maybe Not then the Value of Values - lays this out - though not meant at typescript but static types in general.

the thing most people don't have proper Javascript fundamentals.

Function signatures: JSDoc works

Most types - use Maps | Arrays

if a value doesn't exist in a map we can ignore it. There's also the safe navigation operator.

Instead of mutable objects - there's ways around this too. Negating types again.

locknitpicker 3 hours ago | parent | next [-]

> typescript is largely a result of solving a non-existent problem.

Claiming that lack of static type checking is a non-existent problem is quite a bold claim. Care to offer some details to substantiate your claim?

fenomas 7 hours ago | parent | prev [-]

Uh, among several other issues with this, what use are JSDoc comments for typing, without typescript to check them?

locknitpicker 3 hours ago | parent | prev | next [-]

> The average multi-year TypeScript developer I meet can barely write a basic utility type, let alone has any general (non TypeScript related) notion of cardinality or sub typing. Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.

I think you're both exaggerating your blanket accusations of incompetence and confusing learning curve with mastering extremely niche techniques akin to language gotchas.

cynicalcoder 7 hours ago | parent | prev | next [-]

>And even though I consider myself okay at TypeScript, the gap with the more skilled of my colleagues is still impressively huge.

Maybe they're smart, but the even smarter dev would avoid unnecessary complexity in the first place.

mohas 5 hours ago | parent | prev | next [-]

to our defense, we want to build stuff not become ts wizards. also I've worked with libraries with heavy heavy typing that it was a nightmare if you wanted to use their lib in any other way than what they have imagined.

stefan_ 11 hours ago | parent | prev | next [-]

This is the bell curve meme, and you are in the middle telling us "template metaprogramming in C++ is amazing".

undeveloper 13 hours ago | parent | prev | next [-]

these are things most developers don't know how to do in most language's type systems. I think only rust with its focus on functional roots has seen similar focus on utilizing its type system to its fullest extent.

lloydatkinson 13 hours ago | parent | prev | next [-]

TypeScript codebases I've seen generally seem to have the widest demonstration of skill gap versus other languages I use.

For example, I don't ever see anyone using `dynamic` or `object` in C#, but I will often see less skilled developers using `any` and `// @ts-ignore` in TypeScript at every possible opportunity even if it's making their development experience categorically worse.

For these developers, the `type` keyword is totally unknown. They don't know how to make a type, or what `Omit` is, or how to extend a type. Hell, they usually don't even know what a union is. Or generics.

I sometimes think that in trying to just be a superset of JavaScript, and it being constantly advertised as so, TypeScript does not/did not get taken seriously enough as a standalone language because it's far too simple to just slot sloppy JavaScript into TypeScript. TypeScript seems a lot better now of having a more sane tsconfig.json, but it still isn't strict enough by default.

This is a strong contrast with other languages that compile to JavaScript, like https://rescript-lang.org/ which has an example of pattern matching right there on the home page.

Which brings me onto another aspect I don't really like about TypeScript; it's constantly own-goaling itself because of it's "we don't add anything except syntax and types" philosophy. I don't think TypeScript will ever get pattern matching as a result, which is absurd, because it has unions.

ervine 11 hours ago | parent | next [-]

On the other hand, would we even be talking about it if it hadn't stuck to its goals?

fourthark 11 hours ago | parent | prev [-]

It will get pattern matching when JS does. Not certain yet but in progress.

https://github.com/tc39/proposal-pattern-matching

ibejoeb 11 hours ago | parent [-]

That proposal is really dragging though. And typescript needs as much work because that's where the real power is. We need discern thing like

    match (x) {
      "bob": ...,
      string: ...,
      () => Promise<void>: ...,
      () => Promise<string>: ...,
    }
with exhaustiveness checking for it to be truly useful.
ameliaquining 9 hours ago | parent [-]

Discriminating a function or promise based on return type is never going to work, because JavaScript is dynamically typed and TypeScript erases types at compile time, so there's no way to know at runtime what type a function or promise is going to return.

imiric 11 hours ago | parent | prev | next [-]

You're right, but that begs the question: does a type system really require such complexity?

I'm aware that type theory is a field in and of itself, with a lot of history and breadth, but do developers really need deep levels of type flexibility for a language to be useful and for the compiler to be helpful?

I think TypeScript encourages "overtyping" to the detriment of legibility and comprehension, even though it is technically gradually typed. Because it is so advanced and Turing complete itself, a lot of brain cycles and discussion is spent on implementing and understanding type definitions. And you're definitely right that it being verbose also doesn't help.

So it's always a bittersweet experience using it. On one hand it's great that we have mostly moved on from dynamically typed JavaScript, but on the other, I wish we had settled on a saner preprocessor / compiler / type system.

rjh29 3 hours ago | parent [-]

The idea is to make libraries preserve as much type information as possible, as a principle. Once type information is erased it can't be restored. For regular application code you don't need to use those features.

imiric an hour ago | parent [-]

But regular application code also contains libraries. Type information is useful even if you're the only user of those APIs.

My point was more related to the level of expressiveness required of a type system in order to allow a programmer to produce reliable code without getting in their way. I think TypeScript leans more towards cumbersome than useful.

For example, I'm more familiar with Go's type system, which is on the other side of that scale. It is certainly much less expressive and powerful than TypeScript, and I have found it frustrating and limiting in many ways, but in most day-to-day scenarios it's reasonably adequate. Are Go programs inherently worse off than TypeScript programs? Does a Go programmer have a worse experience overall? I would say: no.

samdoesnothing 12 hours ago | parent | prev | next [-]

I have mixed feelings about Typescript, I hate reading code with heavy TS annotations because JS formatters are designed to keep line widths short, so you end up with a confusing mess of line breaks. Pure JS is also just more readable.

Also you can so easily go overboard with TS and design all sorts of crazy types and abstractions based on those types that become a net negative in your codebase.

However it does feel really damn nice to have it catch errors and give you great autocomplete and refactoring tooling.

paulddraper 6 hours ago | parent | prev | next [-]

Not steep so much as deep.

There’s a lot you can do in TypeScript. But you don’t have to do it. And TS existed successfully a long time without those features.

monkpit 9 hours ago | parent | prev | next [-]

> ask someone to write a signature for array flat

Out of curiosity - what do you think is a satisfactory answer here?

My answer would vary wildly based upon more details, but at the most basic all I can think you could guarantee is Array<unknown> => Array<unknown>?

ForHackernews 13 hours ago | parent | prev [-]

Honestly I just use TypeScript to prevent `1 + [] == "1"` and check that functions are called with arguments. I don't care about type theory at all and the whole thing strikes me as programmers larping (poorly) as mathematicians.

epolanski 12 hours ago | parent | next [-]

I couldn't care less about mathematics, but I do care about making impossible state impossible and types documenting the domain.

If you type some state as:

    isLoading: boolean
    result: Foo
    hasError: boolean
    errorMessage: string | null
then you're creating a giant mess of a soup where the state of your program could have a result, be loading and an error at the same time. If you could recognise that the state of your program is a sum of possible states (loading | success | error), and not their product as the type above you could highly simplify your code, add more invariants and reduce the number of bugs.

And that is a very simple and basic example, you can go *much* further, as in encoding that some type isn't merely a number through branded types, but a special type of number, be it a positive number between 2 and 200 or, being $ or celsius and avoiding again and entire class of bugs by treating everybody just as an integer or float.

samdoesnothing 5 hours ago | parent | next [-]

You can encode that "correctly" in pure JS

   class LoadingState extends State {}
   class ResultState extends State {}
   class ErrorState extends State {}
   const state: State = new LoadingState
Generally it's a bad pattern to have dependant fields like your example, but you don't need TS to solve it.
what 6 hours ago | parent | prev [-]

>encoding that a number is between 2 and 200

What’s the point of this level of autism when you still have to add run time checks?

kennywinker 5 hours ago | parent [-]

For a function setVelocity() that can accept 1..<200. You call it with numbers that you enter directly and types tell you something that would otherwise be a comment on the function, or you do runtime checks elsewhere, and the type becomes proof that you checked them before handing it into the function.

Btw, using “autism” to mean “pedantry” leaves a bit of a bad taste in my mouth. Maybe you could reconsider using it that way in the future.

samdoesnothing 5 hours ago | parent [-]

Pushing everything to types like this creates a different burden where you're casting between types all over the place just to use the same underlying data. You could just clamp velocity to 200 in the callee and save all that hassle.

kennywinker 4 hours ago | parent [-]

Casting? Not really - i think you’d only need a couple type checks.

Imo this is mostly useful for situations where you want to handle input validation (and errors) in the UI code and this function lives far away from ui code.

Your point about clamping makes sense, and it’s probably worth doing that anyway, but without it being encoded in the type you have to communicate how the function is intended to be used some other way.

samdoesnothing 3 hours ago | parent [-]

How would you convert a Number type to a ClampedNumber type without casting?

kennywinker 2 hours ago | parent [-]

Ah, yeah you’re right. I somehow thought typescript could do type narrowing based on checks - like say:

If (i >= 1) { // i’s type now includes >= 1 }

But that is not the case, so you’d need a single cast to make it work (from number to ClampedNumber<1,200>) or however exactly you’d want to express this.

Tbf having looked more closely into how typescript handles number range types, I don’t think I would ever use them. Not very expressive or clear. I think I hallucinated something closer to what is in this proposal: https://github.com/microsoft/TypeScript/issues/43505

I still think that the general idea of communicating what acceptable input is via the type system is a good one. But the specifics of doing that with numbers isn’t great in typescript yet.

samdoesnothing 2 hours ago | parent [-]

How would you implement it in other languages that support it better? Can you literally just do a range check and the compiler infers its range for types? If so thats actually pretty neat.

IceDane 11 hours ago | parent | prev [-]

Blub.