Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

[new-rule] Added new invalid-void rule #4736

Merged
merged 15 commits into from Jul 6, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/configs/all.ts
Expand Up @@ -170,6 +170,7 @@ export const rules = {
"no-default-export": true,
"no-default-import": true,
"no-duplicate-imports": true,
"no-invalid-void": true,
"no-irregular-whitespace": true,
"no-mergeable-namespace": true,
"no-parameter-reassignment": true,
Expand Down
1 change: 1 addition & 0 deletions src/language/rule/abstractRule.ts
Expand Up @@ -67,6 +67,7 @@ export abstract class AbstractRule implements IRule {
): RuleFailure[];
protected applyWithFunction<T, U>(
sourceFile: ts.SourceFile,
// tslint:disable-next-line:no-invalid-void
walkFn: (ctx: WalkContext<T | void>, programOrChecker?: U) => void,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
options?: T,
programOrChecker?: U,
Expand Down
67 changes: 67 additions & 0 deletions src/rules/noInvalidVoidRule.ts
@@ -0,0 +1,67 @@
/**
* @license
* Copyright 2017 Palantir Technologies, Inc.
timocov marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as ts from "typescript";

import * as Lint from "../index";

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
ruleName: "no-invalid-void",
timocov marked this conversation as resolved.
Show resolved Hide resolved
description:
"Disallows usage of `void` type outside of return type. If `void` is used as return type, it shouldn't be a part of intersection/union type.",
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
hasFix: false,
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
type: "maintainability",
typescriptOnly: true,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING = "void as non-return type is forbidden";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

const failedKinds = [
ts.SyntaxKind.PropertySignature,
ts.SyntaxKind.PropertyDeclaration,

ts.SyntaxKind.VariableDeclaration,
ts.SyntaxKind.TypeAliasDeclaration,

ts.SyntaxKind.IntersectionType,
ts.SyntaxKind.UnionType,
];

function walk(ctx: Lint.WalkContext): void {
ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node) {
if (
node.kind === ts.SyntaxKind.VoidKeyword &&
node.parent !== undefined &&
timocov marked this conversation as resolved.
Show resolved Hide resolved
failedKinds.indexOf(node.parent.kind) !== -1
) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}

ts.forEachChild(node, cb);
});
}
34 changes: 34 additions & 0 deletions test/rules/no-invalid-void/test.ts.lint
@@ -0,0 +1,34 @@
function func(): void {}

type NormalType = () => void;

interface Interface {
lambda: () => void;
voidProp: void;
~~~~ [void as non-return type is forbidden]
}

class ClassName {
private readonly propName: void;
~~~~ [void as non-return type is forbidden]
}

let letVoid: void;
~~~~ [void as non-return type is forbidden]

type VoidType = void;
~~~~ [void as non-return type is forbidden]

class OtherClassName {
private propName: VoidType;
}

type UnionType = string | number;
type UnionType2 = string | number | void;
~~~~ [void as non-return type is forbidden]

type UnionType3 = string | (number & any | (string | void));
~~~~ [void as non-return type is forbidden]

type IntersectionType = string & number & void;
~~~~ [void as non-return type is forbidden]
5 changes: 5 additions & 0 deletions test/rules/no-invalid-void/tslint.json
@@ -0,0 +1,5 @@
{
"rules": {
"no-invalid-void": true
}
}