Typescript显然可以很好地与AST一起使用。如果我检查x.type == "Abc"
,则打字稿的下一行知道x
的类型为Abc
。请注意,我使用它来以JSDOC格式对带有类型注释的JS文件进行类型检查。但是我想纯打字稿文件也一样
但是,我在测试一系列元素时遇到问题。
第一个示例有效,因为我遍历了每个元素,并且仅在检查类型时才推送它。因此,打字稿正确地将类型Property[]
推导为函数的返回类型
/**
* @param {ObjectExpression} objectAst
*/
function getPropertiesList(objectAst) {
let propertiesList = []
for (let p of objectAst.value.properties) {
if (p.type == "Property")
propertiesList.push(p)
else
throw new Error("Properties field has elements that aren't of type `Property`")
}
return propertiesList
}
但是,这个示例在功能上是相同的(但是在我看来更清晰,并且不会创建新的数组)不起作用。推断的类型为(SpreadElement|Property|ObjectMethod|ObjectProperty|SpreadProperty)[]
。因此它不会考虑支票。
/**
* @param {ObjectExpression} objectAst
*/
function getPropertiesList(objectAst) {
let propertiesList = objectAst.value.properties
if (!propertiesList.every(p => p.type == "Property"))
throw new Error("Properties field has elements that aren't of type `Property`")
return propertiesList
}
任何人都可以对打字稿如何处理一种情况与另一种情况有所了解吗?
Typescript可以使用检查使特定类型更具体(如第一个示例所示),但是显然不能对数组执行这些检查。
这可以视为打字稿编译器中的错误吗(因为这两段代码显然应该返回相同的类型)?
编辑:为了提供上下文和可测试性,我从recast
导入了类型,如下所示:
/**
* @typedef { import('recast').types.namedTypes.ObjectExpression} ObjectExpression
* @typedef { import('recast').types.namedTypes.Property} Property
*/
答案 0 :(得分:3)
问题在于,编译器不了解array.every()
可以用作array
类型的type guard。此外,也不能将回调函数p => p.type == "Property"
推断为p
类型的类型保护。编译器非常擅长分析内联代码以缩小类型,但当控制流传递给函数时,它几乎gives up (see microsoft/TypeScript#9998)。
如果您希望TypeScript理解调用boolean
返回函数充当类型保护,则需要手动将这些函数注释为user-defined type guard。可以将foo(x: T): boolean
之类的函数更改为foo(x: T): x is U
,其中“ x is U"
是类型谓词。如果foo(val)
返回true
,则编译器会将val
缩小为U
,否则不会。
对于回调,这需要将p => p.type == "Property"
更改为(p): p is Property => type == "Property"
。对于array.every()
,该方法是Array<T>
接口内的declared in the standard library。幸运的是,您可以使用merge in extra method overloads to interfaces(请注意,如果您的代码在模块中,则可能必须专门使用global augmentation来添加到Array<T>
之类的全局接口中)。看起来像这样:
interface Array<T> {
every<U extends T>(cb: (x: T) => x is U): this is Array<U>;
}
现在,编译器将看到,如果回调是类型保护功能,则every()
本身将充当类型保护。您的代码将按预期工作:
function getPropertiesList(objectAst: ObjectAST): Property[] {
let propertiesList = objectAst.value.properties
if (!propertiesList.every((p): p is Property => p.type == "Property"))
throw new Error("Properties field has elements that aren't of type `Property`")
return propertiesList
}
但是,对于every()
的单次使用而言,这可能会产生太多工作。实际上,您可能应该只使用type assertion并继续前进。类型断言适用于您比编译器更了解类型的情况。这是一个合理的使用时间:
function getPropertiesListAssert(objectAst: ObjectAST): Property[] {
let propertiesList = objectAst.value.properties
if (!propertiesList.every(p => p.type == "Property"))
throw new Error("Properties field has elements that aren't of type `Property`")
return propertiesList as Property[]; // assert
}
好的,希望能有所帮助;祝你好运!