当我忽略泛型类型定义时,为什么打字稿不会抱怨?

时间:2021-07-28 13:19:41

标签: typescript generics typescript-generics

假设您有以下代码:

class Superclass<T>{
    combine(object: Superclass<T>) {
        console.log("combined!");
        return object;
    }
}

class Generic1 {
    x = 12;
    // some class...
}
class Generic2 {
    y = "Hello!";
    // some class...
}

new Superclass<Generic1>().combine(new Superclass<Generic2>());

Playground link

你肯定会注意到,这里应该有一个错误。 Superclass<Generic1> 只能与 Superclass<Generic1> 组合,但不能与 Superclass<Generic2> 组合。 Visual Studio 代码也显示:

enter image description here

但是编译后没有错误!为什么?我误解了泛型吗?

1 个答案:

答案 0 :(得分:5)

TypeScript 允许它通过,因为 T 中的 Superclass<T> 不影响其形状,因此 Superclass<A>Superclass<B> 具有相同的形状,因此被 TypeScript 认为是“兼容的”。

请记住,TypeScript 有一个结构 类型系统,其中的成员认为类型是等效的,即使它们的实际定义和原始定义是完全分开的(想想 convergent evolution)。至于为什么令人惊讶:请记住,TypeScript 确实没有有一个主格类型系统,其中具有相同成员、结构和布局的类型,一直到跨平台的二进制表示,都被认为是完全不同的,如只要它们具有不同的类型名称(这就是 Java、C# 和许多其他语言的类型系统的工作方式)。

所以虽然 Superclass<T> 没有使用 T 并且它不影响它的形状,所以 tsc 认为 T 是多余的并忽略它:

所以这个...

class Superclass<T> {
    combine(object: Superclass<T>): Superclass<T> { ... }
}

...可证明等价于:

class Superclass {
    combine(object: Superclass): Superclass { ... }
}

然而,一旦 Superclass<T> 依赖于 T,例如添加一个类型为 T 的字段,它就会抛出一个错误:

class Superclass<T>{

    public value: T;

    constructor(value: T) {
        this.value = value;
    }

    combine(object: Superclass<T>) {
        console.log("combined: " + object.value.toString());
        return object;
    }
}

class Generic1 {
    x = Math.random();
}
class Generic2 {
    y = Math.random();
}

var x = new Superclass<Generic1>( new Generic1() ).combine(new Superclass<Generic2>( new Generic2() ) );

错误:

Argument of type 'Superclass<Generic2>' is not assignable to parameter of type 'Superclass<Generic1>'.
  Property 'x' is missing in type 'Generic2' but required in type 'Generic1'.

至于 为什么会这样 - 这是(我认为)在 TypeScript 的文档中解释的in the section about Type Compatibility

<块引用>

TypeScript 的结构类型系统的基本规则是 xy 兼容,如果 y 至少具有与 x 相同的成员

在这种情况下,当 Superclass<T> 关闭为 Superclass<Generic1>Superclass<Generic2> 时,两种类型都具有完全相同的成员,并且 TypeScript 认为它们彼此兼容。