microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.16k stars 12.38k forks source link

Uncaught Type assertion between Interface and Class Types can lead to runtime errors #59744

Closed Hedayet closed 2 weeks ago

Hedayet commented 2 weeks ago

🔎 Search Terms

interface assertion

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/JYOwLgpgTgZghgYwgAgJIDED2nkG8C+AUIQgDZwDOFyWOwAtgA6kT0TjUbZ6HLIKYQFMFACuCMJigAKRqIBGpYAmQg4bAFzJhUUAHMAlHiJ8KcAJ4AJCKVI4AvMmlH7APh58+AoZhYA6Oz1pAHIACxs7ZAB3KVIAE2CDAG5eZHwUohJBYWQYbC0uB2MUrKEwXOwAeXkAKwgJLVpkRzycShpsJOQAem7kMFDgagpQzFF45HkUUAA3OCU40opfCADMINbquok-MysIzGTPXuRREDiIGFAIRe9l-0DpTdr6sD81NiPPY76zi6uQDdCEA

💻 Code

interface IFoo {}

class Foo implements IFoo {
  constructor(public name: string) {}
  sayHello = () => {
    console.log('hello world');
  };
}

const foo: IFoo = {};

const fooObject: Foo = foo as Foo; // this should be invalid
console.log(fooObject.sayHello);   // undefined
console.log(fooObject.name);       // undefined

🙁 Actual behavior

Structural type checking might be confusing and leaves space for unintended runtime errors.

  1. Define an interface IFoo
  2. created a class Foo that implements IFoo and adds a method (sayHello)
  3. created an object literal (foo) conforming to the IFoo interface
  4. used a type assertion to cast the object literal to the Foo type (fooObject)
  5. attempted to call the sayHello method and use the name field on the type-asserted object (fooObject)

What Went Wrong:

  1. Code complies successfully
  2. Code will fail during runtime as sayHello and name are undefined on the type-asserted object (fooObject)

🙂 Expected behavior

The type assertion to cast foo as Foo should fail

Additional information about the issue

I understand TypeScript's structural typing system does not check whether the object was created by which class constructor. But in my opinion this is a dangerous gap (even if intentional)

bgenia commented 2 weeks ago

Type assertions are unsafe by design, as they allow to cast less specific types to more specific types. You can check out #56235 for safe type assertions proposal and its current alternatives.

typescript-bot commented 2 weeks ago

This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes.