Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I don't know that many languages but, having been writing lots of typescript in the last 3 years there are so many things I love about it.

It infers types. If I do

    const data = [
      { name: 'bob', age: 35, state: 'CA' },
      { name: 'jill', age: 37, state: 'MA' },
      { name: 'sam', age: 23, state: 'NY' },
    ];
Typescript knows data is an array of { name: string, age: number, state: string }. I don't have to tell it.

Further, if I use any field, example

    const avg = data.reduce((acc, { age }) => acc + age, 0) / data.length;
It knows that `age` is a number. If I go change data and add an age that is not a number it will complain immediately. I didn't have to first define a type for data, it inferred it in a helpful way.

Further, if I add `as const` at the end of data, then it will know 'state' can only be one of `CA`, `MA`, `NY` and complain if I try to check it against any other value. Maybe in this case 'state' was a bad choice of example but there are plenty of cases where this has been super useful both for type safety and for code completion.

There's insane levels of depth you can build with this.

Another simple example

    const kColors = {
      red: '#FF0000',
      green: '#00FF00',
      blue: '#0000FF',
    } as const;

    function keysOf<T extends string>(obj: { [k in T]?: unknown }): readonly T[] {
      return Object.keys(obj) as unknown[] as T[];
    }

    type Color = keyof typeof kColors;
    const kAllColors = keysOf(kColors);
Above, Color is effectively an enum of only 'red', 'green', 'blue'. I can use it in any function and it will complain if I don't pass something provably 'red', 'green', or 'blue'. kAllColors is something I can iterate over all colors. And I can safely index `kColors` only by a Color

In most other languages I've used I'd have to first declare a separate enum for the type, then associate each of the "keys" with a value. Then separately make an array of enum values by hand for iteration, easy to get out of sync with the enum declaration.





What you are describing is structural types. It is indeed a mystery that these are so under used, especially as they are a cornerstone of type theory. Structural types are so useful that they creep into most languages in some way. Even in Java, the Kingdom of the Nouns, where the rulers refused to merge a pair class, functions essentially take tuple arguments and these tuples don't have to be named and defined. You can't return a tuple though, so there is an unfortunate asymmetry. In Haskell and OCaml, we like to describe functions in a structural way, so com.google.android.Predicate would be just "a -> Bool". You wouldn't have to convert your com.google.guava.Predicate. But even these languages lack structural records and variants and suffer for it.

By now I'm also convinced that structural typing is a better default. Nominal types are still useful to assign properties that are hard or impossible to encode with structure alone (e.g. string that is a valid email address) . But I haven't encountered any other examples yet, where I'm missing nominal types.

OCaml has structural variants in the form of polymorphic variants. On the product side, OCaml has objects, first-class modules, labelled tuples (since OCaml 5.4) which are all a form of structural records, with different trade-off in term of ergonomics.

Inferred types are really great, but I feel they do not scale. Inside a method/function? Generally fine, but the signature, including the return type, had better be explicit.

Also, I think there was some performance issue with too much inference? Could be wrong, could also be fixed.


This. As soon as you need to use the type in another function (e.g. function parameter), you'll discover that it's better to just write it out.

They do scale, but explicit types have two bonuses in my eyes:

1. can be read without a compiler, useful when reading PRs

2. They make the compiler work less, it's easier to check than infer


Type inference is part of every modern language. Structural typing not so much, though.

Java has it through a compiler extension (https://github.com/manifold-systems/manifold/tree/master/man...) but I don't know many other languages that support this feature.

As much as I dislike the entire Javascript runtime environment, I find TypeScript to be the best typing system out there for any imperative language.


> I don't know many other languages that support this feature

The other widely-used one is Go with its structurally-typed interfaces.


Right... But Go is exclusively structurally typed, whereas Java with the manifold compiler plugin complements the type system's nominal foundation -- a class can still implement a structural interface nominally. Doing so enforces intention and helps both people and tooling comprehend code faster and more efficiently. But the general idea is to use an interface structurally where it otherwise can't be used nominally or doing so is more complicated.

Not surprisingly, C# worlds the same way with the only exception that you have to declare the variables as “var” but they are still strongly typed

It's also not quite as easy to declare and build an array. You can't just write

  var arr = [ 1, 2, 3];
You have to write something like

  var arr = new int[] { 1, 2, 3 };
However if you have previously declared `arr` to be of type `int[]` (or another collection type such as `List<int>`), then you can write

  arr = [ 1, 2, 3 ];
I haven't used TypeScript so don't know if it distinguishes between arrays and lists, and if so how it determines one or the other in inferred types. Would be curious to know.

Technically you're also allowed to use anonymously typed objects as inferred generic typed parameters in method calls.

Even though that works reasonably well, you may still want to explicitly define your types/interfaces. In this example, if I do need to hardcode the data instead of fetching it somewhere, I would define the interface with "state" being a union of values, because there is nothing that stops you from using `state: 'ABC'` which would be hard to discover.

Lots of languages can infer types. And your last example with the colors is just a dictionary.

It's not a dictionary (type-wise). "as const" is the magic ingredient.

Most languages have poor support for structural types though. If you try and join two records together (like a SQL join), what will your favourite language infer then?

C# has anonymous types which is pretty much the same thing. Though I prefer to declare actual types for most usecases, I'll only use anonymous types for intermediate results and such.

I certainly don't mean to knock nominal types. But I think structural types are more fundamental. A language would only need a single "newtype" or "nominal" keyword to create nominal types from structural types.

Why structural is more fundamental?

C#'s anonymous type shares some flexibility of structural type system even though it still a nominal type.

  > A language would only need a single "newtype" or "nominal" keyword to create nominal types from structural types.
I think you also can add `structural` keyword & apply structural type system in generally nominal type system as well if we're talking about adding feature.

Claude is "very good" in applying >var< continuously :-D

dictionaries generally aren't guaranteed to contain an entry for every possible value of the key type. while you could implement the colors example with a dictionary, ideally you'd want the type system to assure that given a Color, there will be a string associated with it

Sounds like enums with extra steps.

Enums aren't type safe in typescript

Sounds like a major flaw.

it’s Enuma associated with data without having to repeat yourself

If you have to define the Enums in one place and then repeat them all in another just to associate data with each one you’ve failed


Maybe you can elaborate but enums in Java or Kotlin easily have data associated with them.

    enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
    }



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: