Skip to content

Commit

Permalink
feat: TypeScript 4.2 syntax support (#3112)
Browse files Browse the repository at this point in the history
* chore(typescript-estree): update @babel/parser to 7.13.4

* feat: update typescript to ~4.2

* fix(typescript-estree): add support for JSXNamespacedName node

* fix(typescript-estree): correct tsNodeToESTreeNodeMap

* fix(typescript-estree): add support for ThisKeyword as JSXNamespaceOrIdentifier helper

Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
  • Loading branch information
armano2 and bradzacher committed Mar 1, 2021
1 parent cf86f42 commit 2ebfb21
Show file tree
Hide file tree
Showing 26 changed files with 2,854 additions and 182 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ The latest version under the `canary` tag **(latest commit to master)** is:

## Supported TypeScript Version

**The version range of TypeScript currently supported by this parser is `>=3.3.1 <4.2.0`.**
**The version range of TypeScript currently supported by this parser is `>=3.3.1 <4.3.0`.**

These versions are what we test against.

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"ts-jest": "^26.5.1",
"ts-node": "^9.0.0",
"tslint": "^6.1.3",
"typescript": ">=3.3.1 <4.2.0"
"typescript": ">=3.3.1 <4.3.0"
},
"resolutions": {
"typescript": "4.1.5"
"typescript": "4.2.2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<this:bar />;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const x: abstract new () => void;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const x: new () => void;
1 change: 1 addition & 0 deletions packages/types/src/ast-node-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ enum AST_NODE_TYPES {
JSXFragment = 'JSXFragment',
JSXIdentifier = 'JSXIdentifier',
JSXMemberExpression = 'JSXMemberExpression',
JSXNamespacedName = 'JSXNamespacedName',
JSXOpeningElement = 'JSXOpeningElement',
JSXOpeningFragment = 'JSXOpeningFragment',
JSXSpreadAttribute = 'JSXSpreadAttribute',
Expand Down
16 changes: 14 additions & 2 deletions packages/types/src/ts-estree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export type Node =
| JSXFragment
| JSXIdentifier
| JSXMemberExpression
| JSXNamespacedName
| JSXOpeningElement
| JSXOpeningFragment
| JSXSpreadAttribute
Expand Down Expand Up @@ -374,6 +375,7 @@ export type Expression =
| SequenceExpression
| SpreadElement
| TSAsExpression
| TSTypeAssertion
| TSUnaryExpression
| YieldExpression;
export type ForInitialiser = Expression | VariableDeclaration;
Expand All @@ -398,7 +400,10 @@ export type JSXExpression =
| JSXEmptyExpression
| JSXSpreadChild
| JSXExpressionContainer;
export type JSXTagNameExpression = JSXIdentifier | JSXMemberExpression;
export type JSXTagNameExpression =
| JSXIdentifier
| JSXMemberExpression
| JSXNamespacedName;
export type LeftHandSideExpression =
| CallExpression
| ClassExpression
Expand Down Expand Up @@ -1024,7 +1029,7 @@ export interface ImportSpecifier extends BaseNode {

export interface JSXAttribute extends BaseNode {
type: AST_NODE_TYPES.JSXAttribute;
name: JSXIdentifier;
name: JSXIdentifier | JSXNamespacedName;
value: Literal | JSXExpression | null;
}

Expand Down Expand Up @@ -1071,6 +1076,12 @@ export interface JSXMemberExpression extends BaseNode {
property: JSXIdentifier;
}

export interface JSXNamespacedName extends BaseNode {
type: AST_NODE_TYPES.JSXNamespacedName;
namespace: JSXIdentifier;
name: JSXIdentifier;
}

export interface JSXOpeningElement extends BaseNode {
type: AST_NODE_TYPES.JSXOpeningElement;
typeParameters?: TSTypeParameterInstantiation;
Expand Down Expand Up @@ -1340,6 +1351,7 @@ export interface TSConditionalType extends BaseNode {

export interface TSConstructorType extends FunctionSignatureBase {
type: AST_NODE_TYPES.TSConstructorType;
abstract: boolean;
}

export interface TSConstructSignatureDeclaration extends FunctionSignatureBase {
Expand Down
4 changes: 2 additions & 2 deletions packages/typescript-estree/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
},
"devDependencies": {
"@babel/code-frame": "^7.12.13",
"@babel/parser": "^7.12.16",
"@babel/types": "^7.12.13",
"@babel/parser": "^7.13.4",
"@babel/types": "^7.13.0",
"@types/babel__code-frame": "*",
"@types/debug": "*",
"@types/glob": "*",
Expand Down
115 changes: 71 additions & 44 deletions packages/typescript-estree/src/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,46 @@ export class Converter {
return result;
}

private convertJSXIdentifier(
node: ts.Identifier | ts.ThisExpression,
): TSESTree.JSXIdentifier {
const result = this.createNode<TSESTree.JSXIdentifier>(node, {
type: AST_NODE_TYPES.JSXIdentifier,
name: node.getText(),
});
this.registerTSNodeInNodeMap(node, result);
return result;
}

private convertJSXNamespaceOrIdentifier(
node: ts.Identifier | ts.ThisExpression,
): TSESTree.JSXIdentifier | TSESTree.JSXNamespacedName {
const text = node.getText();
const colonIndex = text.indexOf(':');
// this is intentional we can ignore conversion if `:` is in first character
if (colonIndex > 0) {
const range = getRange(node, this.ast);
const result = this.createNode<TSESTree.JSXNamespacedName>(node, {
type: AST_NODE_TYPES.JSXNamespacedName,
namespace: this.createNode<TSESTree.JSXIdentifier>(node, {
type: AST_NODE_TYPES.JSXIdentifier,
name: text.slice(0, colonIndex),
range: [range[0], range[0] + colonIndex],
}),
name: this.createNode<TSESTree.JSXIdentifier>(node, {
type: AST_NODE_TYPES.JSXIdentifier,
name: text.slice(colonIndex + 1),
range: [range[0] + colonIndex + 1, range[1]],
}),
range,
});
this.registerTSNodeInNodeMap(node, result);
return result;
}

return this.convertJSXIdentifier(node);
}

/**
* Converts a TypeScript JSX node.tagName into an ESTree node.name
* @param node the tagName object from a JSX ts.Node
Expand All @@ -526,8 +566,8 @@ export class Converter {
private convertJSXTagName(
node: ts.JsxTagNameExpression,
parent: ts.Node,
): TSESTree.JSXMemberExpression | TSESTree.JSXIdentifier {
let result: TSESTree.JSXMemberExpression | TSESTree.JSXIdentifier;
): TSESTree.JSXTagNameExpression {
let result: TSESTree.JSXTagNameExpression;
switch (node.kind) {
case SyntaxKind.PropertyAccessExpression:
if (node.name.kind === SyntaxKind.PrivateIdentifier) {
Expand All @@ -539,27 +579,14 @@ export class Converter {
result = this.createNode<TSESTree.JSXMemberExpression>(node, {
type: AST_NODE_TYPES.JSXMemberExpression,
object: this.convertJSXTagName(node.expression, parent),
property: this.convertJSXTagName(
node.name,
parent,
) as TSESTree.JSXIdentifier,
property: this.convertJSXIdentifier(node.name),
});
break;

case SyntaxKind.ThisKeyword:
result = this.createNode<TSESTree.JSXIdentifier>(node, {
type: AST_NODE_TYPES.JSXIdentifier,
name: 'this',
});
break;

case SyntaxKind.Identifier:
default:
result = this.createNode<TSESTree.JSXIdentifier>(node, {
type: AST_NODE_TYPES.JSXIdentifier,
name: node.text,
});
break;
return this.convertJSXNamespaceOrIdentifier(node);
}

this.registerTSNodeInNodeMap(node, result);
Expand Down Expand Up @@ -2113,12 +2140,9 @@ export class Converter {
}

case SyntaxKind.JsxAttribute: {
const attributeName = this.convertChild(node.name);
attributeName.type = AST_NODE_TYPES.JSXIdentifier;

return this.createNode<TSESTree.JSXAttribute>(node, {
type: AST_NODE_TYPES.JSXAttribute,
name: attributeName,
name: this.convertJSXNamespaceOrIdentifier(node.name),
value: this.convertChild(node.initializer),
});
}
Expand Down Expand Up @@ -2407,36 +2431,40 @@ export class Converter {
}
return result;
}
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructorType: {
const result = this.createNode<TSESTree.TSConstructorType>(node, {
type: AST_NODE_TYPES.TSConstructorType,
params: this.convertParameters(node.parameters),
abstract: hasModifier(SyntaxKind.AbstractKeyword, node),
});
if (node.type) {
result.returnType = this.convertTypeAnnotation(node.type, node);
}
if (node.typeParameters) {
result.typeParameters = this.convertTSTypeParametersToTypeParametersDeclaration(
node.typeParameters,
);
}
return result;
}

case SyntaxKind.FunctionType:
case SyntaxKind.ConstructSignature:
case SyntaxKind.CallSignature: {
let type: AST_NODE_TYPES;
switch (node.kind) {
case SyntaxKind.ConstructSignature:
type = AST_NODE_TYPES.TSConstructSignatureDeclaration;
break;
case SyntaxKind.CallSignature:
type = AST_NODE_TYPES.TSCallSignatureDeclaration;
break;
case SyntaxKind.FunctionType:
type = AST_NODE_TYPES.TSFunctionType;
break;
case SyntaxKind.ConstructorType:
default:
type = AST_NODE_TYPES.TSConstructorType;
break;
}
const type =
node.kind === SyntaxKind.ConstructSignature
? AST_NODE_TYPES.TSConstructSignatureDeclaration
: node.kind === SyntaxKind.CallSignature
? AST_NODE_TYPES.TSCallSignatureDeclaration
: AST_NODE_TYPES.TSFunctionType;
const result = this.createNode<
| TSESTree.TSConstructSignatureDeclaration
| TSESTree.TSCallSignatureDeclaration
| TSESTree.TSFunctionType
| TSESTree.TSConstructorType
| TSESTree.TSCallSignatureDeclaration
| TSESTree.TSConstructSignatureDeclaration
>(node, {
type: type,
params: this.convertParameters(node.parameters),
});

if (node.type) {
result.returnType = this.convertTypeAnnotation(node.type, node);
}
Expand All @@ -2446,7 +2474,6 @@ export class Converter {
node.typeParameters,
);
}

return result;
}

Expand Down Expand Up @@ -2694,7 +2721,7 @@ export class Converter {
? (node as any).elementTypes.map((el: ts.Node) =>
this.convertType(el),
)
: node.elements.map((el: ts.Node) => this.convertType(el));
: node.elements.map(el => this.convertType(el));

return this.createNode<TSESTree.TSTupleType>(node, {
type: AST_NODE_TYPES.TSTupleType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export interface EstreeToTsNodeTypes {
[AST_NODE_TYPES.JSXSpreadAttribute]: ts.JsxSpreadAttribute;
[AST_NODE_TYPES.JSXSpreadChild]: ts.JsxExpression;
[AST_NODE_TYPES.JSXMemberExpression]: ts.PropertyAccessExpression;
[AST_NODE_TYPES.JSXNamespacedName]: ts.Identifier | ts.ThisExpression;
[AST_NODE_TYPES.JSXText]: ts.JsxText;
[AST_NODE_TYPES.LabeledStatement]: ts.LabeledStatement;
[AST_NODE_TYPES.Literal]:
Expand Down Expand Up @@ -157,15 +158,11 @@ export interface EstreeToTsNodeTypes {
| ts.ConstructorDeclaration;
[AST_NODE_TYPES.TSArrayType]: ts.ArrayTypeNode;
[AST_NODE_TYPES.TSAsExpression]: ts.AsExpression;
[AST_NODE_TYPES.TSCallSignatureDeclaration]: ts.PropertySignature;
[AST_NODE_TYPES.TSCallSignatureDeclaration]: ts.CallSignatureDeclaration;
[AST_NODE_TYPES.TSClassImplements]: ts.ExpressionWithTypeArguments;
[AST_NODE_TYPES.TSConditionalType]: ts.ConditionalTypeNode;
[AST_NODE_TYPES.TSConstructorType]: ts.ConstructorTypeNode;
[AST_NODE_TYPES.TSConstructSignatureDeclaration]:
| ts.ConstructorTypeNode
| ts.FunctionTypeNode
| ts.ConstructSignatureDeclaration
| ts.CallSignatureDeclaration;
[AST_NODE_TYPES.TSConstructSignatureDeclaration]: ts.ConstructSignatureDeclaration;
[AST_NODE_TYPES.TSDeclareFunction]: ts.FunctionDeclaration;
[AST_NODE_TYPES.TSEnumDeclaration]: ts.EnumDeclaration;
[AST_NODE_TYPES.TSEnumMember]: ts.EnumMember;
Expand Down
20 changes: 8 additions & 12 deletions packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,23 +299,19 @@ tester.addFixturePatternConfig('jsx', {
* https://github.com/Microsoft/TypeScript/issues/7410
*/
'embedded-tags',
/**
* JSX fixtures which have known issues for typescript-estree
* @see https://github.com/Microsoft/TypeScript/issues/7411
*/
'namespaced-attribute-and-value-inserted',
/**
* JSX fixtures which have known issues for typescript-estree
* @see https://github.com/Microsoft/TypeScript/issues/7411
*/
'namespaced-name-and-attribute',
/**
* Current random error difference on jsx/invalid-no-tag-name.src.js
* ts-estree - SyntaxError
* Babel - RangeError
* @see https://github.com/babel/babel/issues/6680
*/
'invalid-no-tag-name',
/**
* [BABEL ERRORED, BUT TS-ESTREE DID NOT]
* SyntaxError: Unexpected token
* TODO: investigate if this code is valid as there is no typescript error
*/
'invalid-namespace-value-with-dots',
],
});
tester.addFixturePatternConfig('jsx-useJSXTextNode');
Expand Down Expand Up @@ -346,7 +342,7 @@ tester.addFixturePatternConfig('typescript/basics', {
/**
* Babel parses it as TSQualifiedName
* ts parses it as MemberExpression
* TODO: report it to babel
* @see https://github.com/babel/babel/issues/12884
*/
'interface-with-extends-member-expression',
/**
Expand Down Expand Up @@ -387,7 +383,7 @@ tester.addFixturePatternConfig('typescript/basics', {
'import-type-error',
/**
* [TS-ESTREE ERRORED, BUT BABEL DID NOT]
* TODO: report this to babel
* This is intentional; babel is not checking types
*/
'catch-clause-with-invalid-annotation',
],
Expand Down
Loading

0 comments on commit 2ebfb21

Please sign in to comment.