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 definition and require Node.js 6 #17

Merged
merged 15 commits into from Aug 27, 2018

Conversation

DenisCarriere
Copy link
Contributor

@sindresorhus
Copy link
Owner

Thanks for creating a type definition 🙌

@DenisCarriere
Copy link
Contributor Author

@sindresorhus You're welcome 😄 I've been looking forward to add Typescript definitions to your repos for a while.

Glad you've started accepting them 👍

@DenisCarriere
Copy link
Contributor Author

DenisCarriere commented Aug 20, 2018

Still getting a few errors when running tests, not too sure if these are valid errors. 🤔

  1. xo doesn't seem to handle Typescript definitions very well
  2. prefer-destructuring doesn't seem like it's possible to deconstruct on line 52

CC: @sindresorhus

$ npm t

> write-json-file@2.3.0 test /Users/denis/Github/write-json-file
> xo && ava


  index.d.ts:1:6
  ✖   1:6  Parsing error: Unexpected token Replacer

  index.js:52:4
  ✖  52:4  Use object destructuring.                 prefer-destructuring

  2 errors
npm ERR! Test failed.  See above for more details.

index.d.ts Outdated
indent?: string | number | null;
detectIndent?: boolean;
sortKeys?: boolean | ((a: string, b: string) => number);
replacer?: Replacer | Array<number | string> | null;
Copy link
Owner

Choose a reason for hiding this comment

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

This should not be null.

index.js Outdated
module.exports.default = (fp, data, opts) => {
return makeDir(path.dirname(fp), {fs})
.then(() => init(main, fp, data, opts));
};
Copy link
Owner

Choose a reason for hiding this comment

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

Instead of duplicating the code here, assign the method to a variable and then assign that variable to both module.exports and module.exports.default.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 makes sense

package.json Outdated
],
"types": "index.d.ts",
Copy link
Owner

Choose a reason for hiding this comment

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

This is not needed. From the style guide:

You don't need to add a typings field to package.json as TypeScript will infer it from the name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, didn't know this wasn't required, good to know.

index.d.ts Outdated
}

/**
* Stringify and write JSON to a file atomically
Copy link
Owner

Choose a reason for hiding this comment

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

Should end in a dot.

index.d.ts Outdated

interface Options {
indent?: string | number | null;
detectIndent?: boolean;
Copy link
Owner

Choose a reason for hiding this comment

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

Is there any way to define default arguments in the types themselves instead of in the docs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question, I don't think it's possible as an interface.

[ts] A parameter initializer is only allowed in a function or constructor implementation.

You can do it if the types are created from a .ts file

index.d.ts Outdated
* @param {boolean} [options.detectIndent=false] Detect indentation automatically if the file exists.
* @param {boolean|function} [options.sortKeys=false] Sort the keys recursively. Optionally pass in a compare function.
* @param {function|Array<number|string>|null} [options.replacer] Passed into JSON.stringify.
* @param {number} [options.mode=0o666] Mode used when writing the file.
Copy link
Owner

Choose a reason for hiding this comment

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

From the style guide:

@param should not include the parameter type.

Since it's inferred from the type definition already and just results in duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

index.d.ts Outdated
* @param {number} [options.mode=0o666] Mode used when writing the file.
* @returns {void}
* @example
* const writeJsonFile = require('write-json-file');
Copy link
Owner

Choose a reason for hiding this comment

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

Use import

index.d.ts Outdated
*
* writeJsonFile('foo.json', {foo: true}).then(() => {
* console.log('done');
* });
Copy link
Owner

Choose a reason for hiding this comment

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

Use this instead:

(async () => {
	await writeJsonFile('foo.json', {foo: true});
	console.log('done');
})();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 I like this example as well

index.d.ts Outdated
*
* @param {string} filepath Filepath
* @param {any} data Data
* @param {object} [options] Optional parameters
Copy link
Owner

Choose a reason for hiding this comment

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

No need to have a description for these 3 parameters as the name is clear enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough, dropping those params can be extracted from the Types

@sindresorhus
Copy link
Owner

xo doesn't seem to handle Typescript definitions very well

Yeah... You have to exclude the type definition file from being linted because of xojs/xo#348. Example.

@sindresorhus
Copy link
Owner

I'm also looking for feedback on the style guide. Anything that could be clearer/improved? Anything you strongly disagree with? You should probably read it again as I have updated it after reviewing this PR.

package.json Outdated
},
"scripts": {
"test": "xo && ava"
"test": "xo --ignore index.d.ts && ava"
Copy link
Owner

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Sounds good!

index.d.ts Outdated
export function sync(filepath: string, data: any, options?: Options): void;

/**
* Stringify and write JSON to a file atomically
Copy link
Owner

Choose a reason for hiding this comment

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

Should end in a dot.

index.d.ts Outdated
*
* Creates directories for you as needed.
*
* @returns {Promise<void>}
Copy link
Owner

Choose a reason for hiding this comment

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

This is moot as it has no description and therefore is better documented by the type definition itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 yea it's pretty pointless, it is defined by the Types

index.d.ts Outdated
*
* Creates directories for you as needed.
*
* @returns {void}
Copy link
Owner

Choose a reason for hiding this comment

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

index.d.ts Outdated
*
* @returns {void}
* @example
* import * as writeJsonFile from 'write-json-file';
Copy link
Owner

Choose a reason for hiding this comment

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

Should be import writeJsonFile from 'write-json-file';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤔 ... to access .sync() you need to add import * as

Copy link
Owner

Choose a reason for hiding this comment

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

Ah, good point 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess if you use Babel this example is correct, I'll change it to:

import writeJsonFile from 'write-json-file';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most Typescript users kinda got used to adding import * as, no need to include it.

Most docs are mixed between Babel & Typescript, we can keep as the Babel way

index.d.ts Outdated
* import writeJsonFile from 'write-json-file';
*
* (async () => {
* await writeJsonFile('foo.json', {foo: true});
Copy link
Owner

Choose a reason for hiding this comment

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

Incorrect indent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤦‍♂️

@sindresorhus
Copy link
Owner

Need feedback on: #17 (comment)

Is there any way to define default arguments in the types themselves instead of in the docs?

We need to define default values somewhere for improved auto-completion experience.

@sindresorhus
Copy link
Owner

// @brandon93s @NeekSandhu Would you be able to help review this?

@DenisCarriere
Copy link
Contributor Author

@sindresorhus I've looked over all your comments and I hope we've covered everything 😄

Got another PR sindresorhus/load-json-file#15 with the same changes.

I'm done for commits, unless you have more comments and reviews.

Let me know if you come up with a solution for #17 (comment)

@brandon93s
Copy link

Looks good to me.

For #17 (comment), unfortunately I don't think there is a good way for defaults to be defined on an interface. Adding the default to the interface documentation would at least make it visible in the intellisense:

/**
 * Detect indentation automatically if the file exists.
 * 
 * Default: false
 */
detectIndent?: boolean;

image

index.d.ts Outdated
* writeJsonFile.sync('foo.json', {foo: true});
* console.log('done');
*/
export function sync(filepath: string, data: any, options?: Options): void;

Choose a reason for hiding this comment

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

data can't really be any. For example, passing in undefined will throw a TypeError. We could attempt to express valid JSON values to be more specific here.

Copy link

@zikaari zikaari Aug 21, 2018

Choose a reason for hiding this comment

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

Consider the fact that even in TypeScripts "official" lib.d.ts file, the signature for JSON.stringify( ... ) is

JSON.stringify(value: any, replacer?: (key: string, value: any) => any, ...): string;

That means even the TypeScript team couldn't come with a reliable, sound way to describe JSON.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's always the object type, however I kinda like using any for this param.

Copy link

@zikaari zikaari Aug 21, 2018

Choose a reason for hiding this comment

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

@brandon93s was hoping to prevent passing undefined (which causes TypeError), the problem is that no signature of any kind (as of ts@3.0.1) can prevent that from happening.

function foo(a: object | number | string): void { }

// ts won't complain i.e no red squiggly lines
foo(undefined)
// no complains here either
foo(null)

EDIT

I was wrong, I forgot about strictNullChecks option that TypeScript has for enforcing this. So in theory data: object | string | number is better than data: any. It'll remind the user of bad input as long as they have strictNullChecks options engaged.

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, let's make it strict. Can be a type called JSONStringifyable maybe.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 for making it more strict in the data param.

There's two options:

  1. We create a type called JSONStringifyable (the type is slightly "hidden" when using intellisense)

screen shot 2018-08-21 at 9 57 48 am

  1. We add the types directly in the data param, that way it's clear that the input types are string|object|number (my preferred choice).

screen shot 2018-08-21 at 9 59 41 am

package.json Outdated
}
},
"xo": {
"ignores": [
Copy link
Contributor

Choose a reason for hiding this comment

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

The indentation is off. These lines are using tabs and the others are using spaces.

I know @sindresorhus switched to tabs for package.json because npm preserves them nowadays. But think it should be done in a separate commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 fixed

@zikaari
Copy link

zikaari commented Aug 21, 2018

As far as #17 (comment) goes, like @brandon93s said, unfortunately not possible yet. I've been using the method he advised, i.e to describe the default value in comment(s), works really well IMO.

Future commit will convert package.json to tabs
@DenisCarriere
Copy link
Contributor Author

Added defaults using the Default: '\t' schema in the comments.

@sindresorhus
Copy link
Owner

According to https://twitter.com/felixfbecker/status/1031660800413454337, we can use the @default tag, so let's do that.

@zikaari
Copy link

zikaari commented Aug 21, 2018

wow...totally TIL thing. It's even worth mentioning in your style guide.

@@ -1 +1,2 @@
node_modules
yarn.lock
Copy link
Contributor

Choose a reason for hiding this comment

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

Add newline at the end of the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added newline

@zikaari
Copy link

zikaari commented Aug 21, 2018

While the context is heated up, have you considered sindresorhus/ama#554

@DenisCarriere
Copy link
Contributor Author

@SamVerschueren @sindresorhus

* Creates directories for you as needed.
*
* @example
* import * as writeJsonFile from 'write-json-file';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sindresorhus FYI: I've changed it back to import * as syntax for the .sync() example.

Reason: Since this is a Typescript definition, it makes more sense to reference the example in the "Typescript" way.

index.d.ts Outdated
* writeJsonFile.sync('foo.json', {foo: true});
* console.log('done');
*/
export function sync(filepath: string, data: object | number | string, options?: Options): void;
Copy link
Owner

Choose a reason for hiding this comment

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

I think we should make object | number | string a named type called JSONStringifyable? It would more clearly explain why it randomly only accepts object/number/string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok! I'll add it

index.d.ts Outdated
*
* @default false
*/
sortKeys?: SortKeys | boolean;
Copy link
Owner

Choose a reason for hiding this comment

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

I think boolean should be first as it should be sorted by most common usage, and most users will only ever need to set {sortKeys: true}.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 sounds good to me

@sindresorhus sindresorhus changed the title Add TypeScript definition Add TypeScript definition and require Node.js 6 Aug 27, 2018
@sindresorhus sindresorhus merged commit 8a602ea into sindresorhus:master Aug 27, 2018
@sindresorhus
Copy link
Owner

This is perfect now. Thanks for your great work and patience, @DenisCarriere 🙌

@DenisCarriere DenisCarriere deleted the typescript branch August 29, 2018 19:21
@DenisCarriere
Copy link
Contributor Author

@sindresorhus You're welcome! Thanks for building awesome open source tools! 🙌

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

Successfully merging this pull request may close these issues.

None yet

5 participants