我不确定我的问题是否清楚,所以我会试着详细说明. 假设我有一个实现接口MyInterface的类MyClass.除了实现所需的属性(在本例中为myProp1)外,它还有一个附加属性myProp2,如下面的代码片段所示:

interface MyInterface {
  myProp1: string;
}

class MyClass implements MyInterface {
  myProp2 = '2';
  constructor(public myProp1: string) {}
}

然后,在创建类的实例时,我将其类型指定为MyInterface.即使instance具有未在MyInterface中指定的属性,TypeScrip也不会出现错误:

let instance: MyInterface = new MyClass('1'); // no type error

但是,当try 访问添加的属性(myProp2)时,我收到一个错误:

console.log(instance.myProp2); // 'myProp2' does not exist on type 'MyInterface'. Did you mean 'myProp1'?

这是一个错误,还是这里的故意行为?

推荐答案

这起到了预期的作用.类型脚本有structural subtyping,这意味着如果类型Y中所需的每个成员都存在于类型X中,则可以将类型X的值赋给类型Y的变量.

因此,一个值是有效的MyInterface所需要的就是它有一个类型为stringmyProp1属性.而且MyClass的每个实例都有这样的属性(这就是implements MyInterface编译时没有错误的原因),所以根据类型脚本,将MyClass实例赋给类型为MyInterface的变量是非常好的.

诚然,TypeScrip有时确实会抱怨额外的属性(通过excess property checking),但这只在某些情况下才会触发;特别是当您分配的object literal具有比预期更多的属性时.由于表达式new MyClass('1')不是对象文字,因此不会出现警告.这在microsoft/TypeScript#5303中被描述为故意的.


此外,编译器不会跟踪分配给非union type变量的内容.instance的类型是非并集MyInterface类型,因此赋值instance = new MyClass('1')不会改变这一点.它是not narrowedMyClass,因此您不能通过instance访问myProp2属性(因为MyInterface没有已知的myProp2属性).

请注意,这与union类型变量的行为不同,在union类型的变量中,assignments will narrow仅表示赋值与之兼容的联合成员:

let instance: MyClass | Date = new MyClass('1');
instance.myProp2; // okay

这意味着您真的不想用非联合类型为变量的类型赋值,除非您希望该变量具有该类型的不同值,并且您不需要跟踪它具有哪个值.如果instance始终是MyClass的一个实例,并且这就是您需要知道的全部内容,那么您可以这样注释它:

let instance: MyClass = new MyClass('1');
instance.myProp2; // okay

或者根本不对其进行注释,并让编译器infer将其类型设置为MyClass:

let instance = new MyClass('1');
instance.myProp2; // okay

一般来说,让编译器为您推断类型是一个好主意,只有在您需要施加比推理提供的更多控制时才对其进行注释是个好主意.

Playground link to code

Typescript相关问答推荐

contextPaneTitleText类型的参数不能分配给remoteconfig类型的关键参数'""'''

使用泛型keyof索引类型以在if-condition内类型推断

TypeScrip表示该类型不可赋值

在类型脚本中的泛型类上扩展的可选参数

迭代通过具有泛型值的映射类型记录

将带点的对象键重新映射为具有保留值类型的嵌套对象

如何将对象数组中的键映射到另一种对象类型的键?

从非文字数组(object.keys和array.map)派生联合类型

打字脚本中的模块化或命名空间

Typescript,如何在通过属性存在过滤后重新分配类型?

map 未显示控制台未显示任何错误@reaction-google-map/api

如何将对象的字符串文字属性用作同一对象中的键类型

为什么我会得到一个;并不是所有的代码路径都返回一个值0;switch 中的所有情况何时返回或抛出?(来自vsCode/TypeScript)

有没有一种简单的方法可以访问Keyboard Shortcuts JSON文件中列出的每个命令的显示名称(标题)?

基于可选参数的动态类型泛型类型

将函数签名合并到重载中

Angular 外部订阅值 Null

使用接口属性作为同一接口中另一个属性的数组长度

为什么在判断参数是否等于联合类型值的条件下,通用类型T extends 'a' | 'b'未正确解析?

ChartComponent类型中缺少属性type,但ApexChart类型中需要属性type