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

Update dependencies and lint for no-unsafe-any #2544

Merged
merged 8 commits into from Apr 17, 2017

Conversation

andy-hanson
Copy link
Contributor

PR checklist

  • Addresses an existing issue: #0000
  • New feature, bugfix, or enhancement
    • Includes tests
  • Documentation update

Overview of change:

The no-unsafe-any rule mostly just required appropriate casts, but in the case of FormatterFunction it seems to have discovered a bug -- formatters actually seem to have to be new-able. So created a FormatterStatic instead. This is technically a breaking change, but code that compiled against FormatterFunction would have been wrong.

@@ -46,6 +46,10 @@ export interface IFormatterMetadata {

export type ConsumerType = "human" | "machine";

export interface FormatterStatic {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to FormatterConstructor?

import { arrayify, camelize, dedent } from "./utils";

const moduleDirectory = path.dirname(module.filename);
const CORE_RULES_DIRECTORY = path.resolve(moduleDirectory, ".", "rules");
const cachedRules = new Map<string, typeof AbstractRule | null>(); // null indicates that the rule was not found
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I found this to be clearer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But rules aren't actually guaranteed to be implemented by AbstractRule subclasses, are they?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that it's renamed to RuleConstructor I'm fine with it.

@@ -20,6 +20,11 @@ import * as ts from "typescript";
import {arrayify, flatMap} from "../../utils";
import {IWalker} from "../walker";

export interface RuleStatic {
Copy link
Contributor

@ajafff ajafff Apr 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also rename to RuleConstructor?

@@ -153,9 +152,9 @@ function loadCachedRule(directory: string, ruleName: string, isCustomPath = fals
}
}

let Rule: typeof AbstractRule | null = null;
let Rule: RuleStatic | null = null;
if (absolutePath != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comparison can be strict

@@ -87,7 +87,7 @@ class NoInferredEmptyObjectTypeRule extends Lint.ProgramAwareRuleWalker {
let isAnonymous: boolean;
if (ts.ObjectFlags == null) {
// typescript 2.0.x specific code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this branch can be removed since support for ts 2.0.x was dropped in v5.0.0

@@ -44,7 +44,7 @@ export function objectify(arg: any): any {
* E.g. "foo-bar" -> "fooBar"
*/
export function camelize(stringWithHyphens: string): string {
return stringWithHyphens.replace(/-(.)/g, (_, nextLetter) => nextLetter.toUpperCase());
return stringWithHyphens.replace(/-(.)/g, (_, nextLetter) => (nextLetter as string).toUpperCase());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding the type annotation to the parameter instead of asserting it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that style since it behaves the same as a typecast but doesn't look like one. Was actually considering writing a lint rule for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

um, I don't think that's quite right @andy-hanson. type assertions (as string) are generally less safe than typedefs, because the assignability checks for the latter are stricter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A type annotation in a callback is unsafe.

declare function f(cb: (x: {}) => void): void;
f((n: number) => { console.log(n + 1); }); // Huh, no error?

A type assertion is of course also unsafe, but it's more obvious that it is.

return ruleModule.Rule;
}
}
return undefined;
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the switch to nulls? can't we use undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use cachedRules.set(fullPath, Rule);, so if Rule is undefined, it will look like we didn't cache a result at all. I actually already had to do this in #2369.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should use a string literal like "not-found" or "load-error" instead of null

@@ -44,7 +44,7 @@ export function objectify(arg: any): any {
* E.g. "foo-bar" -> "fooBar"
*/
export function camelize(stringWithHyphens: string): string {
return stringWithHyphens.replace(/-(.)/g, (_, nextLetter) => nextLetter.toUpperCase());
return stringWithHyphens.replace(/-(.)/g, (_, nextLetter) => (nextLetter as string).toUpperCase());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

um, I don't think that's quite right @andy-hanson. type assertions (as string) are generally less safe than typedefs, because the assignability checks for the latter are stricter

@@ -193,8 +193,8 @@ export class Runner {
try {
this.processFiles(onComplete, files, program);
} catch (error) {
if (error.name === FatalError.NAME) {
console.error(error.message);
if ((error as FatalError).name === FatalError.NAME) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use error: FatalError on the line above instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, TS doesn't allow a type annotation there. It wouldn't be safe anyway since TS doesn't have checked exceptions.

@@ -36,7 +36,7 @@ export class Rule extends Lint.Rules.TypedRule {
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING = "Unsafe use of expression of type 'any'.";
public static FAILURE_STRING: string = "Unsafe use of expression of type 'any'.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surely this isn't necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, no idea why that got there!

}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const limit: number = this.ruleArguments[0] || Rule.DEFAULT_ALLOWED_BLANKS;
const limit = this.ruleArguments[0] as number | undefined || Rule.DEFAULT_ALLOWED_BLANKS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the switch from typedef to type assertion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A typedef const x: number = thisIsAny; is flagged by the no-unsafe-any rule, since it looks safe but isn't. const x = thisIsAny as number; is OK since that's obviously an assertion.

@andy-hanson andy-hanson mentioned this pull request Apr 13, 2017
4 tasks
@@ -252,7 +254,7 @@ export function extendConfigurationFile(targetConfig: IConfigurationFile,
};
}

function getHomeDir() {
function getHomeDir(): string | undefined {
const environment = global.process.env;
const paths = [
Copy link
Contributor

@nchen63 nchen63 Apr 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer type annotation on paths instead of the function return value. or both

@@ -54,7 +53,7 @@ export function loadRules(ruleOptionsList: IOptions[],
} else {
const ruleSpecificList = enableDisableRules || [];
ruleOptions.disabledIntervals = buildDisabledIntervalsFromSwitches(ruleSpecificList);
rules.push(new (Rule as any)(ruleOptions));
rules.push(new (Rule as any)(ruleOptions) as IRule);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need either cast

@@ -15,6 +15,8 @@
* limitations under the License.
*/

// tslint:disable no-unsafe-any (TODO)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

const formatterPath = paths.reduce((p, c) => path.join(p, c), "");
const fullPath = path.resolve(moduleDirectory, formatterPath);

if (fs.existsSync(`${fullPath}.js`)) {
const formatterModule = require(fullPath);
return formatterModule.Formatter;
return formatterModule.Formatter; // tslint:disable-line no-unsafe-any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of disabling rule, cast the require to an interface like { Formatter: FormatterConstructor }

let src: string;
try {
src = require.resolve(name);
} catch (e) {
return undefined;
}
return require(src).Formatter;
return require(src).Formatter; // tslint:disable-line no-unsafe-any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above comment

return ruleModule.Rule;
}
}
return undefined;
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should use a string literal like "not-found" or "load-error" instead of null

Rule = loadRule(absolutePath, ruleName);
let Rule: RuleConstructor | null = null;
if (absolutePath !== undefined) {
Rule = loadRule(absolutePath, ruleName); // tslint:disable-line no-unsafe-any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to disable the rule here

@@ -396,6 +396,7 @@ function getOptionsJson(allOptions: any[]): { order: MemberCategoryJson[], alpha
return { order: convertFromOldStyleOptions(allOptions), alphabetize: false }; // presume allOptions to be string[]
}

// tslint:disable-next-line no-unsafe-any
return { order: categoryFromOption(firstOption[OPTION_ORDER]), alphabetize: !!firstOption[OPTION_ALPHABETIZE] };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of disabling, annotate firstOption as type Options, then call using firstOption.order and firstOption.alphabetize

@@ -15,6 +15,8 @@
* limitations under the License.
*/

// tslint:disable no-unsafe-any (TODO)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to disable the rule here

Copy link
Contributor

@nchen63 nchen63 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just some nits

@@ -143,7 +141,7 @@ export function findConfigurationPath(suppliedConfigFilePath: string | null, inp
}
}

/**
/**findRule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this here?

import { arrayify, camelize, dedent } from "./utils";

const moduleDirectory = path.dirname(module.filename);
const CORE_RULES_DIRECTORY = path.resolve(moduleDirectory, ".", "rules");
const cachedRules = new Map<string, typeof AbstractRule | null>(); // null indicates that the rule was not found
const cachedRules = new Map<string, RuleConstructor | "not-found">(); // null indicates that the rule was not found
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove/update this comment

@nchen63 nchen63 merged commit a3d8d6f into palantir:master Apr 17, 2017
@andy-hanson andy-hanson deleted the use_no-unsafe-any branch April 18, 2017 00:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants