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

[TypeScript] does not actually ensure that the data you are manipulating corresponds to the type that you have declared to represent it. For example, the data might contain additional fields or even incorrect values for declared types.

This is only a problem if you are mixing untyped code with typed code, isn't it? Like when you are parsing JSON, you need to do typechecking right then, rather than just using an "any" type. The only other situation I have run into this in practice with TypeScript is when I'm using a library that has slightly misdefined types.



> This is only a problem if you are mixing untyped code with typed code, isn't it?

I find it a bit strange that people talk about this as "only a problem", as though it was some weird niche edge case and not an ever-present issue. The written-in-plain-JS ecosystem completely dwarfs the written-in-TypeScript one; unless you're doing something rather trivial you're quite likely to end up depending on a library that barely had the shape of its objects in mind during development, with typings (if they're even present) that were contributed by someone that's almost definitely not the actual library author.

Of course, if you're competent enough you can correct typings and always remember to run runtime checks on data that doesn't come from you and so on. But it's too easy for a beginner to fall into traps like mishandling data that comes from a dependency (especially when it's buggy/ostensibly has typings) - in my opinion, there should be a first-party compiler/linter option to enforce checks on any `any`.


You don't have to depend on badly-written untyped third-party libraries, just because there are a lot of them out there. Many projects and companies will avoid doing so. This is especially reasonable if your comparison is switching to a language like Rust; there are probably fewer third-party libraries available in Rust overall than there are libraries with accurate TypeScript definitions available.


"Badly-written" is of course subjective, but as for untyped/loosely typed I think it's a bit difficult to claim that people are avoiding using them when (for example) the typings for an obviously popular library like Express are chock-full of `any` types[0]. Including several (like request bodies) that should instead be `unknown`. I'm sorry but it's rather naïve to expect a beginner to TypeScript, especially one that's coming from JS, to not trip up at all using typings like that and a compiler/language spec that implicitly casts things declared as `any`.

0. https://github.com/DefinitelyTyped/DefinitelyTyped/blob/mast...


You still have to validate input whenever you interact with servers, the operating system, or the user.


No, nominal types are extremely useful to ensure the correctness of your software. You can define a function to receive a FirstName and LastName instead of passing strings so you cannot accidentally mix up the parameters for example. There are several techniques to approximate nominal typing in typescript (https://medium.com/better-programming/nominal-typescript-eee... for example) and there is an open issue https://github.com/Microsoft/Typescript/issues/202 but it’s still not being considered for implementation as far as I remember.


That's correct. You have to insert validation code at all the entry points, which was the case for me.

Moving to Rust doesn't eliminate validation altogether, but you don't have to do any type related validation which is nice.


Not sure if this is a place to ask this but if someone does not have experience working with Javascript, they might have trouble reasoning about this code:

https://codesandbox.io/s/is849

edit: complete code

```typescript

class Person { id: number; name: string; yearOfBirth: number;

    constructor(id: number, name: string, yearOfBirth: number) {
        this.id  = id;
        this.name = name;
        if (yearOfBirth < 1900 || yearOfBirth > 2020) {
            throw new Error("I don't understand you. Go back to your time machine.");
        } else {
            this.yearOfBirth = yearOfBirth;
        }
    }

    getAge(): number {
        const currentDate: number = new Date().getUTCFullYear();
        return currentDate - this.yearOfBirth;
    }
}

class Dog { id: number; name: string; yearOfBirth: number;

    constructor(id: number, name: string, yearOfBirth: number) {
        this.id  = id;
        this.name = name;
        if (yearOfBirth < 1947 || yearOfBirth > 2020) {
            throw new Error("I don't understand you. Go back to your time machine.");
        } else {
            this.yearOfBirth = yearOfBirth;
        }
    }

    getAge(): number {
        const currentDate: number = new Date().getUTCFullYear();
        return (currentDate - this.yearOfBirth) * 7;
    }
}

const buzz: Person = new Person(1, `Buzz`, 1987);

const airbud: Dog = buzz;

console.log(`Buzz is ${buzz.getAge()} years old.`);

console.log(`Airbud is ${airbud.getAge()} years old in human years.`);

```


Unfortunately HN doesn't support commonmark's triple backtick code blocks. You'll need to use 4 spaces before each line of code.


Two spaces. Four will work, of course, it just wastes a bit of horizontal screen space.


What’s confusing? I must’ve missed it in my cursory scan.


Dog and Person are structurally the same so you can assign a person to a dog and vice versa. But that's just how structural typing works and as a user of TypeScript I haven't run into a case where this'd be an issue.


Haha oh. Yeah, assumed that the Dog definition wasn’t worthless. Indeed TS is structurally typed. And that’s nice!


I worked on a web based editor. A library would give us a range to highlight, in 1-based coordinates. The editor control was 0-based. As you can imagine it was easy to forget to translate back and forth in one path or another. In a strongly typed language I would simply define two Range types and the compiler would eliminate the mistake. I assumed Typescript could help me in the same way but it allowed the two types to be interchanged silently because they had the same structure. Perhaps I was holding it wrong?


Typescript has two hacks that help with mixing of similar data and introduce somewhat-nominal typing - branding and flavoring [1]. Also see smart constructors [2] for more functional approach.

[1] https://gist.github.com/dcolthorp/aa21cf87d847ae9942106435bf...

[2] https://dev.to/gcanti/functional-design-smart-constructors-1...


That's the most they could simplify the code to make the point?


A person value assigned to a dog variable


No[1], TypeScript is unsound in many ways. Most of them are intentional trade-offs to catch as many bugs as possible while not being too restrictive/allowing most JavaScript code to be annotated.

[1]: https://codesandbox.io/s/te0pn?file=/index.ts

Edit: Apparently TypeScript playground links cannot be shared :( Edit2: Published on codesandbox.io, hopefully that works


> The only other situation I have run into this in practice with TypeScript is when I'm using a library that has slightly misdefined types.

Unfortunately there's no way to know which libraries have defined their types correctly, so you end up having to check the types you get back from every library you use.


When getting external data, it always good to do a combination of casting and validating. There are a lot of good libraries to help you with this: https://github.com/moltar/typescript-runtime-type-benchmarks




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

Search: