Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compiler option to require explicit casts from any to other types #22716

Closed
wolverian opened this issue Mar 20, 2018 · 6 comments
Closed
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@wolverian
Copy link

wolverian commented Mar 20, 2018

TypeScript Version: [email protected]

Search Terms: explicit any conversion

Code

function x(): any {
	return 42
}

const y: string = x()

Expected behavior: I can use a compiler option to require an explicit cast from any to string, and the code above would give an error with that option enabled. Function x in the example usually comes from a library definition file like: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/9f4c75126167d0d8af759f58405d53d983e94ad0/types/yargs/index.d.ts#L226

Actual behavior: There is no such option.

Playground Link: http://www.typescriptlang.org/play/index.html#src=function%20x()%3A%20any%20%7B%0D%0A%20%20%20%20return%2042%0D%0A%7D%0D%0A%0D%0Aconst%20y%3A%20string%20%3D%20x()

Related Issues: #22464 (comment)

@krryan
Copy link

krryan commented Mar 20, 2018

That library definition is arguably poor; any is basically the keyword for "turn off the type checker." In previous threads, something like {} | null | undefined has been recommended in situations where something can have "any" type but you still want type safety. Makes any a somewhat misleading term, in my opinion, but our project has just done type Unknown = {} | null | undefined and that's covered everything.

If x were so defined, const y: string = x(); would absolutely fail and require as string.

@wolverian
Copy link
Author

@krryan Thank you for the suggestion! In practice, any is prevalent in library type definitions in situations like this. I'm not sure it's practical to suggest authors of library definitions to fix the situation.

@ghost
Copy link

ghost commented Mar 20, 2018

In practice, any is prevalent in library type definitions in situations like this.

True, lib.d.ts has 404 uses of any, and there are many other lib files included by default, plus any is very common on DefinitelyTyped. You may be interested in https://palantir.github.io/tslint/rules/no-unsafe-any/ and #10715.

@ajafff
Copy link
Contributor

ajafff commented Mar 20, 2018

TSLint's no-unsafe-any is useful if you are accessing a variable of type any directly:

declare let foo: any;
declare let bar: string;

foo.bar; // lint error
foo(); // lint error
bar = foo; // lint error

But it doesn't detect any nested in another type:

declare let foo: any[];
declare let bar: string[];

bar = foo; // this is still possible

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Mar 20, 2018
@RyanCavanaugh
Copy link
Member

This needs some very careful spec'ing to be actionable. The problem is that any assignability doesn't just show up during straightforward assignments - it's everywhere, and sometimes internal comparisons used to establish relationships (e.g. determining if a location is a type inference location) will instantiate generics with any and then perform structural comparisons. So you need some way to determine whether a particular assignment is a "real" one from userland, or one that "shouldn't matter", and a way to make this distinction in a way that doesn't violate any internal assumptions about consistency. It's also problematic because all type relationship comparisons are cached, so you'd need to split that cache into "userland" / "internal" relationship results under such a flag.

There are also places where there's an assignability relationship to any occurring in a place where you can't cast. For example:

class Base {
   foo(): any { return null; }
}
class Derived extends Base {
   foo() { return 0; }
}

This is in principle an error because a Derived isn't assignable to a Base because its method return type number is not assignable from the base return type any.

It's a trivial change to just "make any not assignable" in the compiler and you'll immediately see everything explode if you try it.

@wolverian
Copy link
Author

Thanks Ryan! That’s a very informative comment.

Looks like the current situation would be best solved with a linter, and in the long term by the unknown proposal in #10715.

@microsoft microsoft locked and limited conversation to collaborators Jul 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants