Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TypeScript declaration. #103

Merged
merged 3 commits into from
Oct 4, 2018
Merged

Conversation

bradleyayers
Copy link
Contributor

This resolves #102

@bradleyayers
Copy link
Contributor Author

Any further thoughts on this @JedWatson?

@cwmacdon
Copy link

cwmacdon commented Dec 8, 2016

@dcousens @bradleyayers

It would be nice if there were definitions for the alternate classnames/dedupe and classnames/bind versions.

How does this look for bind.d.ts?

import { default as base } from './index';

interface IClassNamesBind {
    bind: (styles: any) => typeof base;
}

declare var classNames: IClassNamesBind;

export default classNames;

@bradleyayers
Copy link
Contributor Author

@cwmacdon looks good to me — I'd love to see that get merged in too 👍.

@jednano
Copy link

jednano commented Dec 10, 2016

@bradleyayers is there any reason these definitions don't match the ones on DefinitelyTyped 100%?

Also, the publishing doc states the following:

Also note that if your main declaration file is named index.d.ts and lives at the root of the package (next to index.js) you do not need to mark the "types" property, though it is advisable to do so.

Perhaps you can add it?

{
    "types": "./index.d.ts"
}

@bradleyayers
Copy link
Contributor Author

@jedmao I'm not sure I considered just copying over the @types/ code, maybe I wanted to avoid licensing issues, but using it (or matching it) indeed seems more desirable than having a set of deviating definitions.

Having the .d.ts placed next to the index.js should indeed work. I'll give that a go and update the PR

@jednano
Copy link

jednano commented Dec 11, 2016

It will work, but they still recommend being explicit about it. I think what i was trying to say before its that if what you have here is better than the one on DT, perhaps you should submit a PR to DT first so that they are in sync. If you do so, submit to the types-2.0 branch.

@bradleyayers
Copy link
Contributor Author

I'll compare and take the best and upstream if necessary. Thanks

@cwmacdon
Copy link

cwmacdon commented Jan 5, 2017

Any progress on this @bradleyayers @jedmao? Anything I can do?

@bradleyayers
Copy link
Contributor Author

bradleyayers commented Jan 6, 2017

@cwmacdon I haven't had time to get back to this. But what you could do is to look into whether we can just copy the DefinitionTyped definitions into classnames directly (I'm not sure if there's licensing issues). This would avoid having two different definitions.

If we can, raise a PR to do so 😄

@cwmacdon
Copy link

@bradleyayers So I tried copying the Definitely Typed version into my node_modules/classnames/index.d.ts

Then I wrote a file to use it

import classNames from 'classNames';

and it gave the error
File {.../classNames/index.d.ts} is not a module.

The definition that you have included above works perfectly.

@adidahiya essentially wrote the DefinitelyTyped version, maybe he can weigh in?

@adidahiya
Copy link

there are no licensing issues. I think you are free to copy the definitions I wrote for DefinitelyTyped, just make sure to add the package to notNeededPackages.json. I'll take a look at the code here now

index.d.ts Outdated
@@ -0,0 +1,9 @@
type ClassValue = string | number | ClassDictionary | ClassArray;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index.d.ts Outdated
type ClassValue = string | number | ClassDictionary | ClassArray;

interface ClassDictionary {
[id: string]: boolean;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, this needs to allow undefined and null. also, personal preference (generally in the TS community), can you use 4-space indentation instead of tabs?

index.d.ts Outdated

interface ClassArray extends Array<ClassValue> { }

export default function(...classes: ClassValue[]): string;
Copy link

@adidahiya adidahiya Jan 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most consumers are currently doing import * as classNames from "classnames";, and this will break that. actually I don't even think this is the shape of the export; see this part of the DT readme for guidance.

$ node -p 'require("classnames").default'
undefined
$ node -p 'require("classnames")'
[Function: classNames]

you should use the export = syntax from my DT typings instead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adidahiya There are many ways to do this...
import classNames from 'classNames';
import * as classNames from 'classNames';
import classNames = require('classNames');

Is there a "best" way? Is there a way to write the definition to work with all of the above?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both import classNames from 'classNames'; and import * as classNames from 'classNames'; are generally OK. The last one that uses require() is discouraged because it is old CommonJS-style syntax.

but, like I said, the DefinitelyTyped README stipulates that you should not declare a default export unless require("classnames").default actually returns the module.

Copy link

@cwmacdon cwmacdon Jan 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adidahiya Thanks for your input here. Good to know.

Looking at your definition on DefinitelyTyped, what is the purpose of the classnames module wrapped around the export?

declare module "classnames" {
	export = classNames
}

I think this is the underlying reason why @bradleyayers and myself strayed from using the definitions on DefinitelyTyped? If we remove this wrapping module, then we can just import * as classNames from 'classNames';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you don't need that part. That was a remnant from before we had @types on NPM -- when you use those typings through tsd or the typings tool, you would need the ambient declaration of the "classnames" module. Now that we have node-style resolution of index.d.ts, that part isn't needed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adidahiya Would you recommend updating the DefinitelyTyped definition to remove it as well? Or should we just leave it out in this PR?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, updating the DT definition sounds like a good idea now that the repo has switched over to @types completely. Once a release of classnames is made that includes definitions though, we should remove those typings from DT (and update notNeededPackages.json in the same PR).

@cwmacdon
Copy link

@bradleyayers Thanks to input from @adidahiya and @jedmao, Could you update the PR with the changes outlined below?

  1. index.d.ts with the definition from DefinitelyTyped with one slight change: Remove the declare module "classnames" wrapper around the export.

  2. Add a bind.d.ts with following:

import * as base from './index';

interface IClassNamesBind {
    bind: (styles: any) => typeof base;
}

declare var classNames: IClassNamesBind;

export = classNames;
  1. Change package.json to explicitly point to the type definitions:
    "types": "./index.d.ts"

Then once this is complete and merged, a PR to DefinitelyTyped will be needed to add classNames to the notNeededPackages.json to help users know that the types are included in this package now.

@cwmacdon
Copy link

@JedWatson Any chance a new release could be cut with this PR?

@cwmacdon
Copy link

@dcousens Perhaps you could merge this PR? I see you've kind of taken over merging PRs since mid last year.

@dcousens
Copy link
Collaborator

I'm waiting on @JedWatson to add me as a contributor for publishing to npm 👍

@adidahiya
Copy link

bump again here @JedWatson @dcousens, thanks

@Havret
Copy link

Havret commented Apr 17, 2017

It would be great if it was merged. What are we waiting for?

@adidahiya
Copy link

This PR just merged in DT so you should be able to pick up the latest @types/classnames with most of these improvements. DefinitelyTyped/DefinitelyTyped#16420

@cwmacdon
Copy link

@adidahiya Thanks for the heads up. I have doubts this will ever get merged in here, so @types/classnames will be the best we can get.

@maciejw
Copy link

maciejw commented Feb 12, 2018

@bradleyayers can you tell me why you decided to go with export = classNames; instead of export default, I'm curious

this does not work with allowSyntheticDefaultImports = false witch is default setting in ts.

webpack can handle this when I do

import  * as classNames from 'classnames'

but rollup will say

Cannot call a namespace ('classNames')

with allowSyntheticDefaultImports = true both works fine using this

import  classNames from 'classnames'

export = has many issues and it requires this kind of import

import classNames = require('classnames')

(https://www.typescriptlang.org/docs/handbook/modules.html)


OMG import default does not work with jest and wallaby :) I'm going to kill myself :)

@adidahiya
Copy link

@maciejw you probably want the new --esModuleInterop flag added in TS 2.7.

this does not work with allowSyntheticDefaultImports = false witch is default setting in ts.

Note that this only affects type checking, not JS emit.

See the DefinitelyTyped README for info about why export = syntax is used here.

@maciejw
Copy link

maciejw commented Feb 12, 2018

@adidahiya indeed I have ts 2.7, but I did not change this flag... this esModuleInterop flag break my postcss modules in jest. witch worries me gravely:)

now, what I did is I copied d.ts locally and declared it as another module and that imported types only
to a file and reexported it as below. I had to copy d.ts because types are not visible when export = is used I think.

import * as ns from 'classnames-ts';

const classNames : ns.ClassNamesFn = require('classnames');

export { classNames };

this works fine only with require in all those environments: jest, rollup, webpack

