Skip to content

Structural typing undermining type safety #5933

Closed

Description

@alexpomsft
class Dog {furColor: string}
class Cat {}

function walk(d: Dog): void {}
function pet(c: Cat): void {}

const d: Dog = {furColor: "brown"};
const c: Cat = {};

walk(c); // error, good!
pet(d); // no error, bad!

Ryan and I have had extensive conversations about this problem. In my opinion this should clearly fail since both Dog and Cat are classes. Type safety should ensure that when we demand a class type the thing passed in should be in the inheritance chain. Structural typing shouldn't apply to classes.

There's an argument to be made that this case also shouldn't be supported for interfaces, which I'm also sympathetic to. This similar example proves the problem:

interface Dog {furColor: string}
interface Cat {}

function walk(d: Dog): void {}
function pet(c: Cat): void {}

const d: Dog = {furColor: "brown"};
const c: Cat = {};

walk(c); // error, good!
pet(d); // no error, bad!

However, if we mark furColor as optional, I think there's a totally valid argument to be made that it should compile and the desired behavior is met, e.g.

interface Dog {furColor?: string}
interface Cat {}

function walk(d: Dog): void {}
function pet(c: Cat): void {}

const d: Dog = {furColor: "brown"};
const c: Cat = {};

walk(c); // no error, good!
pet(d); // no error, good!

Activity

  1. zpdDG4gta8XKpMCd commented on Dec 5, 2015

    @zpdDG4gta8XKpMCd

    so what is the take out of what you said? what's the action item?

  2. Arnavion commented on Dec 6, 2015

    @Arnavion

    A similar thing is the ability to assign object literals to class types as long as they have the same members, but currently constructor is not such a required member. So this issue is a prerequisite for #3841

    class Foo {
        constructor(public prop: string) { }
        static bar() { }
    }
    
    var foo = new Foo("5");
    foo.constructor.bar(); // Currently not allowed. Would be allowed if #3841 is implemented.
    
    var foo2: Foo = { prop: "5" }; // Currently allowed
    foo2.constructor.bar(); // Currently not allowed. Should continue to not be allowed if #3841 is implemented, which means the assignment itself should be disallowed.
  3. added
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    SuggestionAn idea for TypeScript
    on Dec 8, 2015
  4. mhegazy commented on Dec 8, 2015

    @mhegazy
    Contributor

    This is an issue that we have talked about on and off over the past years. for this to proceed we will need a proposal for how this fits in the current system, what are the proposed new rules for comparing class types, what impact of a change does this have on existing code (we can help out with part by running the ts internal snapshot of real world code bases).

  5. RyanCavanaugh commented on Jul 17, 2020

    @RyanCavanaugh
    Member

    I think some day we might revisit class nominality, but we haven't seen any proposals we really like yet

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

    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions