A 'CSS reset' for TypeScript, improving types for common JavaScript API's

  • With the ‘unknown’ type available is there a good case for ‘any’ anymore?

    I really like the ‘unknown’ type. In catch statements for example the error is unknown but you can do dynamic checks to see what it is, then typescript lets you treat it that way.

  • It's a weird choice that this library makes some JS APIs more strict, and others less strict. Specifically, the changes to JSON/Array.isArray make sense, the value is unknown, and coercing it to any encourages leaking that type further, or runtime errors. However, the changes to includes/Set basically amount to "I said that this array contained this enum, but I changed my mind when I wrote this if statement". This actually allows more bugs to slip through, rather than fewer.

  • As a side note, in editors using js/ts language server from VSCode, you can benefit from these types even in "vanilla" JavaScript with JSDoc. It could look like:

        // @ts-check
        /// <reference path="./ts-reset-0.3.7/src/entrypoints/recommended.d.ts"/>
        // = contents of the archive from
        // https://github.com/total-typescript/ts-reset/releases
        
        const allCurrencies = /** @type {const} */(["EUR", "USD"]);
        /**
         * @typedef {typeof allCurrencies[number]} currency
         */
        
        allCurrencies.includes('XXX');
        // No 'Argument of type '"XXX"' is not assignable…' error any more.
        // Would be that error without ts-reset.
        
        /** @type {currency} */
        let foo = 'EUR'; // OK
        /** @type {currency} */
        let bar = 'XXX'; // that error, expected

  • Why not just send a pull request to TypeScript if some of the types are incorrect?

  • I've been using kotlin-js for the past few years. Not an obvious choice for a lot of typescript users but IMHO something that would lead to a lot of obvious improvements to typescript if more people would. Like being more strict by default and getting rid of a lot of the javascript compatibility. This library seems like a noble attempt to patch it up somewhat. But why not go all the way and just fix the language properly?

    Kotlin and typescript are actually similar enough that transitioning from one to the other isn't that big of a deal. The key difference is that typescript tries to maintain compatibility with javascript whereas it's just a compilation target for kotlin-js.

    Like with typescript, you use type safe wrappers for any javascript code you need to access. You can actually reuse typescript type definition files and generate Kotlin ones from them. Not perfect, sadly, but it works for simple libraries and you can manually deal with the more complicated ones. I use things like fluent-js, maplibre, and a few other libraries. I don't use react, but a lot of kotlin-js developers do. There are no real limitations on what you can use here.

    The one compromise kotlin-js makes to enable interfacing with javascript code is the dynamic keyword, which you use to do the bait and switch style APIs you see a lot in the javscript world. It could be a list, a number, or a string, etc. Dynamic allows you to work with such APIs. It's a necessary evil. But otherwise, it's all good. It's strict by default. It doesn't have a mode where it is not strict. And this is what enables IDEs like intellij to be a lot smarter with Kotlin than it is with typescript.

    If you ignore the few syntax and language features that are unique to either Kotlin or Typescript, the key difference is that typescript is more sloppy and it's mostly because of js compatibility. And since kotlin-js has almost none of that and can manage fine without it, it kind of proves that this level of sloppiness is simply unnecessary and redundant. A whole lot of downsides and not a lot of upsides. Typescript is much more sloppy than it needs to be. Making it less sloppy is the obvious way to improve it. It's why I use kotlin-js instead.

  • Interesting. Do these represent oversights in TypeScript's builtin type definitions, or are they artifacts of legacy considerations?

  • Interesting! The example

    [1, 2, undefined].filter(Boolean). // number[]

    is interesting because it makes use of the fact that the types of individual array members are known at compile time. I can think of some times when I have dealt with arrays that are defined at compile time, but not where I've needed to call .filter on such an array. Is there a common use case here?

  • I’m not sure anybody has ever convinced me that unknown is meaningfully better than any, in any scenario (pun). I get that it’s “typesafe” on the surface but we both know you’re going to turn around and cast it to the type you think it is so what’s the point

  • [dead]