Try to type a flatMap and then we talk.
type ValueOrArray = T | Array >; type FlatMap = (array: Array , fn: (el: In) => ValueOrArray ) => Array ;
Functional Programming with TypeScript's Type System
31–40 of 46 posts
Re: Functional Programming with TypeScript's Type System
#32Earlier quoted context omitted.
type ValueOrArray = T | Array >; type FlatMap = (array: Array , fn: (el: In) => ValueOrArray ) => Array ;
This does not work, that's the issue, try to run it.
https://www.typescriptlang.org/play?target=6#code/C4TwDgpgBA...
Re: Functional Programming with TypeScript's Type System
#33I 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 ; function someFunc(): Foo { return f; // No error or warning, even with all strict flags enabled }
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 f…
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/contravariance-...
Re: Functional Programming with TypeScript's Type System
#34Earlier quoted context omitted.
Presumably the issue is that a Readonly 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 should be neither a subtype nor a supertype of T
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.
Re: Functional Programming with TypeScript's Type System
#35I 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 ; function someFunc(): Foo { return f; // No error or warning, even with all strict flags enabled }
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 f…
Re: Functional Programming with TypeScript's Type System
#36As 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.
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.
Re: Functional Programming with TypeScript's Type System
#37Earlier quoted context omitted.
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 f…
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 contrav…
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.
Re: Functional Programming with TypeScript's Type System
#38I 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 ; function someFunc(): Foo { return f; // No error or warning, even with all strict flags enabled }
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…
Re: Functional Programming with TypeScript's Type System
#39As 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.
Re: Functional Programming with TypeScript's Type System
#40Earlier quoted context omitted.
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 contrav…
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.
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 = (animals: T[], animal: T) => animals.push(animal)