[HN Gopher] Functional Programming with TypeScript's Type System
___________________________________________________________________
Functional Programming with TypeScript's Type System
Author : evolveyourmind
Score : 81 points
Date : 2023-04-16 09:31 UTC (13 hours ago)
(HTM) web link (desislav.dev)
(TXT) w3m dump (desislav.dev)
| hoelle wrote:
| Gross. I love it.
| ck45 wrote:
| Related: "TypeScripting the technical interview"
| https://news.ycombinator.com/item?id=35120084
| coneonthefloor wrote:
| What a monstrosity.
| rdez6173 wrote:
| Fun exploration and exceptionally impractical. Love it.
| Waterluvian wrote:
| While no actual Turing machine's tape is infinitely long, I found
| issues in TypeScript with how finite generics are.
|
| You have to define every possible count of generic arguments if
| you want to preserve their types. And if you go above that count
| your type system degrades. I think there's also a maximum of 7 or
| so before it doesn't work. Beyond that and the generic type
| widens.
|
| For example, Lodash enumerating types for 2 to 7 generic items
| per function:
| https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0452...
|
| Admittedly I don't understand the problem space well. I've just
| seen it happen to me and in others' code. It might not actually
| be an issue, or is already fixed.
| SirensOfTitan wrote:
| This was fixed in Typescript 4.0, with the introduction of
| variadic tuples:
| https://www.typescriptlang.org/docs/handbook/release-notes/t...
| Waterluvian wrote:
| Oh awesome! Thanks for sharing.
| nkrisc wrote:
| As wonderfully absurd as this is, I learned more about
| TypeScript's type system from this post than I have from its
| documentation.
|
| _Entirely_ possible that PEBKAC, but I've found TypeScript's
| documentation to be on the worse end of the programming language
| documentation quality spectrum.
| DangitBobby wrote:
| It's not just you. To learn how to express more advanced types
| (or learn whether they are even possible to express), I've had
| to Google, read source code, or scan random medium articles and
| blogs from tech companies. Rarely have I learned anything new
| from the TS docs.
| wooly_bully wrote:
| It's good for reference but not for discovery. If I already
| know the general concept (let's say Template Literal Types) I
| can get good info on it, but if I start with a question like
| 'Is there a way to make sure this string literal starts with
| "id_"?' then I find it very hard to know.
|
| Random but this is what I'm finding GPT-4 best at: translating
| random questions into domain terminology + providing examples.
| iudqnolq wrote:
| maybe this is just an issue with me but I've not found any
| way to search the docs. the only thing Google seems to index
| is the release notes, and going backwards from the release
| notes to guess the appropriate section of the docs that will
| explain that concept is really annoying.
| nkrisc wrote:
| I can't even find basic type definitions for standard JS
| functions. I have to type the function in my IDE and then
| open the type definition from there.
|
| So it works, I guess, but that's not really how I want to
| work.
| ekvintroj wrote:
| Try to type a flatMap and then we talk.
| klysm wrote:
| Is this actually an issue?
| [deleted]
| anamexis wrote:
| type ValueOrArray<T> = T | Array<ValueOrArray<T>>; type
| FlatMap<In, Out> = (array: Array<In>, fn: (el: In) =>
| ValueOrArray<Out>) => Array<Out>;
| ekvintroj wrote:
| This does not work, that's the issue, try to run it.
| anamexis wrote:
| It seems to work for me, what issue are you seeing?
|
| https://www.typescriptlang.org/play?target=6#code/C4TwDgpgB
| A...
| [deleted]
| bottlepalm wrote:
| I'm really interested in this for fun, but it's still way over my
| head. And this is someone who has been using typescript daily for
| years. Would love to read something more long form that builds up
| to this.
| firechickenbird wrote:
| At this point I'm wondering if the TypeScript type system can be
| used for dependant types that would allow formal verification of
| the programs
| remexre wrote:
| That'd require it to be sound and programs to be total: an
| infinite loop is a proof of anything, and type system
| unsoundness leads to false proofs
| jcparkyn wrote:
| I've done my share of weird stuff with TS types before, but I'd
| never seen the trick of using interfaces to make higher-order
| functions. Neat.
| [deleted]
| foepys wrote:
| I don't get TypeScript's type system.
|
| It is obviously very powerful and can model very complex type
| constraints.
|
| But then you have stuff like this where it is not checking types
| as I would expect: interface Foo { bar: string; }
| const f = {bar: "foobar"} as Readonly<Foo>; function
| someFunc(): Foo { return f; // No error or warning, even
| with all strict flags enabled }
| rendall wrote:
| Sorry, I don't get it. What do you expect to happen here?
| PhilipRoman wrote:
| Presumably the issue is that a Readonly<Foo> shouldn't be a
| subtype of Foo
|
| I should note that I haven't yet had the pleasure of using a
| language that handles const-ness properly, as Readonly<T>
| should be neither a subtype nor a supertype of T
| rendall wrote:
| As an aside, I'm on mobile and tried to visit the
| TypeScript playground to play around with this, but
| weirdly, the default code is an implementation of FizzBuzz!
| There was not an obvious way to clear it to get a blank
| editor. Even "select all" context menu was hijacked. So I
| gave up. I'll have to file an issue.
| eyelidlessness wrote:
| What I do is delete all but a few characters after "code"
| in the URL, then reload to get an empty playground. It's
| annoying but it works.
| LaGuardiaKid wrote:
| Handling of readonly correctness is definitely something ts
| doesn't handle well
| globuous wrote:
| Yeah, there are some weird stuff in typescript, for instance,
| this typechecks class Animal {}
| class Dog extends Animal { woof() {} }
| class Cat extends Animal { meow() {} }
| let append_animals = (animals: Animal[], animal: Animal) =>
| animals.push(animal) let dogs = [new Dog()]
| append_animals(dogs, new Cat()) dogs.map(dog =>
| dog.woof())
|
| Which if you evaluate, you'll obviously get:
| Uncaught TypeError: dog.woof is not a function
|
| Whereas Mypy won't typecheck the equivalent Python code:
| class Animal: pass class Dog(Animal):
| pass def append_animals(animals:
| List[Animal], animal: Animal) -> None:
| animals.append(animal) dogs = [Dog()]
| append_animals(dogs, Dog())
|
| It'll throw with: $ mypy types.py
| types.py:16: error: Argument 1 to "append_animals" has
| incompatible type "List[Dog]"; expected "List[Animal]"
| types.py:16: note: "List" is invariant -- see https://mypy.read
| thedocs.io/en/stable/common_issues.html#variance
| types.py:16: note: Consider using "Sequence" instead, which is
| covariant
| Shacklz wrote:
| From my point of view, this is one of those cases where
| structural typing just doesn't work all that well when used
| for OOP.
|
| Typescript does give you a solution to this problem, namely
| that you use generics to constrain the parameters of your
| method: let append_animals = <T extends
| Animal>(animals: T[], animal: T) => animals.push(animal)
|
| Your example now gives the expected error.
|
| TypeScript does indeed have its quirks, but most of them do
| not really matter for real-life purposes or can easily be
| worked around like in the example above.
| canadianfella wrote:
| [dead]
| brabel wrote:
| The problem with your TS code is that it's using covariance
| on a mutable generic type, which is unsafe and strict type
| systems would've forbidden that.
|
| To expand: TS treats `Dog[]` as a subtype of `Animal[]`
| because `Dog` is a subtype of `Animal`... that work if you
| only read values from the array... but trying to change the
| array, you run into trouble. Some languages let you declare
| covariance (reading ) and contravariance explicitly to
| address this issue. To my limited knowledge of TS, that's not
| possible in TS (as it tries to keep things simple and
| compatible with JS, probably).
|
| The answers in this[1] SO question explain these concepts
| better than I could.
|
| [1] https://stackoverflow.com/questions/27414991/contravarian
| ce-...
| DangitBobby wrote:
| Why was the `dogs` array initialized as an `Animal[]` type
| instead of `Dog[]` type which would forbid the addition of
| a `Cat` type?
|
| Why would you be able to map a call to `woof` over an
| `Animal[]` when `Animal` doesn't implement `woof`? I don't
| understand how the SO link answers these questions.
| c-hendricks wrote:
| > Why was the `dogs` array initialized as an `Animal[]`
| type instead of `Dog[]`
|
| That might be your confusion: it wasn't. Its type is
| `Dog[]`.
|
| > which would forbid the addition of a `Cat` type?
|
| Why would that be forbidden? The problematic method is
| `append_animals`, which only cares that both arguments
| satisfy `Animal`, which both `Dog` and `Cat` do.
|
| > Why would you be able to map a call to `woof` over an
| `Animal[]` when `Animal` doesn't implement `woof`
|
| Back to your root confusion, since for all intents and
| purposes, `dogs.map` thinks it's an array of dogs, it
| doesn't complain.
|
| If `append_animals` was written like this, things would
| be fine: let append_animals = <T
| extends Animal>(animals: T[], animal: T) =>
| animals.push(animal)
| DangitBobby wrote:
| I see what you're saying. Thanks for taking the time to
| explain.
| sakesun wrote:
| This is an issue open for discussion since 2017
|
| https://github.com/microsoft/TypeScript/issues/13347
| rektide wrote:
| Are there other examples of ways TS confounds you?
|
| This feels like a "haha, gotcha!" moment. Like Gary Bernhardt's
| _Wat_ talk, it 's one single example that looks extremely
| silly... but has next to no actual impact on anyone using the
| language regularly, is like the faintest little quirk.
|
| Typescript seems reasonably acceptable. I struggle to think of
| what I would ask for, what would be significantly massively
| different in my life if there were a hypothetical much better
| alternative to Typescript.
| deathtrader666 wrote:
| >> if there were a hypothetical much better alternative to
| Typescript. Like ReScript ?
| foepys wrote:
| Don't get me wrong, I am very glad that TS exists and I am
| using it at least weekly. But there are inconsistencies in
| the type system that are very surprising to beginners because
| TS is trying so hard to be good at edge cases.
| _fat_santa wrote:
| Having used TS in production with web and mobile (React Native)
| apps, most of it is rather simple interfaces to make sure you
| are passing data correctly. I'd say 99.9% of TS code I saw was
| to make sure that you passed SuperComplexBusinessObject
| correctly.
|
| But like you, I've come across many instances where I got "hey
| TS aren't you supposed throw an error here?"
___________________________________________________________________
(page generated 2023-04-16 23:01 UTC)