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

Implicit downcasting to object and classes #26199

Closed
sergey-shandar opened this issue Aug 3, 2018 · 7 comments
Closed

Implicit downcasting to object and classes #26199

sergey-shandar opened this issue Aug 3, 2018 · 7 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@sergey-shandar
Copy link
Contributor

TypeScript Version: 3.1.0-dev.201xxxxx

Search Terms: implicit downcast

Code

function acceptObjectOnly(_: object) {}

acceptObjectOnly(3) // error: as expected

const emptyInterface: {} = 3

acceptObjectOnly(emptyInterface) // ok: but error is expected

class MyClass {}

function acceptMyClassOnly(_: MyClass) {}

acceptMyClassOnly(3) // ok: but error is expected

Expected behavior:

Actual behavior:

Playground Link:

Related Issues:

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Aug 3, 2018
@RyanCavanaugh
Copy link
Member

@sergey-shandar
Copy link
Contributor Author

sergey-shandar commented Aug 3, 2018

Not this one https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-all-types-assignable-to-empty-interfaces
Not this one https://github.com/Microsoft/TypeScript/wiki/FAQ#why-do-these-empty-classes-behave-strangely

@RyanCavanaugh could you, please, point to exact location where it says that interfaces (which may be used for any JavaSript types except null and undefined) are implicitly convertible to object (AFAIK, classes are derived from object)?

And why, in this case, the empty interface is not implicitly convertable into number for example?

IMHO, it's a bug. And I can't see a reason to have this as a feature.

function acceptObjectOnly(_: object) {}

acceptObjectOnly(3) // error: as expected

const emptyInterface: {} = 3

acceptObjectOnly(emptyInterface) // ok: but error is expected

class MyClass {}

function acceptMyClassOnly(_: MyClass) {}

acceptMyClassOnly(3) // ok: but error is expected

function acceptNumberOnly(_: number) {}

acceptNumberOnly(emptyInterface) // error: as expected

@bsiegel
Copy link
Member

bsiegel commented Aug 4, 2018

I'm not sure how the rule is being applied in this case - definitely something strange with the empty interface that doesn't seem to be covered in the docs:

const a: object = 3 // fails
const b: object = 3 as {} // works
const c: string = 3 as {} // fails

@sergey-shandar
Copy link
Contributor Author

sergey-shandar commented Aug 4, 2018

BTW, it could be also non-empty interface. IMHO, the problem is that TS considers interfaces are derived from object. Which is not true. For example, string is not an object but it may implement some interfaces.

More fun

function acceptObjectOnly(_: object) {}

acceptObjectOnly(3) // error: as expected

interface SomeInterface {
    toString(): string
}

const someInterface: SomeInterface = 3

acceptObjectOnly(someInterface) // ok: but error is expected

class MyClass {}

function acceptMyClassOnly(_: MyClass) {}

acceptMyClassOnly(3) // ok: but error is expected

function acceptNumberOnly(_: number) {}

acceptNumberOnly(someInterface) // error: as expected

@RyanCavanaugh
Copy link
Member

could you, please, point to exact location

You'll have to read the document and understand it holistically. No documentation can cover the entire matrix of all things you might do and comprehensively specify every outcome; there are implicit isomorphisms (interface X { } and { } are very similar for example).

object is assignable from { } for mostly historical reasons.

And why, in this case, the empty interface is not implicitly convertable into number for example?

You're asking why type relationships are directional?

@sergey-shandar
Copy link
Contributor Author

sergey-shandar commented Aug 4, 2018

@RyanCavanaugh I understand the historical context but it doesn't mean that the problem shouldn't be fixed.

For example, we may have a strict mode when interfaces are not convertible to object. Or we may have special interfaces which aren't convertible to object (for backward compatibility). Something like

pure interface Empty {
}

@RyanCavanaugh
Copy link
Member

Please log a suggestion if you have a proposal for what should be done and how/why (i.e. motivating use cases where this would provide value above the status quo). Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants