Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: TypeScript 4.2 syntax support #3112

Merged
merged 10 commits into from Mar 1, 2021
4 changes: 2 additions & 2 deletions package.json
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"
}
}
@@ -0,0 +1 @@
<this:bar />;
@@ -0,0 +1 @@
const x: abstract new () => void;
@@ -0,0 +1 @@
const x: new () => void;
1 change: 1 addition & 0 deletions packages/types/src/ast-node-types.ts
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
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is unrelated to upgrade of typescript

| 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
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
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;
bradzacher marked this conversation as resolved.
Show resolved Hide resolved

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;
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
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
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;
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
[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;
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
[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
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