Skip to content

Latest commit

 

History

History
274 lines (212 loc) · 6.94 KB

explicit-module-boundary-types.md

File metadata and controls

274 lines (212 loc) · 6.94 KB

Require explicit return and argument types on exported functions' and classes' public class methods (explicit-module-boundary-types)

Explicit types for function return values and arguments makes it clear to any calling code what is the module boundary's input and output.

Consider using this rule in place of no-untyped-public-signature which has been deprecated.

Rule Details

This rule aims to ensure that the values returned from a module are of the expected type.

The following patterns are considered warnings:

// Should indicate that no value is returned (void)
export function test() {
  return;
}

// Should indicate that a number is returned
export default function () {
  return 1;
}

// Should indicate that a string is returned
export var arrowFn = () => 'test';

// All arguments should be typed
export var arrowFn = (arg): string => `test ${arg}`;
export var arrowFn = (arg: any): string => `test ${arg}`;

export class Test {
  // Should indicate that no value is returned (void)
  method() {
    return;
  }
}

The following patterns are not warnings:

// Function is not exported
function test() {
  return;
}

// A return value of type number
export var fn = function (): number {
  return 1;
};

// A return value of type string
export var arrowFn = (arg: string): string => `test ${arg}`;

// All arguments should be typed
export var arrowFn = (arg: string): string => `test ${arg}`;
export var arrowFn = (arg: unknown): string => `test ${arg}`;

// Class is not exported
class Test {
  method() {
    return;
  }
}

Options

The rule accepts an options object with the following properties:

type Options = {
  /**
   * If true, the rule will not report for arguments that are explicitly typed as `any`
   */
  allowArgumentsExplicitlyTypedAsAny?: boolean;
  /**
   * If true, body-less arrow functions that return an `as const` type assertion will not
   * require an explicit return value annotation.
   * You must still type the parameters of the function.
   */
  allowDirectConstAssertionInArrowFunctions?: boolean;
  /**
   * An array of function/method names that will not have their arguments or their return values checked.
   */
  allowedNames?: string[];
  /**
   * If true, functions immediately returning another function expression will not
   * require an explicit return value annotation.
   * You must still type the parameters of the function.
   */
  allowHigherOrderFunctions?: boolean;
  /**
   * If true, type annotations are also allowed on the variable of a function expression
   * rather than on the function arguments/return value directly.
   */
  allowTypedFunctionExpressions?: boolean;
};

const defaults = {
  allowArgumentsExplicitlyTypedAsAny: false,
  allowDirectConstAssertionInArrowFunctions: true,
  allowedNames: [],
  allowHigherOrderFunctions: true,
  allowTypedFunctionExpressions: true,
};

Configuring in a mixed JS/TS codebase

If you are working on a codebase within which you lint non-TypeScript code (i.e. .js/.jsx), you should ensure that you should use ESLint overrides to only enable the rule on .ts/.tsx files. If you don't, then you will get unfixable lint errors reported within .js/.jsx files.

{
  "rules": {
    // disable the rule for all files
    "@typescript-eslint/explicit-module-boundary-types": "off"
  },
  "overrides": [
    {
      // enable the rule specifically for TypeScript files
      "files": ["*.ts", "*.tsx"],
      "rules": {
        "@typescript-eslint/explicit-module-boundary-types": ["error"]
      }
    }
  ]
}

allowArgumentsExplicitlyTypedAsAny

Examples of incorrect code for this rule with { allowArgumentsExplicitlyTypedAsAny: true }:

export const func = (value: any): void => ({ type: 'X', value });
export function foo(value: any): void {}

Examples of correct code for this rule with { allowArgumentsExplicitlyTypedAsAny: true }:

export const func = (value: number): void => ({ type: 'X', value });
export function foo(value: number): void {}

allowDirectConstAssertionInArrowFunctions

Examples of incorrect code for this rule with { allowDirectConstAssertionInArrowFunctions: true }:

export const func = (value: number) => ({ type: 'X', value });
export const foo = () => {
  return {
    bar: true,
  } as const;
};
export const bar = () => 1;
export const baz = arg => arg as const;

Examples of correct code for this rule with { allowDirectConstAssertionInArrowFunctions: true }:

export const func = (value: number) => ({ type: 'X', value } as const);
export const foo = () =>
  ({
    bar: true,
  } as const);
export const bar = () => 1 as const;
export const baz = (arg: string) => arg as const;

allowedNames

You may pass function/method names you would like this rule to ignore, like so:

{
  "@typescript-eslint/explicit-module-boundary-types": [
    "error",
    {
      "allowedNames": ["ignoredFunctionName", "ignoredMethodName"]
    }
  ]
}

allowHigherOrderFunctions

Examples of incorrect code for this rule with { allowHigherOrderFunctions: true }:

export var arrowFn = () => () => {};

export function fn() {
  return function () {};
}

export function foo(outer) {
  return function (inner): void {};
}

Examples of correct code for this rule with { allowHigherOrderFunctions: true }:

export var arrowFn = () => (): void => {};

export function fn() {
  return function (): void {};
}

export function foo(outer: string) {
  return function (inner: string): void {};
}

allowTypedFunctionExpressions

Examples of incorrect code for this rule with { allowTypedFunctionExpressions: true }:

export let arrowFn = () => 'test';

export let funcExpr = function () {
  return 'test';
};

export let objectProp = {
  foo: () => 1,
};

export const foo = bar => {};

Examples of additional correct code for this rule with { allowTypedFunctionExpressions: true }:

type FuncType = () => string;

export let arrowFn: FuncType = () => 'test';

export let funcExpr: FuncType = function () {
  return 'test';
};

export let asTyped = (() => '') as () => string;
export let castTyped = <() => string>(() => '');

interface ObjectType {
  foo(): number;
}
export let objectProp: ObjectType = {
  foo: () => 1,
};
export let objectPropAs = {
  foo: () => 1,
} as ObjectType;
export let objectPropCast = <ObjectType>{
  foo: () => 1,
};

type FooType = (bar: string) => void;
export const foo: FooType = bar => {};

When Not To Use It

If you wish to make sure all functions have explicit return types, as opposed to only the module boundaries, you can use explicit-function-return-type

Further Reading