It appears that Typescript has a strong compatibility with AST. When checking x.type == "Abc"
, Typescript is able to infer that x
is of type Abc
. This capability comes in handy when I use it for typechecking JS files with JSDOC format annotations, and I believe it also applies to pure Typescript files.
However, I'm facing some challenges when testing for an array of elements.
The first example functions correctly because each element is looped over individually, and only added to the array if the type check passes. Consequently, Typescript accurately identifies the return type of the function as 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
}
On the other hand, the second example, which is functionally equivalent but cleaner and avoids creating a new array, does not work as expected. The inferred type is
(SpreadElement|Property|ObjectMethod|ObjectProperty|SpreadProperty)[]
, indicating that the type check is not taken into consideration.
/**
* @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
}
Could someone shed some light on why Typescript handles one scenario differently from the other?
While Typescript can leverage checks to refine a type (as demonstrated in the first example), it seems unable to apply these checks on arrays.
Should this be considered a limitation or potential bug in the Typescript compiler since both code snippets should logically return the same type?
EDIT: For context and ease of testability, I have imported types from recast
using the following syntax:
/**
* @typedef { import('recast').types.namedTypes.ObjectExpression} ObjectExpression
* @typedef { import('recast').types.namedTypes.Property} Property
*/