1. 14
  1.  

  2. 12

    Typescript has become my favourite language for side projects and I’ve even snuck it into the Day Job. The dynamism of JS with a fairly rich type system at “compile” time is a fantastic combination.

    I suspect that part of what’s great is that the type system has been built to be expressive enough to capture real JS usage and APIs. Instead of being a static type system imposed on a dynamic languages it’s a type system suited to the language and its users.

    1. 1

      Every static type system is a type system imposed on a dynamic language. Are you meaning to compare to flow here?

      1. 7

        Au contraire! You have it backwards. Dynamic languages do have static types – just, not very expressive ones.

        https://existentialtype.wordpress.com/2011/03/19/dynamic-languages-are-static-languages/

        1. 3

          Au contraire your au contraire! Static languages have dynamic types, but they’re so weak you need to run a SECOND program just to make sure you don’t accidentally use them!

        2. 3

          Not so, some languages are built from the ground up with static types. There’s no way to implement Haskell (typeclasses) or Scala (implicits) without implementing a de facto static type system.

          1. 1

            Haskell is built from the ground up with types, sure. In a sense though it is still a series of extensions to the untyped LC. Type classes are type level computation and therefore a part of the type system so their absence doesn’t make a great counter example.

            Granted this is all a lot more hair splitting than my original question.

            1. 1

              I think the major difference is that some languages expose untyped constructs to language users, while in other cases untyped constructs are just hidden artifacts of a compiler implementation that are invisible to users.

              1. 0

                the issue is these terms are vague with conflicting alternative definitions. None of this is relevant to clarifying the second paragraph I was asking about.

              2. 1

                Haskell is built from the ground up with types, sure. In a sense though it is still a series of extensions to the untyped LC. Type classes are type level computation and therefore a part of the type system so their absence doesn’t make a great counter example.

                What do you mean? There’s no sense in which a Haskell expression like (decode x) - can be understood from an untyped perspective. Both its semantics and its behaviour are fundamentally entangled with what type the expression has.

                Granted this is all a lot more hair splitting than my original question.

                It’s not “hair splitting”, your original claim is false and misleading.

              3. 1

                Further at runtime the type information about a generic parameter is almost totally lost, you need to have an instance to base type information off of. There are real weaknesses with bolting strong typing onto dynamically typed runtimes.

                1. 1

                  Generics should be erased at runtime, having them there only makes it easier to violate parametricity.

                  1. 1

                    From the perspective of someone who’s written multiple ORMs and other serialization frameworks … this is objectively wrong. You need to know what fields are on an object, and be able to call into generic functions with strongly typed parameters to make sure things like interfaces are still satisfied.

                    Typescript is a step forward for javascript devs but it is still very, very lacking compared to something like golang even.

                    1. 1

                      Take a look at how ORMs and serialization frameworks are implemented in Scala or Haskell sometime. Implementing those things requires ad-hoc polymorphism, but that can be done at compile time rather than at runtime, via typeclasses or similar mechanisms. This is better for maintainability and understandability of code than doing runtime reflection on reified generics, because it makes the distinction between parametric and ad-hoc polymorphism visible to the programmer - one can immediately see which functions will always follow the same code path at runtime, and which maybehave differently for different types.

                      1. 1

                        You get compile-time guarantees with reified generics too; and they end up being safer and more expressive (and faster in a lot of cases). I’m not sure what you’re arguing for other than less expressive systems are easier to understand.

                        1. 1

                          A lot of guarantees the reader would expect from a parametric function can be violated if that function uses runtime type information. https://failex.blogspot.co.uk/2013/06/fake-theorems-for-free.html gives a few simple examples - const3 is only possible to implement because the types are present at runtime. The only thing that reified generics give you compared to erased generics is the ability to implement functions that use the mechanism that const3 uses, and such functions are a bad idea.

                          (There are problems that you might assume could only be solved by writing functions like const3 - you mentioned ORMs and serialization - but actually it’s perfectly possible to solve those problems without reified generics)

                          1. 1

                            You’re mixing paradigms here.

                            I’m an ORM. I see a type. I need to create a binder at runtime to take a database row result and map it to the fields on that type. I can either have the user create mapping functions to do this manually, or I can examine the type, dynamically emit that mapping function for them (in .net you can do this with IL/Lamda.Compile()), saving the developer a lot of steps per type.

                            One runtime approach creates a lot of manual work for the developer. One does not. The second order affects that reification creates for functional programming (specifically the corner cases you outlined) do not outweigh the other benefits of having full type information at runtime. Especially in a runtime that can dynamically emit and load types.

                            1. 1

                              I’m an ORM. I see a type. I need to create a binder at runtime to take a database row result and map it to the fields on that type. I can either have the user create mapping functions to do this manually, or I can examine the type, dynamically emit that mapping function for them (in .net you can do this with IL/Lamda.Compile()), saving the developer a lot of steps per type.

                              Again, look at how this is done in Haskell or Scala. You put a typeclass constraint on your loader function, and the language uses typeclass derivation to generate the mapping codepath at compile time. You get the safety of compile-time type checking, but the developer doesn’t have to do any extra steps (depending on the language they might have to add a marker like deriving (DatabaseMappable), but they never have to manually implement the mapping functions).

          2. 9

            I didn’t see any superpowers here. Just the most basic of typing. Let’s wait for the second part, I guess?

            1. 3

              When compared to vanilla JavaScript, some developers believe types are superpowers.

            2. 4

              We are using TypeScript and FuseBox at work. TypeScript good: lots of popular libraries have third-party type declarations, it’s fast, it’s constantly updated, it works great with React Native, & FuseBox is a total (and essentially drop-it-in) replacement for Babel for us. TypeScript bad: verbose error messages more confusing than helpful sometimes & function parameters and return values stuck at bivariance. Shocking that the jump from untyped to typed JS was so smooth. Heard similarly great things about Flow.

              Overall it’s a great time to write iOS apps where simple views are just purely functional, completely typed React Native components! Use redux reducers as your StateT over some RootStore state; use jest.mock() and redux-saga to discharge side effects and allow for unit tests. The only warning I’ll give is that you should keep view controllers in ObjC – nobody has taken the time to patiently transcribe all of Apple’s delegate methods into JS yet, and even simple apps will want to break out of the JS world.

              You know, I remember with great fondness writing JS as one big spindly blob with jQuery and maybe whatever half-baked implementation of classes you wrote. No tooling, no MDN, no linter or prettier or anything. The lawless days where a DOM node could be pushed around however you wanted. Tumbleweeds. Sand. Water every thousand miles. Fondness.

              1. 3

                Having worked with TypeScript for little under a month now I’m not so convinced. I’m meeting a lot of friction where there either are no types for a library, which isn’t all that bad, or they’re incorrect and actually preventing you from using the library as its intended to be used. That combined with JavaScripts incredibly weak dynamic types ends up with the result that even though TypeScript thinks your typing is sound some library may take your array and silently make it in to an object without any errors being raised. During this month TypeScript has helped me once, when I mistyped a name to some existing but very similar name (project -> product). Other than that the errors have turned up at run time where TS holds no power.

                Thus far I much prefers ClojureScripts much better dynamic types to TypeScripts static and somewhat ineffective static proofs. But that could of course change as I get further in.

                1. 1

                  I personally prefer Flow over TypeScript, but the general idea of adding types to JavaScript is something I’m all for. One of my favourite things about both of these tools is being able to, for example, change the shape of my Redux store and then follow the errors Flow/TypeScript generates to find every part of the codebase which that change affects.

                  1. 2

                    How are you finding flow+redux?

                    I spent >30 unsuccessful hours trying to get connect to return a useful component with the right Props (and learned all the secret/experimental features doing so).

                    I’d like to try TS on my next project; flow has burned me by being silently unsound.

                    1. 1

                      Yeah react-redux’s connect() HOC is the one place where I’ve never had much luck with Flow. The main issue seems to be an underlying issue in the Redux library definitions, though there’s a PR to fix those which I haven’t tried myself https://github.com/flowtype/flow-typed/pull/1731

                      Edit: Just threw those new definitions into a medium size project and they work great. Even helped me pick up one or two subtle bugs.

                      1. 15

                        Glad it worked for you.

                        That said, the thought of using two-day-old code from an open pull request is reminding me why I’m trying to avoid the front-end space right now.

                        1. 2

                          I want to upvote this a thousand times.