chalk: Minor version 2.2 is breaking for TypeScript consumers

https://github.com/sourcegraph/javascript-typescript-langserver/issues/371 https://travis-ci.org/sourcegraph/javascript-typescript-langserver/jobs/289329597#L1487-L1490

src/logging.ts(72,40): error TS2339: Property 'grey' does not exist on type 'typeof "/home/travis/build/sourcegraph/javascript-typescript-langserver/node_modules/chalk/types/...'.
src/logging.ts(80,40): error TS2339: Property 'bgCyan' does not exist on type 'typeof "/home/travis/build/sourcegraph/javascript-typescript-langserver/node_modules/chalk/types/...'.
src/logging.ts(88,40): error TS2339: Property 'bgYellow' does not exist on type 'typeof "/home/travis/build/sourcegraph/javascript-typescript-langserver/node_modules/chalk/types/...'.
src/logging.ts(96,40): error TS2339: Property 'bgRed' does not exist on type 'typeof "/home/travis/build/sourcegraph/javascript-typescript-langserver/node_modules/chalk/types/...'.

Before 2.2, with @types/chalk, it was possible to import chalk like this:

https://github.com/sourcegraph/javascript-typescript-langserver/blob/6fcc4e1df9283dbd84db9411aa26e9f49556cb50/src/logging.ts#L2

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/05ac46f9a9307d43e082b7cec4b32f40df3a3b58/types/chalk/index.d.ts#L6

2.2 brings its own types, which override @types/chalk, but they don’t allow this import (only a defaut import):

https://github.com/chalk/chalk/blob/d86db88e778fa856f4d6f5f68c588750ca06b822/types/index.d.ts#L90

Although it seems to work at runtime and appears semantically correct / correct in regard to the JS:

https://github.com/chalk/chalk/blob/d86db88e778fa856f4d6f5f68c588750ca06b822/index.js#L219

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 6
  • Comments: 41 (19 by maintainers)

Commits related to this issue

Most upvoted comments

Obligatory XKCD:

workflow

@sindresorhus thanks for Chalk and your other contributions!

You may not appreciate it, and I know you didn’t author it, but @types/chalk for all intents and purposes was the “official” Chalk API for TypeScript users. Creating an index.d.ts that differs from that API is a breaking change, by definition. The other commenters on this issue are evidence to this effect.

Also, I’ve used upwards of 100 typed packages over the past year, and this typing is the first to require a non-namespaced import chalk from "chalk" style. I’d expect a good deal of issues or questions to arise from this.

For other people wondering why chalk broke for them with a minor release, as stated above, change

import * as chalk from "chalk"

to

import chalk from "chalk"

I also believe current definitions are not done okay and should be exported as namespace first. Upgrading chalk to a new version broked all my projects currently, why not simply use ones from the @types/chalk and update this typings if needed?

Another issue I have with the new TypeScript definitions:

function logBold(msg): string {
  return chalk.bold(msg)
}
 error TS2322: Type 'Chalk' is not assignable to type 'string'
import * as chalk from 'chalk'

being broken is fixed for me with a search+replace to

import chalk from 'chalk'

Shouldn’t the TypeScript typings reflect what’s available at runtime / to JS consumers? I.e. if you remove the module.exports for JS users at some point, that would be a major version and then it can be removed for TS consumers in the same update

@felixfbecker Yep! That only effects what typescript emits (your code). Not declarations within types provided by modules you’re using (eg chalk).

@mceachen The updated type definitions actually fix a few bugs.

import * as chalk from 'chalk'
console.log(typeof chalk) //function

The above syntax for extracting an object of type function from a module should not be possible. While many transpilers allow this, it is not valid es2015

The previous types also did not capture chalk (the default export) as a function, which it is.

Didn’t know that. I loved my import { red } from ‘chalk’;. It would be so nice, if this could officially be added in the future again.

That won’t happen. As I’ve commented earlier, you can just do:

import chalk from 'chalk';
const {red} = chalk;

The Chalk colors are part of the Chalk class and not part of the exported interface directly for reasons explained in my earlier comment.

Didn’t know that. I loved my import { red } from 'chalk';. It would be so nice, if this could officially be added in the future again.

Somehow the “downsides” never really applied to me:

You import these highly generic variable names into the module scope and have to update it every time you need a new style.

The generic names made my easy to read, I never really had troubles with name conflicts and I never needed more than three or four colours at once. (Also auto-importing and removing unused imports made this very ergonomic.)

I’ve also just been broken by this. Despite initial thrashing, the changeover is not too painful. Since examples are useful I thought I’d share the changes I made to switch to this:

Previous import examples:

import { constructor as ChalkConstructor, Chalk, ChalkChain } from 'chalk';

Now looks like:

import chalk from 'chalk';

Usage looks like goes from this:

    const colors = new ChalkConstructor({ enabled: loaderOptions.colors });

const makeLogInfo = (loaderOptions: LoaderOptions, logger: InternalLoggerFunc, green: ChalkChain) =>
//...

function successfulTypeScriptInstance(
    // ...
    colors: Chalk,
    // ...
) {
// ...
}

To this:

    const colors = new chalk.constructor({ enabled: loaderOptions.colors });

const makeLogInfo = (loaderOptions: LoaderOptions, logger: InternalLoggerFunc, green: typeof chalk) =>
//...

function successfulTypeScriptInstance(
    // ...
    colors: typeof chalk,
    // ...
) {
// ...
}

PS every time a package on npm starts shipping it’s own *.d.ts an angel gets his wings 👼 😉

Chalk is by nature a default export. The fact that you can do import { red, green } from 'chalk' is just because CommonJS default export is not enforced as an ES2015 default export when used with TS or Babel. I also don’t think import { red, green } from 'chalk' is a good pattern. You import these highly generic variable names into the module scope and have to update it every time you need a new style. Chalk is the import. The styles (like red, green) are just modifiers, not exports. If you want the colors as variables, you can simply do const {red, green} = chalk;.

You will have to change import * as chalk from 'chalk' at some point regardless, as that’s how it will work when we convert Chalk to be an ES2015 module sometime in the future.

I don’t think so unfortunately - but what is better about

import chalk from 'chalk'

vs

import * as chalk from 'chalk'
import { red, green } from 'chalk'

? The latter seems more flexible to me. Or are there any changes planned to the Chalk API that would disallow this kind of import, e.g. in a native ES6 environment because the chalk has to be a Chalk instance?