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

[no-object-literal-type-assertion] New option: "allow-arguments" #4521

50 changes: 50 additions & 0 deletions src/rules/code-examples/noObjectLiteralTypeAssertion.examples.ts
@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2019 Palantir Technologies, Inc.
*
* 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 Lint from "../../index";
import { ICodeExample } from "../../language/rule/rule";

// tslint:disable: object-literal-sort-keys
export const codeExamples: ICodeExample[] = [
{
description:
"Disallow object literals to appear in type assertion expressions (default). Casing to `any` and `unknown` is allowed.",
config: Lint.Utils.dedent`
"rules": { "no-object-literal-type-assertion": true }
`,
pass: Lint.Utils.dedent`
let foo = {} as any;
let foo = {} as unknown;

let foo = {} as any as Foo;
let foo = {} as unknown as Foo;
`,
fail: Lint.Utils.dedent`
let foo = {} as Foo;
let foo = <Foo>{};
`,
},
{
description: "Allow using a type assertion when the object literal is used as an argument.",
config: Lint.Utils.dedent`
"rules": { "no-object-literal-type-assertion": [true, { "allow-arguments": true }] }
`,
pass: Lint.Utils.dedent`
bar({} as Foo)
`,
},
];
52 changes: 46 additions & 6 deletions src/rules/noObjectLiteralTypeAssertionRule.ts
Expand Up @@ -24,6 +24,18 @@ import * as ts from "typescript";

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

import { codeExamples } from "./code-examples/noObjectLiteralTypeAssertion.examples";

const OPTION_ALLOW_ARGUMENTS = "allow-arguments";

const DEFAULT_OPTIONS: Options = {
[OPTION_ALLOW_ARGUMENTS]: false,
};

interface Options {
[OPTION_ALLOW_ARGUMENTS]: boolean;
}

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
Expand All @@ -37,23 +49,39 @@ export class Rule extends Lint.Rules.AbstractRule {
The compiler will warn for excess properties with this syntax, but not missing required fields.
For example: \`const x: { foo: number } = {}\` will fail to compile, but
\`const x = {} as { foo: number }\` will succeed.`,
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
optionsDescription: Lint.Utils.dedent`
One option may be configured:

* \`${OPTION_ALLOW_ARGUMENTS}\` allows type assertions to be used on object literals inside call expressions.`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
options: {
type: "object",
properties: {
[OPTION_ALLOW_ARGUMENTS]: {
type: "boolean",
},
},
additionalProperties: false,
},
optionExamples: [true, [true, { [OPTION_ALLOW_ARGUMENTS]: true }]],
type: "functionality",
typescriptOnly: true,
codeExamples,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING =
"Type assertion on object literals is forbidden, use a type annotation instead.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
return this.applyWithFunction(
sourceFile,
walk,
parseOptions(this.ruleArguments[0] as Partial<Options> | undefined),
);
}
}

function walk(ctx: Lint.WalkContext<void>): void {
function walk(ctx: Lint.WalkContext<Options>): void {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (
isAssertionExpression(node) &&
Expand All @@ -66,10 +94,22 @@ function walk(ctx: Lint.WalkContext<void>): void {
isParenthesizedExpression(node.expression)
? node.expression.expression
: node.expression,
)
) &&
!(ctx.options[OPTION_ALLOW_ARGUMENTS] && isArgument(node))
) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}
return ts.forEachChild(node, cb);
});
}

function isArgument(node: ts.Node): boolean {
return ts.isCallLikeExpression(node.parent);
}

function parseOptions(options: Partial<Options> | undefined): Options {
return {
...DEFAULT_OPTIONS,
...options,
};
}
@@ -0,0 +1,23 @@
<T> ({});
~~~~~~~~ [0]

({}) as T;
~~~~~~~~~ [0]

<T> x;

x as T;

// Allow cast to 'any'
{} as any;
<any> {};

// Allow cast to 'unknown'
{} as unknown;
<unknown> {};

// Allow object literals to be asserted when used as arguments.
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
foo({} as T);
foo(<T>{});

[0]: Type assertion on object literals is forbidden, use a type annotation instead.
@@ -0,0 +1,5 @@
{
"rules": {
"no-object-literal-type-assertion": [true, { "allow-arguments": true }]
}
}
Expand Up @@ -16,4 +16,10 @@ x as T;
{} as unknown;
<unknown> {};

foo({} as T);
~~~~~~~ [0]

foo(<T>{});
~~~~~ [0]

[0]: Type assertion on object literals is forbidden, use a type annotation instead.