Hacker News new | ask | show | jobs
by seepel 1961 days ago
> What thing that violates a type check would be "perfectly fine to do"?

Here is an example limitation of typescript's type system that I routinely run into while developing real code [1].

I look at it like this. If you consider all possible programs, some are invalid, some are valid, and some are valid and useful. A type system's job is to reject as many invalid programs as possible while accepting as many valid programs as possible and trying to optimize for useful valid programs. Due to the halting problem this is impossible to do perfectly, so any given type system will likely accept some invalid programs and reject some useful valid programs. If the type system happens to reject your useful valid program, you'll likely have a bad day :)

[1] https://www.typescriptlang.org/play?#code/PTAEHUFNQCwQwG7TgO...

1 comments

Sometimes the type system is forcing you to think in terms of what it is that you are passing around. In this case, getEmails() isn't expecting a group of students or a group of faculty, but rather a group of people that can be emailed. You can introduce an interface of that type and have it inherited by both Student and Faculty and use that in the method for clarity and type-safety without over-relying on union types: https://www.typescriptlang.org/play?ssl=1&ssc=1&pln=38&pc=30...
I appreciate the effort you’ve both put in to concrete examples. I think yours gets to a point that I haven’t often seen stated. You’ve named your interface Person, but perhaps even Emailable could serve the purpose. The problem is that you had to name it. Naming well is hard, and I believe strong type systems often create a need for more names. It’s a cost I don’t often see considered in the tradeoff.
Instead of giving the interface an explicit name, I think you could instead use something like this:

    function getEmails(group: Array<{email: string}>) {
        return group.map((p) => p.email)
    }
which leaves the function more open ended than relying on an explicitly named interface that other types then have to inherit from.