给出接口A和B,它们共同包含x1
属性
interface A {
a1: number;
a2: number;
x1: number; // <<<<
}
interface B{
b1: number;
x1: number; // <<<<
}
给出实现a
和b
let a: A = {a1: 1, a2: 1, x1: 1};
let b: B = {b1: 1, x1: 1};
即使b1
不属于Partial<A>
,这也只是通过了:
let partialA: Partial<A> = b;
但是失败了,因为b1
不属于Partial<A>
:
let partialA: Partial<A> = {b1: 1, x1: 1};
有人可以告诉我为什么吗?
答案 0 :(得分:2)
这将是一段旅程,所以和我一起呆在那里:
通常,子类型应可分配给基本类型。在Typescript中,应将具有更多属性的类型分配给只需要属性子集的类型。例如,这是合法的:
let source = { a1: 0, a2: 0}
let target: { a1: number } = source
现在令人惊讶的是,由于结构化类型的工作方式,Partial<A>
是Partial<B>
的子类型,而Partial<B>
是Partial<A>
的子类型。子类型中可能缺少可选属性,因此,类型中缺少的可选属性不会使类型成为子类型。如果我们删除了可选属性,我们只剩下{}
,它可以是任何其他类型的基本类型。如果我们要求编译器使用条件类型回答此子类型问题,则编译器在这一点上同意我的观点:
type q1 = Partial<A> extends Partial<B> ? "Y" : "N" // Y
type q2 = Partial<B> extends Partial<A> ? "Y" : "N" // Y
对此有一个例外(可能是两个),将对象文字直接分配给特定类型的引用。这就是所谓的多余属性检查,这是因为如果我们直接执行上述分配,将会得到一个错误:
let target: { a1: number } = { a1: 0, a2: 0} // err
这是一个错误的原因是,错误地创建具有更多属性或拼写错误的属性的对象文字并进行此检查(这是违反原则的,它可以将子类型分配给基本类型,这是一个常见错误)类型)来捕获此错误。这也是您在
上遇到错误的原因let partialA: Partial<A> = {b1: 1, x1: 1};
但是多余的属性检查仅在对象文字直接直接分配给特定类型的变量时起作用。因此,在分配let partialA: Partial<A> = b;
上,它不会在多余的属性检查时触发错误。
进一步的复杂之处是Partial<A>
是所谓的弱类型。在PR中介绍了对此类型的检查:
弱类型是仅具有可选属性且不为空的类型。由于除了具有匹配的不可分配属性的类型之外,这些类型都可以从任何其他类型进行分配,因此它们的类型检查非常弱。一个简单的解决方法是要求类型只有在不完全不相交的情况下才可以分配给弱类型。
现在,由于弱类型没有强制性属性,因此应遵循以下原则:将子类型分配给基本类型,将其他任何对象分配给这种类型:
let k = { foo: 0 }
let partialA: Partial<A> = k;
k
的类型是Partial<A>
的子类型,请确保k
与Partial<A>
没有共同之处,但这不是问题。毕竟Partial<A>
不需要任何属性,因此k
不需要任何属性,并且它具有一个额外的属性(通常是子类型的属性),通过添加成员来变得更加具体。>
上面的代码错误的原因是上面的PR参考,它假定将完全无关的类型分配给弱类型可能是错误。因此,就像多余的属性检查一样,引入了禁止这种分配的规则(该规则再次破坏了将子类型分配给基本类型的想法)。
但是,在您的情况下,Partial<A>
和Partial<B>
确实有一些共同之处x1
,因此此规则不会捕获可能错误的分配。尽管令人惊讶,因为Partial<A>
和Partial<B>
都没有强制属性,但它们是彼此的子类型,并且除非有特殊原因(例如过多的属性检查或此额外的弱类型检测规则),否则分配为允许。