Giter Site home page Giter Site logo

sindresorhus / typescript-definition-style-guide Goto Github PK

View Code? Open in Web Editor NEW
542.0 15.0 10.0 23 KB

Style guide for adding type definitions to my npm packages

License: Creative Commons Attribution 4.0 International

typescript typescript-definitions style-guide

typescript-definition-style-guide's Introduction

TypeScript Definition Style Guide

Style guide for adding type definitions to my npm packages

Open an issue if anything is unclear or if you have ideas for other checklist items.

This style guide assumes your package is native ESM.

Checklist

  • Use tab-indentation and semicolons.
  • The definition should target the latest TypeScript version.
  • Exported properties/methods should be documented (see below).
  • The definition should be tested (see below).
  • When you have to use Node.js types, install the @types/node package as a dev dependency. Do not add a /// <reference types="node"/> triple-slash reference to the top of the definition file.
  • Third-party library types (everything in the @types/* namespace) must be installed as direct dependencies, if required. Use imports, not triple-slash references.
  • Ensure you're not falling for any of the common mistakes.
  • For packages with a default export, use export default function foo(…) syntax.
  • Do not use namespace.
  • Use the name "types" and not "typings" for the TypeScript definition field in package.json.
  • Place "types" in package.json after all official package properties, but before custom properties, preferably after "dependencies" and/or "devDependencies".
  • If the entry file in the package is named index.js, name the type definition file index.d.ts and put it in root.
    You don't need to add a types field to package.json as TypeScript will infer it from the name.
  • Add the type definition file to the files field in package.json.
  • The pull request should have the title Add TypeScript definition. (Copy-paste it so you don't get it wrong)
  • Help review other pull requests that adds a type definition.

Check out this, this, and this example for how it should be done.

Types

  • Types should not have namespaced names; type Options {}, not type FooOptions {}, unless there are multiple Options interfaces.
  • Use the array shorthand type notation; number[], not Array<number>.
  • Use the readonly number[] notation; not ReadonlyArray<number>.
  • Prefer using the unknown type instead of any whenever possible.
  • Don't use abbreviation for type/variable/function names; function foo(options: Options), not function foo(opts: Opts).
  • When there are more than one generic type variable in a method, they should have descriptive names; type Mapper<Element, NewElement> = …, not type Mapper<T, U> = ….
  • Don't prefix the name of interfaces with I; Options, not IOptions.
  • Imports, destructuring, and object literals should not have spaces around the identifier; {foo}, not { foo }.
  • Don't use permissive types like object or Function. Use specific type-signatures like Record<string, number> or (input: string) => boolean;.
  • Use Record<string, any> for accepting objects with string index type and Record<string, unknown> for returning such objects. The reason any is used for assignment is that TypeScript has special behavior for it:

    The index signature Record<string, any> in TypeScript behaves specially: it’s a valid assignment target for any object type. This is a special rule, since types with index signatures don’t normally produce this behavior.

Prefer read-only values

Make something read-only when it's not meant to be modified. This is usually the case for return values and option interfaces. Get familiar with the readonly keyword for properties and array/tuple types. There's also a Readonly type to mark all properties as readonly.

Before:

type Point = {
	x: number;
	y: number;
	children: Point[];
};

After:

type Point = {
	readonly x: number;
	readonly y: number;
	readonly children: readonly Point[];
};

Import types explicitly

Don't use implicit global types except for built-ins or when they can't be imported.

Before:

export function getWindow(): Electron.BrowserWindow;

After:

import {BrowserWindow} from 'electron';

export function getWindow(): BrowserWindow;

Readable named imports

Use a readable name when using named imports.

Before:

import {Writable} from 'node:stream';

After:

import {Writable as WritableStream} from 'node:stream';

Documentation

Exported definitions should be documented with TSDoc. You can borrow text from the readme.

Example:

export type Options = {
	/**
	Allow negative numbers.

	@default true
	*/
	readonly allowNegative?: boolean;

	/**
	Has the ultimate foo.

	Note: Only use this for good.

	@default false
	*/
	readonly hasFoo?: boolean;

	/**
	Where to save.

	Default: [User's downloads directory](https://example.com)

	@example
	```
	import add from 'add';

	add(1, 2, {saveDirectory: '/my/awesome/dir'})
	```
	*/
	readonly saveDirectory?: string;
};

/**
Add two numbers together.

@param x - The first number to add.
@param y - The second number to add.
@returns The sum of `x` and `y`.
*/
export default function add(x: number, y: number, options?: Options): number;

