diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index ce069a2aff6..167bb2b4933 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -181,6 +181,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: |
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | | | |
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | |
+| [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Requires type annotations to exist | | | |
| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | | :thought_balloon: |
| [`@typescript-eslint/unified-signatures`](./docs/rules/unified-signatures.md) | Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter | | | |
diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md
index a30bd468d17..ab59ea66ab0 100644
--- a/packages/eslint-plugin/ROADMAP.md
+++ b/packages/eslint-plugin/ROADMAP.md
@@ -32,8 +32,8 @@
| [`only-arrow-functions`] | 🔌 | [`prefer-arrow/prefer-arrow-functions`] |
| [`prefer-for-of`] | ✅ | [`@typescript-eslint/prefer-for-of`] |
| [`promise-function-async`] | ✅ | [`@typescript-eslint/promise-function-async`] |
-| [`typedef`] | 🛑 | N/A |
| [`typedef-whitespace`] | ✅ | [`@typescript-eslint/type-annotation-spacing`] |
+| [`typedef`] | ✅ | [`@typescript-eslint/typedef`] |
| [`unified-signatures`] | ✅ | [`@typescript-eslint/unified-signatures`] |
[1] The ESLint rule only supports exact string matching, rather than regular expressions
@@ -592,6 +592,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint-
[`@typescript-eslint/no-unnecessary-type-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md
[`@typescript-eslint/no-var-requires`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-var-requires.md
[`@typescript-eslint/type-annotation-spacing`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/type-annotation-spacing.md
+[`@typescript-eslint/typedef`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/typedef.md
[`@typescript-eslint/unified-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unified-signatures.md
[`@typescript-eslint/no-misused-new`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-new.md
[`@typescript-eslint/no-object-literal-type-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-object-literal-type-assertion.md
diff --git a/packages/eslint-plugin/docs/rules/typedef.md b/packages/eslint-plugin/docs/rules/typedef.md
new file mode 100644
index 00000000000..4498ab95610
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/typedef.md
@@ -0,0 +1,264 @@
+# Require type annotations to exist (typedef)
+
+TypeScript cannot always infer types for all places in code.
+Some locations require type annotations for their types to be inferred.
+
+```ts
+class ContainsText {
+ // There must be a type annotation here to infer the type
+ delayedText: string;
+
+ // `typedef` requires a type annotation here to maintain consistency
+ immediateTextExplicit: string = 'text';
+
+ // This is still a string type because of its initial value
+ immediateTextImplicit = 'text';
+}
+```
+
+> Note: requiring type annotations unnecessarily can be cumbersome to maintain and generally reduces code readability.
+> TypeScript is often better at inferring types than easily written type annotations would allow.
+> Instead of enabling `typedef`, it is generally recommended to use the `--noImplicitAny` and/or `--strictPropertyInitialization` compiler options to enforce type annotations only when useful.
+
+## Rule Details
+
+This rule can enforce type annotations in locations regardless of whether they're required.
+This is typically used to maintain consistency for element types that sometimes require them.
+
+> To enforce type definitions existing on call signatures as per TSLint's `arrow-call-signature` and `call-signature` options, use `explicit-function-return-type`.
+
+## Options
+
+This rule has an object option that may receive any of the following as booleans:
+
+- `"arrayDestructuring"`
+- `"arrowParameter"`: `true` by default
+- `"memberVariableDeclaration"`: `true` by default
+- `"objectDestructuring"`
+- `"parameter"`: `true` by default
+- `"propertyDeclaration"`: `true` by default
+- `"variableDeclaration"`
+
+For example, with the following configuration:
+
+```json
+{
+ "rules": {
+ "typedef": [
+ "error",
+ {
+ "arrowParameter": false,
+ "variableDeclaration": true
+ }
+ ]
+ }
+}
+```
+
+- Type annotations on arrow function parameters are not required
+- Type annotations on variables are required
+- Options otherwise adhere to the defaults
+
+### arrayDestructuring
+
+Whether to enforce type annotations on variables declared using array destructuring.
+
+Examples of **incorrect** code with `{ "arrayDestructuring": true }`:
+
+```ts
+const [a] = [1];
+const [b, c] = [1, 2];
+```
+
+Examples of **correct** code with `{ "arrayDestructuring": true }`:
+
+```ts
+const [a]: number[] = [1];
+const [b]: [number] = [2];
+const [c, d]: [boolean, string] = [true, 'text'];
+```
+
+### arrowParameter
+
+Whether to enforce type annotations for parameters of arrow functions.
+
+Examples of **incorrect** code with `{ "arrowParameter": true }`:
+
+```ts
+const logsSize = size => console.log(size);
+
+['hello', 'world'].map(text => text.length);
+
+const mapper = {
+ map: text => text + '...',
+};
+```
+
+Examples of **correct** code with `{ "arrowParameter": true }`:
+
+```ts
+const logsSize = (size: number) => console.log(text);
+
+['hello', 'world'].map((text: string) => text.length);
+
+const mapper = {
+ map: (text: string) => text + '...',
+};
+```
+
+### memberVariableDeclaration
+
+Whether to enforce type annotations on member variables of classes.
+
+Examples of **incorrect** code with `{ "memberVariableDeclaration": true }`:
+
+```ts
+class ContainsText {
+ delayedText;
+ immediateTextImplicit = 'text';
+}
+```
+
+Examples of **correct** code with `{ "memberVariableDeclaration": true }`:
+
+```ts
+class ContainsText {
+ delayedText: string;
+ immediateTextImplicit: string = 'text';
+}
+```
+
+### objectDestructuring
+
+Whether to enforce type annotations on variables declared using object destructuring.
+
+Examples of **incorrect** code with `{ "objectDestructuring": true }`:
+
+```ts
+const { length } = 'text';
+const [b, c] = Math.random() ? [1, 2] : [3, 4];
+```
+
+Examples of **correct** code with `{ "objectDestructuring": true }`:
+
+```ts
+const { length }: { length: number } = 'text';
+const [b, c]: [number, number] = Math.random() ? [1, 2] : [3, 4];
+```
+
+### parameter
+
+Whether to enforce type annotations for parameters of functions and methods.
+
+Examples of **incorrect** code with `{ "parameter": true }`:
+
+```ts
+function logsSize(size): void {
+ console.log(size);
+}
+
+const doublesSize = function(size): numeber {
+ return size * 2;
+};
+
+const divider = {
+ curriesSize(size): number {
+ return size;
+ },
+ dividesSize: function(size): number {
+ return size / 2;
+ },
+};
+
+class Logger {
+ log(text): boolean {
+ console.log('>', text);
+ return true;
+ }
+}
+```
+
+Examples of **correct** code with `{ "parameter": true }`:
+
+```ts
+function logsSize(size: number): void {
+ console.log(size);
+}
+
+const doublesSize = function(size: number): numeber {
+ return size * 2;
+};
+
+const divider = {
+ curriesSize(size: number): number {
+ return size;
+ },
+ dividesSize: function(size: number): number {
+ return size / 2;
+ },
+};
+
+class Logger {
+ log(text: boolean): boolean {
+ console.log('>', text);
+ return true;
+ }
+}
+```
+
+### propertyDeclaration
+
+Whether to enforce type annotations for properties of interfaces and types.
+
+Examples of **incorrect** code with `{ "propertyDeclaration": true }`:
+
+```ts
+type Members = {
+ member;
+ otherMember;
+};
+```
+
+Examples of **correct** code with `{ "propertyDeclaration": true }`:
+
+```ts
+type Members = {
+ member: boolean;
+ otherMember: string;
+};
+```
+
+### variableDeclaration
+
+Whether to enforce type annotations for variable declarations, excluding array and object destructuring.
+
+Examples of **incorrect** code with `{ "variableDeclaration": true }`:
+
+```ts
+const text = 'text';
+let initialText = 'text';
+let delayedText;
+```
+
+Examples of **correct** code with `{ "variableDeclaration": true }`:
+
+```ts
+const text: string = 'text';
+let initialText: string = 'text';
+let delayedText: string;
+```
+
+## When Not To Use It
+
+If you are using stricter TypeScript compiler options, particularly `--noImplicitAny` and/or `--strictPropertyInitialization`, you likely don't need this rule.
+
+In general, if you do not consider the cost of writing unnecessary type annotations reasonable, then do not use this rule.
+
+## Further Reading
+
+- [TypeScript Type System](https://basarat.gitbooks.io/typescript/docs/types/type-system.html)
+- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html)
+
+## Compatibility
+
+- TSLint: [typedef](https://palantir.github.io/tslint/rules/typedef)
diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json
index cf3f61c69cd..dac2e6b3f6b 100644
--- a/packages/eslint-plugin/src/configs/all.json
+++ b/packages/eslint-plugin/src/configs/all.json
@@ -69,6 +69,7 @@
"@typescript-eslint/strict-boolean-expressions": "error",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/type-annotation-spacing": "error",
+ "@typescript-eslint/typedef": "error",
"@typescript-eslint/unbound-method": "error",
"@typescript-eslint/unified-signatures": "error"
}
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 4ae1d9b8ea8..1d1bc59a846 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -56,6 +56,7 @@ import semi from './semi';
import strictBooleanExpressions from './strict-boolean-expressions';
import tripleSlashReference from './triple-slash-reference';
import typeAnnotationSpacing from './type-annotation-spacing';
+import typedef from './typedef';
import unboundMethod from './unbound-method';
import unifiedSignatures from './unified-signatures';
@@ -114,10 +115,11 @@ export default {
'require-array-sort-compare': requireArraySortCompare,
'require-await': requireAwait,
'restrict-plus-operands': restrictPlusOperands,
- semi: semi,
'strict-boolean-expressions': strictBooleanExpressions,
'triple-slash-reference': tripleSlashReference,
'type-annotation-spacing': typeAnnotationSpacing,
'unbound-method': unboundMethod,
'unified-signatures': unifiedSignatures,
+ semi: semi,
+ typedef: typedef,
};
diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts
new file mode 100644
index 00000000000..a53f173303c
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/typedef.ts
@@ -0,0 +1,136 @@
+import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
+import * as util from '../util';
+
+const enum OptionKeys {
+ ArrayDestructuring = 'arrayDestructuring',
+ ArrowParameter = 'arrowParameter',
+ MemberVariableDeclaration = 'memberVariableDeclaration',
+ ObjectDestructuring = 'objectDestructuring',
+ Parameter = 'parameter',
+ PropertyDeclaration = 'propertyDeclaration',
+ VariableDeclaration = 'variableDeclaration',
+}
+
+type Options = { [k in OptionKeys]?: boolean };
+
+type MessageIds = 'expectedTypedef' | 'expectedTypedefNamed';
+
+export default util.createRule<[Options], MessageIds>({
+ name: 'typedef',
+ meta: {
+ docs: {
+ description: 'Requires type annotations to exist',
+ category: 'Stylistic Issues',
+ recommended: false,
+ },
+ messages: {
+ expectedTypedef: 'expected a type annotation',
+ expectedTypedefNamed: 'expected {{name}} to have a type annotation',
+ },
+ schema: [
+ {
+ type: 'object',
+ properties: {
+ [OptionKeys.ArrayDestructuring]: { type: 'boolean' },
+ [OptionKeys.ArrowParameter]: { type: 'boolean' },
+ [OptionKeys.MemberVariableDeclaration]: { type: 'boolean' },
+ [OptionKeys.ObjectDestructuring]: { type: 'boolean' },
+ [OptionKeys.Parameter]: { type: 'boolean' },
+ [OptionKeys.PropertyDeclaration]: { type: 'boolean' },
+ [OptionKeys.VariableDeclaration]: { type: 'boolean' },
+ },
+ },
+ ],
+ type: 'suggestion',
+ },
+ defaultOptions: [
+ {
+ [OptionKeys.ArrowParameter]: true,
+ [OptionKeys.MemberVariableDeclaration]: true,
+ [OptionKeys.Parameter]: true,
+ [OptionKeys.PropertyDeclaration]: true,
+ },
+ ],
+ create(context, [options]) {
+ function report(location: TSESTree.Node, name?: string) {
+ context.report({
+ node: location,
+ messageId: name ? 'expectedTypedefNamed' : 'expectedTypedef',
+ data: { name },
+ });
+ }
+
+ function getNodeName(node: TSESTree.Parameter | TSESTree.PropertyName) {
+ return node.type === AST_NODE_TYPES.Identifier ? node.name : undefined;
+ }
+
+ function checkParameters(params: TSESTree.Parameter[]) {
+ for (const param of params) {
+ if (
+ param.type !== AST_NODE_TYPES.TSParameterProperty &&
+ !param.typeAnnotation
+ ) {
+ report(param, getNodeName(param));
+ }
+ }
+ }
+
+ return {
+ ArrayPattern(node) {
+ if (options[OptionKeys.ArrayDestructuring] && !node.typeAnnotation) {
+ report(node);
+ }
+ },
+ ArrowFunctionExpression(node) {
+ if (options[OptionKeys.ArrowParameter]) {
+ checkParameters(node.params);
+ }
+ },
+ ClassProperty(node) {
+ if (
+ options[OptionKeys.MemberVariableDeclaration] &&
+ !node.typeAnnotation
+ ) {
+ report(
+ node,
+ node.key.type === AST_NODE_TYPES.Identifier
+ ? node.key.name
+ : undefined,
+ );
+ }
+ },
+ 'FunctionDeclaration, FunctionExpression'(
+ node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression,
+ ) {
+ if (options[OptionKeys.Parameter]) {
+ checkParameters(node.params);
+ }
+ },
+ ObjectPattern(node) {
+ if (options[OptionKeys.ObjectDestructuring] && !node.typeAnnotation) {
+ report(node);
+ }
+ },
+ 'TSIndexSignature, TSPropertySignature'(
+ node: TSESTree.TSIndexSignature | TSESTree.TSPropertySignature,
+ ) {
+ if (options[OptionKeys.PropertyDeclaration] && !node.typeAnnotation) {
+ report(
+ node,
+ node.type === AST_NODE_TYPES.TSPropertySignature
+ ? getNodeName(node.key)
+ : undefined,
+ );
+ }
+ },
+ VariableDeclarator(node) {
+ if (
+ options[OptionKeys.VariableDeclaration] &&
+ !node.id.typeAnnotation
+ ) {
+ report(node, getNodeName(node.id));
+ }
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/tests/rules/typedef.test.ts b/packages/eslint-plugin/tests/rules/typedef.test.ts
new file mode 100644
index 00000000000..8c281fe5e39
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/typedef.test.ts
@@ -0,0 +1,476 @@
+import rule from '../../src/rules/typedef';
+import { RuleTester, getFixturesRootDir } from '../RuleTester';
+
+const rootDir = getFixturesRootDir();
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ ecmaVersion: 2015,
+ tsconfigRootDir: rootDir,
+ project: './tsconfig.json',
+ },
+});
+
+ruleTester.run('typedef', rule, {
+ valid: [
+ // Array destructuring
+ {
+ code: `const [a]: [number] = [1]`,
+ options: [
+ {
+ arrayDestructuring: true,
+ },
+ ],
+ },
+ {
+ code: `const [a, b]: [number, number] = [1, 2]`,
+ options: [
+ {
+ arrayDestructuring: true,
+ },
+ ],
+ },
+ {
+ code: `const [a] = 1;`,
+ options: [
+ {
+ arrayDestructuring: false,
+ },
+ ],
+ },
+ `let a: number;
+ [a] = [1];`,
+ // Arrow parameters
+ `((a: number): void => {})()`,
+ `((a: string, b: string): void => {})()`,
+ {
+ code: `((a: number): void => { })()`,
+ options: [
+ {
+ arrowParameter: false,
+ },
+ ],
+ },
+ {
+ code: `((a: string, b: string): void => { })()`,
+ options: [
+ {
+ arrowParameter: false,
+ },
+ ],
+ },
+ // Member variable declarations
+ `class Test {
+ state: number;
+ }`,
+ `class Test {
+ state: number = 1;
+ }`,
+ {
+ code: `class Test {
+ state = 1;
+ }`,
+ options: [
+ {
+ memberVariableDeclaration: false,
+ },
+ ],
+ },
+ // Object destructuring
+ {
+ code: `const { a }: { a: number } = { a: 1 }`,
+ options: [
+ {
+ objectDestructuring: true,
+ },
+ ],
+ },
+ {
+ code: `const { a, b }: { [i: string]: number } = { a: 1, b: 2 }`,
+ options: [
+ {
+ objectDestructuring: true,
+ },
+ ],
+ },
+ {
+ code: `const { a } = { a: 1 };`,
+ options: [
+ {
+ objectDestructuring: false,
+ },
+ ],
+ },
+ // Parameters
+ `function receivesNumber(a: number): void { }`,
+ `function receivesStrings(a: string, b: string): void { }`,
+ `function receivesNumber([a]: [number]): void { }`,
+ `function receivesNumbers([a, b]: number[]): void { }`,
+ `function receivesString({ a }: { a: string }): void { }`,
+ `function receivesStrings({ a, b }: { [i: string ]: string }): void { }`,
+ // Property declarations
+ `type Test = {
+ member: number;
+ };`,
+ `type Test = {
+ [i: string]: number;
+ };`,
+ `interface Test {
+ member: string;
+ };`,
+ `interface Test {
+ [i: number]: string;
+ };`,
+ {
+ code: `type Test = {
+ member;
+ };`,
+ options: [
+ {
+ propertyDeclaration: false,
+ },
+ ],
+ },
+ {
+ code: `type Test = {
+ [i: string];
+ };`,
+ options: [
+ {
+ propertyDeclaration: false,
+ },
+ ],
+ },
+ // Variable declarations
+ {
+ code: `const x: string = "";`,
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `let x: string = "";`,
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `let x: string;`,
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `const a = 1;`,
+ options: [
+ {
+ variableDeclaration: false,
+ },
+ ],
+ },
+ {
+ code: `let a;`,
+ options: [
+ {
+ variableDeclaration: false,
+ },
+ ],
+ },
+ {
+ code: `let a = 1;`,
+ options: [
+ {
+ variableDeclaration: false,
+ },
+ ],
+ },
+ ],
+ invalid: [
+ // Array destructuring
+ {
+ code: `const [a] = [1]`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ options: [
+ {
+ arrayDestructuring: true,
+ },
+ ],
+ },
+ {
+ code: `const [a, b] = [1, 2]`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ options: [
+ {
+ arrayDestructuring: true,
+ },
+ ],
+ },
+ // Object destructuring
+ {
+ code: `const { a } = { a: 1 }`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ options: [
+ {
+ objectDestructuring: true,
+ },
+ ],
+ },
+ {
+ code: `const { a, b } = { a: 1, b: 2 }`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ options: [
+ {
+ objectDestructuring: true,
+ },
+ ],
+ },
+ // Arrow parameters
+ {
+ code: `const receivesNumber = (a): void => { }`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ {
+ code: `const receivesStrings = (a, b): void => { }`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ {
+ data: { name: 'b' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ // Member variable declarations
+ {
+ code: `class Test {
+ state = 1;
+ }`,
+ errors: [
+ {
+ data: { name: 'state' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ {
+ code: `class Test {
+ ["state"] = 1;
+ }`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ // Parameters
+ {
+ code: `function receivesNumber(a): void { }`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ {
+ code: `function receivesStrings(a, b): void { }`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ {
+ data: { name: 'b' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ {
+ code: `function receivesNumber([a]): void { }`,
+ errors: [
+ {
+ column: 25,
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ {
+ code: `function receivesNumbers([a, b]): void { }`,
+ errors: [
+ {
+ column: 26,
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ {
+ code: `function receivesString({ a }): void { }`,
+ errors: [
+ {
+ column: 25,
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ {
+ code: `function receivesStrings({ a, b }): void { }`,
+ errors: [
+ {
+ column: 26,
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ // Property declarations
+ {
+ code: `type Test = {
+ member;
+ };`,
+ errors: [
+ {
+ data: { name: 'member' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ {
+ code: `type Test = {
+ [i: string];
+ };`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ {
+ code: `interface Test {
+ member;
+ };`,
+ errors: [
+ {
+ data: { name: 'member' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ },
+ {
+ code: `interface Test {
+ [i: string];
+ };`,
+ errors: [
+ {
+ messageId: 'expectedTypedef',
+ },
+ ],
+ },
+ // Variable declarations
+ {
+ code: `const a = 1;`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `const a = 1, b: number = 2, c = 3;`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ {
+ data: { name: 'c' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `let a;`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `let a = 1;`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ {
+ code: `let a = 1, b: number, c = 2;`,
+ errors: [
+ {
+ data: { name: 'a' },
+ messageId: 'expectedTypedefNamed',
+ },
+ {
+ data: { name: 'c' },
+ messageId: 'expectedTypedefNamed',
+ },
+ ],
+ options: [
+ {
+ variableDeclaration: true,
+ },
+ ],
+ },
+ ],
+});