Skip to content

Implicit downcasting to object and classes #26199

Closed

Description

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:

Activity

  1. sergey-shandar commented on Aug 3, 2018

    @sergey-shandar
    ContributorAuthor

    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

    Ryan Cavanaugh (@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
  2. bsiegel commented on Aug 4, 2018

    @bsiegel
    Member

    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
  3. sergey-shandar commented on Aug 4, 2018

    @sergey-shandar
    ContributorAuthor

    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
  4. RyanCavanaugh commented on Aug 4, 2018

    @RyanCavanaugh
    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?

  5. sergey-shandar commented on Aug 4, 2018

    @sergey-shandar
    ContributorAuthor

    Ryan Cavanaugh (@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 {
    }
  6. RyanCavanaugh commented on Aug 4, 2018

    @RyanCavanaugh
    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!

  7. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions