【问题标题】:why generic class can extend a type shape?为什么泛型类可以扩展类型形状?
【发布时间】:2019-09-23 04:02:44
【问题描述】:

我是 Typescript 的新手,只是一个关于扩展类型形状的泛型类的问题,下面是一些示例代码:

class DataCollection<T extends { name: string }> {
   ...
}

这是有效的代码,似乎一个类可以扩展一个类型,但事实并非如此,如果我们这样编码:

type Person = {
   name: string
}

class DataCollection extends Person {   // invalid code
   ...
}

它是无效的,所以类不能扩展一个尖锐的类型。

而我们只能把类型Person当作接口和代码这样处理:

type Person = {
   name: string
}

class DataCollection implements Person {   //valid code
   ...
}

这意味着,使用泛型类更明智:

class DataCollection<T implements { name: string }> {
   ...
}

那么为什么泛型类只能扩展类型形状而不能实现类型形状?

【问题讨论】:

  • 你在这里混合了一些概念。T extends { name: string } 是泛型类型参数约束。它要求T 具有name 类型为string 的属性。例如。 new DataCollection&lt;Person&gt;() 有效,但 new DataCollection&lt;{ foo: boolean }&gt;() 无效。 typescriptlang.org/docs/handbook/…
  • @AlekseyL。那么为什么我们必须使用“扩展”而不是“实现”
  • 再一次,你在混合不相关的东西。 class DataCollection&lt;T extends { name: string }&gt;(泛型类型约束)与class DataCollection extends Person(继承)无关

标签: typescript


【解决方案1】:

这里有几个概念虽然有一些相似的语法,但它们都是不同的。

引用type Name = ... 定义的标准方法是类型别名而不是类型形状

首先类型只存在于编译时。因此,根据定义,在运行时没有任何东西可以依赖它们。这意味着类型别名Person person 不会以任何方式存在于编译的 js 中。

类具有双重性质,它们的名称既代表类型(类的实例类型)又代表运行时值(用于初始化对象的构造函数)。

当您说class DataCollection extends Person 时,您的意思是,在运行时DataCollection 需要将Person 的原型添加到其原型链中。由于类型别名在编译期间被删除,因此类 DataCollection 没有任何内容添加到其原型链中,这使得该语句对于类型别名(或接口)来说是无意义的。

当您说class DataCollection implements Person 时,您是在要求编译器确保在编译时class DataCollection 具有所有具有Person 类型的适当类型的成员。这是一个纯粹的编译时操作,所以在这里使用类型别名就可以了。

class DataCollection&lt;T&gt; 中的T 是一个泛型类型参数。泛型类型参数可用于使类可重用于多种数据类型,同时保留有关基础类型的信息:

class DataCollection<T> {
    add(value: T) {}
}
var c = new DataCollection<number>();
c.add(1)
c.add("1") // error

Play

请注意,T 从未在运行时表达式中使用,它仅用于类型注释,我们永远不能在表达式中使用T(例如new T() 将无效)。 T 只是在编译时检查类的某些方面(参数、成员、返回值)是否属于实例化类时指定的特定类型(在这种情况下,number 在@987654340 中指定@)。

有时我们需要更多关于T 的信息。例如,我们可能想要一个onAdded 成员。这就是DataCollection&lt;T extends TYPE&gt; 的用武之地。它允许我们将T 限制为另一个指定类型的子类型:

type WithAdd = { onAdd(): void }
class DataCollection<T extends WithAdd> {
    add(value: T) {
        value.onAdd(); // ok since onAdd is on the constraint of T
        value.onAdded(); // error since onAdded is not on the constraint of T
    }
}

class Person {
    name!: string;
    onAdd(): void { }
}
var cErr = new DataCollection<number>(); // error number does not have onAdd
var c = new DataCollection<Person>()
c.add(new Person())
c.add({ onAdd() { } }) // error not a Person 

Play

语法DataCollection&lt;T implements { name: string }&gt; 在打字稿中没有意义。由于约束中的extends 已经只处理类型,所以说T 扩展类型或T 实现类型在含义上确实没有区别。在这两种情况下,T 都是指定类型的子类型,因此如果不添加任何值,两者都会混淆。至于为什么选择extends 而不是implements,这是任何人的猜测。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    • 2018-11-26
    • 1970-01-01
    相关资源
    最近更新 更多