如果你
annotate a variable
a(非-
union
)类型类似
ExempleType
,那么这就是TypeScript所知道的关于该变量类型的全部信息。关于您为其分配的值类型的任何更具体的信息都不会被跟踪。有这样一件事
assignment narrowing
,但这只适用于
联盟
类型。所以如果你写
let x: string[] | undefined = []
那么
x
缩小到
string[]
在那次任务之后。但相似的外观
let y: { x: string[] | undefined } = { x: [] }
不以相同的方式工作,因为您正在分配一个非联合对象类型,其
财产
恰好是一个联盟。如果作业缩窄也适用于非联合类型,那就太好了,但事实并非如此(参见
this comment on microsoft/TypeScript#8513
了解更多信息)。
这就解释了
const someVar: ExempleType = { someProperty: [] };
和
const someVar: ExempleType = {}; someVar.someProperty = [];
在后一种情况下,您已经分配了属性,这实际上是一个联合类型(
optional properties
行为与工会相似
undefined
)因此,它被缩小到
string[]
后续的直接财产访问,如
someVar.someProperty
然后会看到
string[]
但是
someVar
其本身不会变窄:
const someVar: ExempleType = {};
someVar.someProperty = [];
someVar.someProperty.push('test'); // no error
function foo(r: Required<ExempleType>) { }
foo(someVar); // error! Types of property 'someProperty' are incompatible.
一般来说,如果你想让TypeScript跟踪初始化器中的特定信息(而且你不打算做很多突变),那么你可能想完全避免注释,并使用
the
satisfies
operator
检查初始化器是否与该类型兼容。
空数组有点乱,因为
[]
最终将被推断为
never[]
,即使有
满足
.有一个悬而未决的问题
microsoft/TypeScript#51853
.现在使用的“安全”方式
满足
冗长:
const someVar = {
someProperty: [] satisfies string[] as string[]
} satisfies ExempleType;
/* const someVar: { someProperty: string[]; } */
这个
expr satisfies T as T
施工有效地首先检查
expr
可分配给
T
和
然后
扩大到
T
,所以
[] satisfies string[] as string[]
最终以
[]
作为类型
string[]
在检查它是否工作后。写起来更容易
[] as string[]
但这有点不安全。然后
满足
最后确保
{ someProperty: string[] }
与兼容
示例类型
,它是…但是
someVar
现在比
示例类型
;它是
someProperty
众所周知
string[]
,其余代码按预期工作:
someVar.someProperty.push('test'); // okay
function foo(r: Required<ExempleType>) { }
foo(someVar); // okay
Playground link to code