Hacker News new | past | comments | ask | show | jobs | submit login

There's been considerable convergence on this. Most languages now have some degree of type inference for statements now. Even C++ has "auto". That substantially reduced the amount of type boilerplate in code. Remember having to write out long iterator types in C++ "for" statements? We're past that.

As for function declarations and structure fields, that's where you need type information to read the code. Once a program gets beyond a few hundred lines or beyond one developer, some amount of annotation is essential.

The main objections come from Python and Javascript users, of course. Python retrofitted a very strange advisory typing system, and Javascript retrofitted TypeScript. Both are bolt-on type systems and are used in mixed typed/untyped environments. That is painful.

LISP also got a type system retrofit decades ago, with "flavors" and the Common LISP Object System, and that was ugly, too. The lesson here is that retrofitting a type system creates a mess.




I actually think Python's type system is pretty good, given the circumstances. It's got some nice features like Optional forcing checks for None values before use, and structural subtyping with typing.Protocol. Could it be better if Python were designed with types from the ground up? Yeah, of course. But given the requirements of integrating with all existing Python code and not breaking any existing code I think it does a decent job.

The bigger issue with Python and static typing is the ecosystem and conventions that a lot of developers use, made worse by a lot of these developers really being data scientists who are writing Python. *args/**kwargs are heavily abused by people too lazy to write proper method signatures. It's extremely common in Python to have methods pass around DataFrames or dictionaries as a grab-bag of stuff. Bonus points when methods add and remove columns/fields so you don't know what's in the data bag until you run the code (or read every line).

You can do this in almost any language, of course. Nothing stops you from writing a C# program with every type being `dynamic` or have all your Go methods accept `interface{}`. But Python, for a long time, actively encouraged this approach, and sadly there's still many beginner tutorials today that present things like "just write a method that takes *kwargs and you don't have to change the function signature!" as an advanced language feature for smart people instead of an awful footgun.*


Python and Typescript "type systems" were designed for gradual, non green-field typing. It is essential to incremental migration and totally understandable that it works this way.


> LISP also got a type system retrofit decades ago, with "flavors" and the Common LISP Object System, and that was ugly, too. The lesson here is that retrofitting a type system creates a mess.

Flavors and CLOS are not "type systems". They are "object systems" and there is nothing "ugly" about them. Flavors was introduced into a Lisp which did not have a type system. Later CLOS was added to a Lisp which already had a type system: Common Lisp.



TypeScript's type system is flat out amazing though. I wish more type systems were as expressive.


TS' type system is insane in what it's capable of expressing. I've personally wrote a snake game using types alone (you see a board, snake and snacks inside your editor type popups).

Despite that, TS is still quite cumbersome to use due to its initial mission statements. I.e. `Object.keys(myObject)` doesn't return `keyof (typeof myObject)`, so if you'd do something like this: `Object.keys(myObject).forEach(key => myObject[key])` it'll throw an error that key is not assignable to `myObject`. The reasons lie in design choices made in order to be able to gradually migrate an untyped JS codebase into a TS one. There's tons of issues like this, some of them are just completely absurd and make developers that try to achieve simple things throw their laptop out of the window :D


This is an old, old issue, and it's not due to migration but due to inheritance.

`<T>keys(o: T) => keyof T` is actually quite incorrect, because you can't guarentee that keys() doesn't return a value not in `keyof T`, a key could be from a subclass of T.

I think TypeScript is doing the right thing here in not claiming something that's very easily violated.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

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

Search: