DEV Community

Discussion on: Throw away the "Script" from "Type""Script".

 
peerreynders profile image
peerreynders • Edited

Keeping ts files goes against the whole idea of using JSDoc instead of typescript, since you're ending up using both.

Not really. The idea is to blaze ahead while TypeScript isn't getting in your way and only catch up on your types when you choose to.

If you are already using a build tool chain this is easily accomplished by using esbuild which doesn't typecheck at all but outputs JS 20-30x faster than tsc can (standard behaviour when using Vite.js in development mode; if you are using an editor with LSP support many type issues will be flagged at author time anyway but with no-typecheck transpilation you don't need to address them RIGHT NOW, THIS MOMENT just to keep tsc happy).

If you want full control of the runtime JS code:

  • Author complex types in a types.ts file in typescript.
  • When needed import types (perhaps conbined with @typedef) into the *.js file and use them as needed with binding JSDoc syntax.
  • By not manually authoring the index.d.ts TypeScript can still be used to generate it.

With this approach you have 100% control over the runtime JavaScript and can adopt TypeScript in the same manner as ESLint—when you are ready, not when TypeScript demands it (i.e. use TypeScript as a type-linter).

The one downside of this approach:

(Since then I've started using “TS JSDoc” instead of “JSDoc TS” because JSDoc is not the primary concern but its only used as the glue between TypeScript types and Vanilla JavaScript code.)

Thread Thread
 
bwca profile image
Volodymyr Yepishev

I see, thanks for clarification. Somehow from the original article I got the impression that JSDoc allows eliminating Typescript altogether from the codebase, so got curious if it is as powerful when it comes to certain features.

Thread Thread
 
artxe2 profile image
Yeom suyun

Complex types are recommended to be defined in a d.ts file, but I think my first comment was a good answer to your question of "How well does TypeScript support JSDoc?"
Generic types can be written using @template, and JSDoc works the same way as TypeScript when opening curly braces and writing types, so if you move all the code in the playground to JSDoc, it will be in the following form.

/**
 * @typedef Person
 * @property {string} firstName
 * @property {string} lastName
 * @property {number} age
 * @property {Person[]} [children]
 */

/**
 * @typedef Family
 * @property {Person[]} parents
 * @property {{
 *   paternal: Person[]
 *   maternal: Person[]
 * }} grandparents
 */

/**
 * @template T
 * @typedef {Readonly<{
 *   [K in keyof T]: 
 *     T[K] extends (number | string | symbol)
 *       ? Readonly<T[K]> 
 *       : T[K] extends Array<infer A>
 *         ? Readonly<Array<DeepReadonly<A>>> 
 *         : DeepReadonly<T[K]>
 * }>} DeepReadonly
 */

/** @type {Family} */
const family = {
  parents: [
    { firstName: "John", lastName: "Doe", age: 40 },
    { firstName: "Jane", lastName: "Doe", age: 38 },
  ],
  grandparents: {
    paternal: [
      { firstName: "PaternalGrandfather", lastName: "Doe", age: 70 },
      { firstName: "PaternalGrandmother", lastName: "Doe", age: 68 },
    ],
    maternal: [
      { firstName: "MaternalGrandfather", lastName: "Smith", age: 75 },
      { firstName: "MaternalGrandmother", lastName: "Smith", age: 72 },
    ],
  }
};

/** @type {DeepReadonly<Family>} */
const family2 = {
    parents: [
      { firstName: "John", lastName: "Doe", age: 40 },
      { firstName: "Jane", lastName: "Doe", age: 38 },
    ],
    grandparents: {
      paternal: [
        { firstName: "PaternalGrandfather", lastName: "Doe", age: 70 },
        { firstName: "PaternalGrandmother", lastName: "Doe", age: 68 },
      ],
      maternal: [
        { firstName: "MaternalGrandfather", lastName: "Smith", age: 75 },
        { firstName: "MaternalGrandmother", lastName: "Smith", age: 72 },
      ],
    }
};

family.parents = []; // ok
family2.parents = []; // error

family.parents[0].age = 1; // ok
family2.parents[0].age = 1; // error

// error
family2.parents.push({
  age: 40,
  firstName: 'Joseph',
  lastName: 'Doe'
});

/** @type {DeepReadonly<Family>} */
const family3 = family;
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
bwca profile image
Volodymyr Yepishev

Fair enough, thanks! :)

Thread Thread
 
brense profile image
Rense Bakker

@peerreynders funny that you bring up esbuild and vite, as both can run typescript code...

Thread Thread
 
peerreynders profile image
peerreynders • Edited

Yes and that was exactly what I was eluding to.

In some way moving to TS JSDoc is trying to get away from TypeScript being a showstopper and more of a development aid (in the capacity of a type linter).

Application developers who heavily depend on pre-typed dependencies just don't seem to “get it”.

And even when you are authoring applications where you try to minimize dependencies you can easily get into “library style” code where TS can be pain if you just want to quickly explore some solutions. Tools like esbuild and Vite.js can help here while you can still code in TypeScript (while ignoring the LSP red squiggles for now). But not everybody is comfortable with that approach.

And before somebody comes in about Deno:

Therefore, by default, TypeScript modules are not type-checked before they are executed.

From that perspective "TypeScript is pure overhead"—at runtime.

There are other reasons for wanting complete control over the runtime JavaScript; with TS JSDoc you can write just JavaScript but keep TypeScript types while trading off some developer convenience.

Thread Thread
 
brense profile image
Rense Bakker

True, i don't get why people want to see typescript as a show stopper... imho it's because they are inexperienced with typed languages and thus don't understand how to set it up? 🤷 I don't know, I don't remember typescript ever being a showstopper in any of my projects. But I get why some libraries move away from using typescript directly... having to write typescript is appearantly a show stopper for some people and if you are a library maintainer, you don't want to discourage those people from making pull requests because they're convinced typescript stops their show.

Thread Thread
 
peerreynders profile image
peerreynders • Edited

having to write typescript is apparently a show stopper for some people and if you are a library maintainer,

That's not it at all, it's not a “skill issue” especially given the fact that maintaining a TS JSDoc code base typically requires a higher TS skill level than plumbing together an app with TypeScript.

It's about a workflow where “eventual typing” is highly valued (TypeScript is a Subset of JavaScript). Unlike Rust's borrow checker, TypeScript's error messages aren't all that helpful trying to write the “right” code.

When you spike a solution, types like:

github.com/GoogleChromeLabs/comlin...

github.com/ReactiveX/rxjs/blob/9aa...

are just way too much effort (right then and there) just to shut up tsc, especially if later that direction doesn't pan out.

On the flip side TS JSDoc also works well for a type first approach which is generally frowned upon by mainstream TS developers (who prefer to let inference do most of the work).

The various types are designed first and captured in *.ts files and then later referenced in the *.js files via JSDoc to ensure that type linting will flag any errors as close to the source as possible (that and TypeScript always infers an array, never a tuple).

Thread Thread
 
brense profile image
Rense Bakker

Typescript is a superset of JavaScript, not a subset.

You keep talking about shutting up tsc, but I am not sure how the examples you give are supposed to be related to that. If you wanted proper typing with jsdoc, you'd have to write your types like that as well...

Thread Thread
 
peerreynders profile image
peerreynders • Edited

Typescript is a superset of JavaScript, not a subset.

Read the link.

TypeScript cannot type all the stunts you can pull off with JavaScript that work perfectly fine at runtime. At that point it becomes necessary to immediately change the code so that it can be typed and understood by tsc rather than deferring that activity to refactoring later during the make it right phase.

And even when the code can be typed, it can be extremely verbose (i.e. complex) to explain it up front to tsc.

you'd have to write your types like that as well...

TS JSDoc isn't about avoiding types but gaining the freedom to choose when to deal with types. The code can be run and even tested before you have to worry about what tsc has to say about it.

Destroy All Software: Ideology


“I often wish I worked in a language where I could start dynamic and end statically.”

Thread Thread
 
brense profile image
Rense Bakker

So don't explain it upfront to tsc? No part of typescript forces you to write proper types. It's an opt in language not an opt out. If you don't want to write proper types, you don't write proper types for that part of your program... 🤷