Skip to content

Commit

Permalink
feat(typescript-estree): support TSv4 labelled tuple members
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed Aug 9, 2020
1 parent 470174a commit 17bcfb2
Show file tree
Hide file tree
Showing 20 changed files with 2,497 additions and 100 deletions.
@@ -0,0 +1 @@
let x: [a: string, b?: number, c?: (string | number)]
@@ -0,0 +1 @@
let x: [a: string, ...b: number[]]
@@ -0,0 +1 @@
type Foo = [a: string, b?: string]
@@ -0,0 +1 @@
let x: [a: number, b: number, c: number];
51 changes: 33 additions & 18 deletions packages/types/src/ast-node-types.ts
Expand Up @@ -32,7 +32,6 @@ enum AST_NODE_TYPES {
FunctionExpression = 'FunctionExpression',
Identifier = 'Identifier',
IfStatement = 'IfStatement',
Import = 'Import',
ImportDeclaration = 'ImportDeclaration',
ImportDefaultSpecifier = 'ImportDefaultSpecifier',
ImportExpression = 'ImportExpression',
Expand Down Expand Up @@ -95,42 +94,46 @@ enum AST_NODE_TYPES {
TSArrayType = 'TSArrayType',
TSAsExpression = 'TSAsExpression',
TSAsyncKeyword = 'TSAsyncKeyword',
TSBooleanKeyword = 'TSBooleanKeyword',
TSBigIntKeyword = 'TSBigIntKeyword',
TSConditionalType = 'TSConditionalType',
TSConstructorType = 'TSConstructorType',
TSBooleanKeyword = 'TSBooleanKeyword',
TSCallSignatureDeclaration = 'TSCallSignatureDeclaration',
TSClassImplements = 'TSClassImplements',
TSConditionalType = 'TSConditionalType',
TSConstructorType = 'TSConstructorType',
TSConstructSignatureDeclaration = 'TSConstructSignatureDeclaration',
TSDeclareKeyword = 'TSDeclareKeyword',
TSDeclareFunction = 'TSDeclareFunction',
TSDeclareKeyword = 'TSDeclareKeyword',
TSEmptyBodyFunctionExpression = 'TSEmptyBodyFunctionExpression',
TSEnumDeclaration = 'TSEnumDeclaration',
TSEnumMember = 'TSEnumMember',
TSExportAssignment = 'TSExportAssignment',
TSExportKeyword = 'TSExportKeyword',
TSExternalModuleReference = 'TSExternalModuleReference',
TSFunctionType = 'TSFunctionType',
TSImportEqualsDeclaration = 'TSImportEqualsDeclaration',
TSImportType = 'TSImportType',
TSInferType = 'TSInferType',
TSLiteralType = 'TSLiteralType',
TSIndexedAccessType = 'TSIndexedAccessType',
TSIndexSignature = 'TSIndexSignature',
TSInferType = 'TSInferType',
TSInterfaceBody = 'TSInterfaceBody',
TSInterfaceDeclaration = 'TSInterfaceDeclaration',
TSInterfaceHeritage = 'TSInterfaceHeritage',
TSImportEqualsDeclaration = 'TSImportEqualsDeclaration',
TSFunctionType = 'TSFunctionType',
TSIntersectionType = 'TSIntersectionType',
TSLiteralType = 'TSLiteralType',
TSMappedType = 'TSMappedType',
TSMethodSignature = 'TSMethodSignature',
TSModuleBlock = 'TSModuleBlock',
TSModuleDeclaration = 'TSModuleDeclaration',
TSNamedTupleMember = 'TSNamedTupleMember',
TSNamespaceExportDeclaration = 'TSNamespaceExportDeclaration',
TSNonNullExpression = 'TSNonNullExpression',
TSNeverKeyword = 'TSNeverKeyword',
TSNonNullExpression = 'TSNonNullExpression',
TSNullKeyword = 'TSNullKeyword',
TSNumberKeyword = 'TSNumberKeyword',
TSMappedType = 'TSMappedType',
TSObjectKeyword = 'TSObjectKeyword',
TSOptionalType = 'TSOptionalType',
TSParameterProperty = 'TSParameterProperty',
TSParenthesizedType = 'TSParenthesizedType',
TSPrivateKeyword = 'TSPrivateKeyword',
TSPropertySignature = 'TSPropertySignature',
TSProtectedKeyword = 'TSProtectedKeyword',
Expand All @@ -142,25 +145,37 @@ enum AST_NODE_TYPES {
TSStringKeyword = 'TSStringKeyword',
TSSymbolKeyword = 'TSSymbolKeyword',
TSThisType = 'TSThisType',
TSTypeAnnotation = 'TSTypeAnnotation',
TSTupleType = 'TSTupleType',
TSTypeAliasDeclaration = 'TSTypeAliasDeclaration',
TSTypeAnnotation = 'TSTypeAnnotation',
TSTypeAssertion = 'TSTypeAssertion',
TSTypeLiteral = 'TSTypeLiteral',
TSTypeOperator = 'TSTypeOperator',
TSTypeParameter = 'TSTypeParameter',
TSTypeParameterDeclaration = 'TSTypeParameterDeclaration',
TSTypeParameterInstantiation = 'TSTypeParameterInstantiation',
TSTypePredicate = 'TSTypePredicate',
TSTypeReference = 'TSTypeReference',
TSTypeQuery = 'TSTypeQuery',
TSIntersectionType = 'TSIntersectionType',
TSTupleType = 'TSTupleType',
TSOptionalType = 'TSOptionalType',
TSParenthesizedType = 'TSParenthesizedType',
TSUnionType = 'TSUnionType',
TSTypeReference = 'TSTypeReference',
TSUndefinedKeyword = 'TSUndefinedKeyword',
TSUnionType = 'TSUnionType',
TSUnknownKeyword = 'TSUnknownKeyword',
TSVoidKeyword = 'TSVoidKeyword',
}

export { AST_NODE_TYPES };

// Below is a special type-only test which ensures that we don't accidentally leave unused keys in this enum
// eslint-disable-next-line import/first -- purposely down here to colocate it with this hack of a test
import type { Node } from './ts-estree';

type GetKeys<T extends AST_NODE_TYPES> = keyof Extract<Node, { type: T }>;
type AllKeys = {
readonly [T in AST_NODE_TYPES]: GetKeys<T>;
};
type TakesString<T extends Record<string, string>> = T;
// @ts-expect-error: purposely unused
type _Test =
// forcing the test onto a new line so it isn't covered by the expect error
// If there are any enum members that don't have a corresponding TSESTree.Node, then this line will error with "Type 'string | number | symbol' is not assignable to type 'string'."
void | TakesString<AllKeys>;
21 changes: 15 additions & 6 deletions packages/types/src/ts-estree.ts
Expand Up @@ -186,11 +186,11 @@ export type Node =
| JSXExpressionContainer
| JSXFragment
| JSXIdentifier
| JSXMemberExpression
| JSXOpeningElement
| JSXOpeningFragment
| JSXSpreadAttribute
| JSXSpreadChild
| JSXMemberExpression
| JSXText
| LabeledStatement
| Literal
Expand Down Expand Up @@ -246,15 +246,16 @@ export type Node =
| TSIndexedAccessType
| TSIndexSignature
| TSInferType
| TSInterfaceDeclaration
| TSInterfaceBody
| TSInterfaceDeclaration
| TSInterfaceHeritage
| TSIntersectionType
| TSLiteralType
| TSMappedType
| TSMethodSignature
| TSModuleBlock
| TSModuleDeclaration
| TSNamedTupleMember
| TSNamespaceExportDeclaration
| TSNeverKeyword
| TSNonNullExpression
Expand All @@ -264,10 +265,10 @@ export type Node =
| TSOptionalType
| TSParameterProperty
| TSParenthesizedType
| TSPropertySignature
| TSPublicKeyword
| TSPrivateKeyword
| TSPropertySignature
| TSProtectedKeyword
| TSPublicKeyword
| TSQualifiedName
| TSReadonlyKeyword
| TSRestType
Expand All @@ -291,8 +292,8 @@ export type Node =
| TSUnionType
| TSUnknownKeyword
| TSVoidKeyword
| UpdateExpression
| UnaryExpression
| UpdateExpression
| VariableDeclaration
| VariableDeclarator
| WhileStatement
Expand Down Expand Up @@ -525,6 +526,7 @@ export type TypeNode =
| TSIntersectionType
| TSLiteralType
| TSMappedType
| TSNamedTupleMember
| TSNeverKeyword
| TSNullKeyword
| TSNumberKeyword
Expand All @@ -539,8 +541,8 @@ export type TypeNode =
| TSTypeLiteral
| TSTypeOperator
| TSTypePredicate
| TSTypeReference
| TSTypeQuery
| TSTypeReference
| TSUndefinedKeyword
| TSUnionType
| TSUnknownKeyword
Expand Down Expand Up @@ -1603,6 +1605,13 @@ export interface TSThisType extends BaseNode {
type: AST_NODE_TYPES.TSThisType;
}

export interface TSNamedTupleMember extends BaseNode {
type: AST_NODE_TYPES.TSNamedTupleMember;
elementType: TypeNode;
label: Identifier;
optional: boolean;
}

export interface TSTupleType extends BaseNode {
type: AST_NODE_TYPES.TSTupleType;
elementTypes: TypeNode[];
Expand Down
6 changes: 3 additions & 3 deletions packages/typescript-estree/package.json
Expand Up @@ -50,9 +50,9 @@
"tsutils": "^3.17.1"
},
"devDependencies": {
"@babel/code-frame": "^7.8.3",
"@babel/parser": "^7.8.3",
"@babel/types": "^7.8.3",
"@babel/code-frame": "^7.10.4",
"@babel/parser": "^7.11.3",
"@babel/types": "^7.11.0",
"@types/babel__code-frame": "^7.0.1",
"@types/debug": "^4.1.5",
"@types/is-glob": "^4.0.1",
Expand Down
87 changes: 56 additions & 31 deletions packages/typescript-estree/src/convert.ts
Expand Up @@ -1603,12 +1603,14 @@ export class Converter {
imported: this.convertChild(node.propertyName ?? node.name),
});

case SyntaxKind.ImportClause:
case SyntaxKind.ImportClause: {
const local = this.convertChild(node.name);
return this.createNode<TSESTree.ImportDefaultSpecifier>(node, {
type: AST_NODE_TYPES.ImportDefaultSpecifier,
local: this.convertChild(node.name),
range: [node.getStart(this.ast), node.name!.end],
local,
range: local.range,
});
}

case SyntaxKind.ExportDeclaration:
if (node.exportClause?.kind === SyntaxKind.NamedExports) {
Expand Down Expand Up @@ -2621,34 +2623,12 @@ export class Converter {
}

// TypeScript specific types
case SyntaxKind.OptionalType: {
return this.createNode<TSESTree.TSOptionalType>(node, {
type: AST_NODE_TYPES.TSOptionalType,
typeAnnotation: this.convertType(node.type),
});
}
case SyntaxKind.ParenthesizedType: {
return this.createNode<TSESTree.TSParenthesizedType>(node, {
type: AST_NODE_TYPES.TSParenthesizedType,
typeAnnotation: this.convertType(node.type),
});
}
case SyntaxKind.TupleType: {
// In TS 4.0, the `elementTypes` property was changed to `elements`.
// To support both at compile time, we cast to access the newer version
// if the former does not exist.
const elementTypes =
'elementTypes' in node
? (node as any).elementTypes.map((el: ts.Node) =>
this.convertType(el),
)
: node.elements.map((el: ts.Node) => this.convertType(el));

return this.createNode<TSESTree.TSTupleType>(node, {
type: AST_NODE_TYPES.TSTupleType,
elementTypes,
});
}
case SyntaxKind.UnionType: {
return this.createNode<TSESTree.TSUnionType>(node, {
type: AST_NODE_TYPES.TSUnionType,
Expand All @@ -2661,12 +2641,6 @@ export class Converter {
types: node.types.map(el => this.convertType(el)),
});
}
case SyntaxKind.RestType: {
return this.createNode<TSESTree.TSRestType>(node, {
type: AST_NODE_TYPES.TSRestType,
typeAnnotation: this.convertType(node.type),
});
}
case SyntaxKind.AsExpression: {
return this.createNode<TSESTree.TSAsExpression>(node, {
type: AST_NODE_TYPES.TSAsExpression,
Expand Down Expand Up @@ -2732,6 +2706,57 @@ export class Converter {
type: AST_NODE_TYPES.TSAbstractKeyword,
});
}

// Tuple
case SyntaxKind.TupleType: {
// In TS 4.0, the `elementTypes` property was changed to `elements`.
// To support both at compile time, we cast to access the newer version
// if the former does not exist.
const elementTypes =
'elementTypes' in node
? (node as any).elementTypes.map((el: ts.Node) =>
this.convertType(el),
)
: node.elements.map((el: ts.Node) => this.convertType(el));

return this.createNode<TSESTree.TSTupleType>(node, {
type: AST_NODE_TYPES.TSTupleType,
elementTypes,
});
}
case SyntaxKind.NamedTupleMember: {
const member = this.createNode<TSESTree.TSNamedTupleMember>(node, {
type: AST_NODE_TYPES.TSNamedTupleMember,
elementType: this.convertType(node.type, node),
label: this.convertChild(node.name, node),
optional: node.questionToken != null,
});

if (node.dotDotDotToken) {
// adjust the start to account for the "..."
member.range[0] = member.label.range[0];
member.loc.start = member.label.loc.start;
return this.createNode<TSESTree.TSRestType>(node, {
type: AST_NODE_TYPES.TSRestType,
typeAnnotation: member,
});
}

return member;
}
case SyntaxKind.OptionalType: {
return this.createNode<TSESTree.TSOptionalType>(node, {
type: AST_NODE_TYPES.TSOptionalType,
typeAnnotation: this.convertType(node.type),
});
}
case SyntaxKind.RestType: {
return this.createNode<TSESTree.TSRestType>(node, {
type: AST_NODE_TYPES.TSRestType,
typeAnnotation: this.convertType(node.type),
});
}

default:
return this.deeplyCopy(node);
}
Expand Down
Expand Up @@ -68,7 +68,6 @@ export interface EstreeToTsNodeTypes {
| ts.ConstructorDeclaration
| ts.Token<ts.SyntaxKind.NewKeyword | ts.SyntaxKind.ImportKeyword>;
[AST_NODE_TYPES.IfStatement]: ts.IfStatement;
[AST_NODE_TYPES.Import]: ts.ImportExpression;
[AST_NODE_TYPES.ImportDeclaration]: ts.ImportDeclaration;
[AST_NODE_TYPES.ImportDefaultSpecifier]: ts.ImportClause;
[AST_NODE_TYPES.ImportExpression]: ts.CallExpression;
Expand Down Expand Up @@ -186,14 +185,18 @@ export interface EstreeToTsNodeTypes {
[AST_NODE_TYPES.TSMethodSignature]: ts.MethodSignature;
[AST_NODE_TYPES.TSModuleBlock]: ts.ModuleBlock;
[AST_NODE_TYPES.TSModuleDeclaration]: ts.ModuleDeclaration;
[AST_NODE_TYPES.TSNamedTupleMember]: ts.NamedTupleMember;
[AST_NODE_TYPES.TSNamespaceExportDeclaration]: ts.NamespaceExportDeclaration;
[AST_NODE_TYPES.TSNonNullExpression]: ts.NonNullExpression;
[AST_NODE_TYPES.TSOptionalType]: ts.OptionalTypeNode;
[AST_NODE_TYPES.TSParameterProperty]: ts.ParameterDeclaration;
[AST_NODE_TYPES.TSParenthesizedType]: ts.ParenthesizedTypeNode;
[AST_NODE_TYPES.TSPropertySignature]: ts.PropertySignature;
[AST_NODE_TYPES.TSQualifiedName]: ts.QualifiedName;
[AST_NODE_TYPES.TSRestType]: ts.RestTypeNode;
[AST_NODE_TYPES.TSRestType]:
| ts.RestTypeNode
// for consistency and following babel's choices, a named tuple member with a rest gets converted to a TSRestType
| ts.NamedTupleMember;
[AST_NODE_TYPES.TSThisType]: ts.ThisTypeNode;
[AST_NODE_TYPES.TSTupleType]: ts.TupleTypeNode;
[AST_NODE_TYPES.TSTypeAliasDeclaration]: ts.TypeAliasDeclaration;
Expand Down Expand Up @@ -275,5 +278,6 @@ export interface EstreeToTsNodeTypes {
*/
export type TSESTreeToTSNode<T extends TSESTree.Node = TSESTree.Node> = Extract<
TSNode | ts.Token<ts.SyntaxKind.NewKeyword | ts.SyntaxKind.ImportKeyword>,
// if this errors, it means that one of the AST_NODE_TYPES is not defined in the above interface
EstreeToTsNodeTypes[T['type']]
>;
1 change: 1 addition & 0 deletions packages/typescript-estree/src/ts-estree/ts-nodes.ts
Expand Up @@ -42,6 +42,7 @@ export type TSNode =
| ts.TypeQueryNode
| ts.TypeLiteralNode
| ts.ArrayTypeNode
| ts.NamedTupleMember
| ts.TupleTypeNode
| ts.OptionalTypeNode
| ts.RestTypeNode
Expand Down

0 comments on commit 17bcfb2

Please sign in to comment.