TypeScript Adds Incidental Complexity to Using Libraries

Inter-operating with libraries in the JavaScript ecosystem from TypeScript introduces incidental complexity due to packaging and types. There are (at least) four different ways to import a library[0] in TypeScript, each with subtle differences. Depending on the way the library is packaged you will need to pick the correct incantation.

To get the benefit of static typing for a non-typed library, you also need to add @types/name-of-lib to your dependencies. Sometimes these are no longer needed when the library adds TypeScript support as did aws-sdk which, depending on what blog post or StackOverflow question you are looking at may or may not tell you.

Finally, types in a library that has TypeScript support can be done poorly. For example, I found a rather unfortunate issue in marked.js because the Tokens union type doesn’t have a common discriminator property. That makes it not possible, or extremely hack-y to find a specific kind of element[1].

[0]

import { Lib } from 'some-lib'
import 'some-lib';
import * as Lib from 'some-lib';
const lib = require('some-lib');

[1]

export function extractMarkdownTitle(md: string): string | null {
  const lexer_output = marked.lexer(md) as marked.TokensList;

  // Hack: This isn't typeable because Tokens is not a descriminating
  // union type (Def and Space don't have a `type` prop)
  let title: any = lexer_output.find((val: any) => {
    // Without casting to `any`, this won't compile
    return val.type === 'heading';
  });

  return title ? title.text : null;
}