继承和联合类型在表达多态性方面有什么区别?

lis*_*aak 5 polymorphism inheritance typescript union-types

我正在 Typescript 中使用联合类型,我刚刚意识到它们可以用来表达多态性而不是继承。从 Java 开始,我会写这样的东西。

interface Smiley {
  print(): string;
}

class Happy implements Smiley {
  print() {
    return ':-)';
  }
}

class Sad implements Smiley {
  print() {
    return ':-(';
  }
}
Run Code Online (Sandbox Code Playgroud)

另一方面,我可以用工会表达同样的事情

class Happy {
  print() {
    return ':-)';
  }
}

class Sad {
  print() {
    return ':-(';
  }
}

type Smiley = Happy | Sad;
Run Code Online (Sandbox Code Playgroud)

这些方法之间的理论或实践差异是什么?

我想到的是第一个允许 Smiley 类型的值是任何实现 Smiley 接口的值。第二个只允许笑脸高兴或悲伤,除此之外别无其他。

我还缺少什么吗?

Nic*_*wer 4

有了这些特定的属性,没有太大区别。Smiley将表现得像类型{ print: () => string }. 两个版本都不会强制它,因此 Happy 和 Sad 是唯一与该类型匹配的东西;有人总是可以使用返回字符串的打印函数编写一个新对象,并且它将匹配Smiley

但是一旦您开始添加更多属性,它们的行为就不会相同。如果您向这两个类添加公共属性,则示例 2 将自动在 Smiley 中包含该属性。但在示例 1 中,只有将 Smiley 添加到 Smiley 时,Smiley 才会获得额外的属性,而将其添加到 Happy/Sad 则不会获得额外的属性。

或者更重要的是它们具有一些不同的属性。这就是工会最有用的地方。类型之间共享的任何属性都可以访问,而不会出现打字稿抱怨,但是除非您进行适当的检查来缩小类型范围,否则无法访问唯一的属性。

class Happy {
  print() {
    return ':-)';
  }
  greet() {
    return 'hi';
  }
}

class Sad {
  print() {
    return ':-(';
  }
  exterminate() {
    return 'bye';
  }
}

type Smiley = Happy | Sad;

const example: Smiley = /* some object; maybe it's a Happy, maybe it's a Sad, maybe something else matching Smiley*/

example.print(); // Allowed by typescript
if ('greet' in example) {
  example.greet(); // Allowed, because we have narrowed it down to a Happy
}
example.exterminate(); // Not allowed, because I have no code to confirm it's a Sad
Run Code Online (Sandbox Code Playgroud)