Note:

  • Don't prefix lines with *.
  • Don't hard-wrap.
  • Put an empty line between type entries.
  • Sentences should start with an uppercase character and end in a dot.
  • There's an empty line after the function description.
  • Parameters and the return value should be documented.
  • There's a dash after the parameter name.
  • @param should not include the parameter type.
  • If the parameter description just repeats the parameter name, leave it out.
  • If the parameter is options it doesn't need a description.
  • If the function returns void or a wrapped void like Promise<void>, leave out @returns.
  • If you include an @example, there should be a newline above it. The example itself should be wrapped with triple backticks (```).
  • If the API accepts an options-object, define an Options type as seen above. Document default option values using the @default tag (since type cannot have default values). If the default needs to be a description instead of a basic value, use the formatting Default: Lorem Ipsum..
  • Use @returns, not @return.
  • Ambient declarations can't have default parameters, so in the case of a default method parameter, document it in the parameter docs instead, as seen in the above example.
  • @returns should not duplicate the type information unless it's impossible to describe it without.
    • @returns A boolean of whether it was enabled.@returns Whether it was enabled.

Code examples

  • Include as many code examples as possible. Borrow from the readme.
  • Code examples should be fully functional and should include the import statement.

Testing

The type definition should be tested with tsd. Example of how to integrate it.

Example:

import {expectType} from 'tsd';
import delay from './index.js';

expectType<Promise<void>>(delay(200));

expectType<Promise<string>>(delay(200, {value: '🦄'}));
expectType<Promise<number>>(delay(200, {value: 0}));

expectType<Promise<never>>(delay.reject(200, {value: '🦄'}));
expectType<Promise<never>>(delay.reject(200, {value: 0}));

When it makes sense, also add a negative test using expectError().

Note:

  • The test file should be named index.test-d.ts.
  • tsd supports top-level await.
  • When testing promise-returning functions, don't use the await keyword. Instead, directly assert for a Promise, like in the example above. When you use await, your function can potentially return a bare value without being wrapped in a Promise, since await will happily accept non-Promise values, rendering your test meaningless.
  • Use const assertions when you need to pass literal or readonly typed values to functions in your tests.

typescript-definition-style-guide's People

Contributors

bendingbender avatar shellscape avatar sindresorhus avatar sonicdoe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

typescript-definition-style-guide's Issues

Add semantic versioning to this style guide

We should introduce a version mechanism to track the updates of this guide. Semver is the de facto system used nowadays. We can also apply a comment header in TS definition files to point the style version that is used. Something like:

/**
 * [... other info here...]
 * Typescript Definition Style Guide: v0.1.0 (See https://github.com/sindresorhus/typescript-definition-style-guide more details.)
 */
export interface BlahBlah {
 // code here...
}

How do you find this idea?

Proposal regarding to tab-indentation type

I was checking the rules lately and found a point that want to discuss. In the Checklist section the first bullet mentions that we have to use tab-indentation. There is a long running discussion of whether or not should tab be used as default indentation style in contrast of spaces. I prefer spaces over tabs mainly because using spaces it guaranties that the number of columns will be the same in any environment (1 space = 1 column).

I know that comes to personal preference, but most projects I have worked with prefer spaces over tabs. It would be bad DX if the whole codebase use spaces for indentation, and the definition file use tabs. This is what I propose:

  • Use spaces over tabs, by default.
  • Use the same indentation style that applies to the codebase (spaces or tabs).

Would you mind to explain the reasons behind the decision to apply tab indentation?

Thank you for the effort to standardized a style guide for TS type definition files.

Preference on position of `types` in package.json

Thanks for putting this guide together @sindresorhus.

I've long been using the package.json layout in your modules as a "source of truth" for my own modules, and I've "written that into stone" with prettier-plugin-package. I've recently had a PR to assert the position of types in package.json, and I wanted to get your take on it. Citing this portion of the guide:

  • Use the name "types" and not "typings" for the TypeScript definition field in package.json.
  • If the entry file in the package is named index.js, name the type definition file index.d.ts and put it in root.

    You don't need to add a types field to package.json as TypeScript will infer it from the name.
  • Add the type definition file to the files field in package.json.

It would seem that the only thing here missing is preference on where types should be positioned within package.json. What's your take on this? Do you have a preference of before X property and after Y property?

Input would be very useful and much appreciated 🍺

Replace textual guidelines with ESLint rules where possible

Instead of a 590-words styleguide it could be shorter and the rest can be enforced by eslint, where automation is possible, like:

Clarify the size of indentation

It would be good to strictly clarify the indentation size in the Readme. The examples use size-4 tab indentation. Is it the default? This issue is related with #1: If we decide to change the default indentation style, we also have to document the new size.

Clarification regarding tests (Just Asking)

So the example mentioned for delay are perfect because delay has a conditional return type depending on the type of value.

But would you want tests for constant types.
Like for example clone-regexp always returns a regexp.

It sounds like overkill because if it does not return a regexp it is not the types test which would fail, but the function itself.

Or you prefer the files and test setup created so that if any complexity gets added its easier for the next person to update tests as well.

Omitting `*` breaks formatting in VS Code

  • Don't prefix lines with *.

Opening this since I haven't seen this style used elsewhere, and it's breaking VS Code. Closing this issue with "it's a VS Code issue" is absolutely okay 👌

When not prefixing lines with *, all empty lines get's stripped in the @example. It also doesn't highlight the @example block in the source code.

Screenshot 2019-07-29 at 21 13 01

☝️ With * prefix, notice the @example code being red, and that the popup that shows the function help has line breaks at the correct places.

👇 Without * prefix, notice that the @example has the same green color as any other comment, and that the empty lines are stripped in the help popup.

Screenshot 2019-07-29 at 21 13 27

edit: looking at the TSDoc and JSDoc repo/website, I can't find anything that says specifically that you must have them. But I also haven't found a single example that omits them 🤔

edit2: It also breaks syntax highlighting when e.g. describing params:

Screenshot 2019-07-29 at 21 34 35

Screenshot 2019-07-29 at 21 34 50

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.