@bradleyayers
Copy link
Contributor Author

bradleyayers commented Feb 12, 2018

@maciejw years ago when I opened this PR I used export =, as at the time this library did not have an ES module export (ES modules are not callable, so import * as classnames from "classnames"; classNames() is not valid — this is what rollup complains about with Cannot call a namespace ('classNames')). The TypeScript method of interop with callable commonjs modules is the export = declaration.

However since then the library has added support for ES modules by way of a default export (915186a). So I recommend this PR be changed to use a default export rather than the callable commonjs module. I've updated the PR to use a default export.

Unfortunately bind.js still is not a valid ES module (no default export), so it needs to stay as export =.

@maciejw
Copy link

maciejw commented Feb 12, 2018

@bradleyayers I stumbled upon similar case with not callable export = in invariant zertosh/invariant#32 library... I'm sure not last one ;) to sum it up:
commonjs 'module.export' = d.ts 'export =' witch can be imported in es6 only with import * as x from 'x' with exception to x cannot be callable, on top of this different bundlers/transpiles are adding logic, to solve this incompatibility, is this picture accurate?
whould it be a better solution to add module=index.es.js in package.json as a shim? d.ts then would be complatible with indes.es.js and index.js would be left for commonjs only

@jednano
Copy link

jednano commented Feb 12, 2018

Isn't it about time this library drop support for dead/unmaintained Node.js versions? It seems only Node.js v0.10 and 0.12 are failing this build.

@bradleyayers
Copy link
Contributor Author

@maciejw I've responded in zertosh/invariant#32 (comment)

@dcousens
Copy link
Collaborator

dcousens commented Jul 20, 2018

Can we not merge this to DefinitelyTyped instead?
Why should it be in this repository, the maintainers don't use TS?
Anyone willing to champion the bindings?

@bradleyayers
Copy link
Contributor Author

Hi thanks for the questions. In regards to whether it can be merged to DefinitelyTyped someone will need to check the DefiniteyTyped repository to confirm it is not already there. I would be surprised if someone has not already contributed them there.

In regards to why the types should be in this repository I suspect the argument for having the types in this repository may simply boil down to https://en.m.wikipedia.org/wiki/Cohesion_(computer_science).

In regards to the maintainers not using TypeScript, I empathise with their plight and would not want them to take on any burden they do not feel comfortable with. They do amazing work for free and they should be not bullied into anything.

In regards to championing the work I believe this PR may qualify.

Copy link
Collaborator

@dcousens dcousens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@jedmao can you rebase your PR off this?

@jednano
Copy link

jednano commented Jul 31, 2018

@dcousens I've rebased #162

@ssylvia
Copy link

ssylvia commented Aug 21, 2018

Any idea when this will be merged or is there anything I can do to help get this merged?

I have not been able to get types working this library or by using @types/classname.

Using:

import classnames from 'classnames';

with "allowSyntheticDefaultImports": true in tsconfig.

@adidahiya
Copy link

@ssylvia you should try enabling both --allowSyntheticDefaultExports and --esModuleInterop

@dcousens dcousens merged commit 2c76e87 into JedWatson:master Oct 4, 2018
@dcousens
Copy link
Collaborator

dcousens commented Oct 4, 2018

Wait, these were copied from DefinitelyTyped...

Please add the copyright notice to the top of this file, it should be enough - though I am not a lawyer - as the LICENSE is the same.

Otherwise I'll revert.

@bradleyayers

@lukescott
Copy link

Is there a reason ClassDictionary is more strict that DefinitelyTyped? Real world example that doesn't work with the stricter version:

<div className={classnames("textField", error, {error})}>

error is a string that contains an error codes, such as "required". If the error is "required" then className will be "textField error required" while no error would be "textField".

With the stricter types you have to do:

<div className={classnames("textField", error, {error: !!error})}>

A bit of a nit-pick I guess, but the first one is a tad shorter and the library itself allows any as it does a truthy check.

Also it looks like the latest published version to npm does not contain the TypeScript definitions.

@theKashey
Copy link

Still not released.

@FDiskas
Copy link

FDiskas commented Mar 17, 2021

Still :D
That means the project is dead?

@JedWatson
Copy link
Owner

@FDiskas it's released

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bundle TypeScript definitions