6

TypeScript allows implicitly casting from any to a stronger type. Considering deserialized JSON is of type any, this behavior allows many type errors and breaks typings.

What compiler or linting options can I use to block implicitly casting from any to a stronger type?

Repro This operation should be flagged for allowing casting from any to a stronger type.

const o = JSON.parse("");
const s: string = o.twinlakes;
twinlakes
  • 9,438
  • 6
  • 31
  • 42
  • As soon as you choose to work with `any`, you get this behaviour. The solution is to create a type for the JSON data. For example: `const o : Test = JSON.parse("")`. If you really don't know the contents of the JSON data you'll have to write code to determine the JSON content. I wouldn't recommend working with unknown data throughout a project. – Kokodoko Jan 11 '19 at 05:01
  • Yes that is the motivation for this question. I want to use an automated system to flag anyone choosing to work with `any` in our code base. – twinlakes Jan 11 '19 at 06:09
  • 2
    In your example, Typescript and TSLint think everything is fine, since you apparently WANT to work with `any`. (Since `JSON.parse()` explicitly sets `o` to the `any` type). I would also like some kind of warning for this! – Kokodoko Jan 11 '19 at 12:05

2 Answers2

9

If you haven't yet, try enabling the TypeScript --noImplicitAny compiler flag to stop declarations from accidentally forgetting to mark types of things that can't be inferred. This'll stop code such as function takesData(data) { /* ... */ } from allowing their data variables to "implicitly" be type any.

Edit January 2023: the correct answer is now to use typescript-eslint:

Additionally, there are several ESLint rules from typescript-eslint you can use for anys that can sneak in even with --noImplicitAny:

You can enable those rules individually in your ESLint config to use them in your project. Alternately, they're all enabled if you extend from the plugin:@typescript-eslint/strict configuration.

Finally, as Titian mentions in the comments, consider using unknown in your code in any place you'd have wanted to use any. unknown is similar to any but TypeScript will enforce that you properly type narrow unknown values before using them unsafely.


The answer below is outdated because TSLint is deprecated. Please see above for the answer with typescript-eslint.

Additionally, there are a couple TSLint rules that help here:

  • no-any: Disallows usages of any as a type declaration. Use this to stop declarations from including any in them, e.g. let data: any = {/*...*/}.
  • no-unsafe-any: Disallows accidentally using any in an unsafe way. This is different from no-any in that you might still be using any types without knowing it: for example, let data = JSON.parse("{}");.
Josh
  • 1,997
  • 13
  • 21
  • 4
    And use `unknown` anywhere you would use `any` – Titian Cernicova-Dragomir Jan 06 '19 at 18:51
  • I currently already have "tslint:recommended" and "tslint-react" rule sets. I just added "no-unsafe-any" explicitly and see no change when casting properties of an `any` object to `string`. I believe `noImplicitAny` in `tsconfig.json` is already blocking the use case of `no-any`. – twinlakes Jan 07 '19 at 00:13
  • `noImplicitAny` does not block using the `any` type. It only blocks _implicit_ `any`s, meaning ones that aren't declared as type `any`. Functions whose parameters are not given an explicit type definition, such as `function foo(data) {}`, are implicit. The `let data: any` above is explicit. – Josh Jan 07 '19 at 01:02
  • I just added a repro case to the original question. I tried adding `no-any` and `no-unsafe-any` to my tslint rules, but it is not flagging any of the code. – twinlakes Jan 11 '19 at 01:06
  • Your repro case is very similar to some of the test cases for `no-unsafe-any`, so it definitely should be flagging it. This is probably either a TSLint bug (if so, file an issue on TSLint please!) or because your lint command isn't linting your file. – Josh Jan 11 '19 at 16:49
  • After further investigation, I think this is a VSCode TSLint extension bug (not TSLint). Thanks for the suggestion. – twinlakes Jan 13 '19 at 16:52
  • Ooh! This might help: https://github.com/Microsoft/vscode-tslint/blob/master/tslint/README.md#how-can-i-use-tslint-rules-that-require-type-information – Josh Jan 13 '19 at 20:08
0

In additiona to Josh's answer, since the type check takes place during runtime, you can try using the 'as' keyword to cast the value from 'any' to the type you want.

const o = JSON.parse("{\"twinlakes\":\"Value\"}");

const s: string = (o as any).twinlakes;

How that works

Michael Haephrati
  • 3,660
  • 1
  • 33
  • 56