-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(ast-spec): tighter types and documentation for declaration/* #9211
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(ast-spec): tighter types and documentation for declaration/* #9211
Conversation
Thanks for the PR, @Josh-Cena! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
☁️ Nx Cloud ReportCI is running/has finished running commands for commit fc730f1. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this CI Pipeline Execution ✅ Successfully ran 1 targetSent with 💌 from NxCloud. |
d02a303
to
4407bad
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments
*/ | ||
exported: Identifier | null; | ||
/** | ||
* The kind of the export. | ||
*/ | ||
// TODO(#1852) - breaking change remove this because it is a semantic error to have it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TS now allows export type * from
*/ | ||
exportKind: ExportKind; | ||
exportKind: 'value'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tightened: export type default
is not legal
@@ -64,9 +71,14 @@ export interface ExportNamedDeclarationWithoutSourceWithMultiple | |||
attributes: ImportAttribute[]; | |||
declaration: null; | |||
source: null; | |||
specifiers: ExportSpecifier[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removals here are not breaking because these properties are already declared on the base
// TODO: we should have a way to differentiate between these two cases | ||
specifiers: ImportClause[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the future: specifiers
should be able to be null
, like how typeParameters
can be both empty or null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't ever happen, sadly. The ESTree spec is pretty well locked in once defined and so we're stuck with the decisions of the past.
If we change something like this then we're liable to break all sorts of rules that assume it's always defined.
In general we can only ever change our AST extension shapes and the "core" AST is fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hhhh, I forgot this is not TS specific. Fine then.
/** | ||
* TS1183: An implementation cannot be declared in ambient contexts. | ||
*/ | ||
body: undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tightened: I think this is safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct to do.
The error you cited is a semantic error, not a syntax error.
This means that TS produces an AST for the error and us we do too.
/** | ||
* TS1040: 'async' modifier cannot be used in an ambient context. | ||
*/ | ||
async: false; | ||
declare: true; | ||
/** | ||
* TS1221: Generators are not allowed in an ambient context. | ||
*/ | ||
generator: false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tightened
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto above - the errors are semantic and TS still produces an AST - AND it includes these flags correctly.
So we can't refine these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't we supposed to throw on semantic errors? Wasn't fisker fixing some of these before?
@@ -19,7 +19,45 @@ export interface TSImportEqualsDeclaration extends BaseNode { | |||
* import F3 = require('mod'); | |||
* ``` | |||
*/ | |||
moduleReference: EntityName | TSExternalModuleReference; | |||
// TODO(#1852) - breaking change remove this as it is invalid | |||
moduleReference: Identifier | TSQualifiedName | TSExternalModuleReference; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slight tightening: EntityName
is a moving target and doesn't really make sense here. I will continue to fix uses of it. It broke when ThisExpression
became a valid EntityName
.
export type TSImportEqualsDeclaration = | ||
| TSImportEqualsNamespaceDeclaration | ||
| TSImportEqualsRequireDeclaration; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to union
// TODO: we emit the wrong AST for `module A.B {}` | ||
// https://github.com/typescript-eslint/typescript-eslint/pull/6272 only fixed namespaces | ||
// Maybe not worth fixing since it's legacy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth pointing out
export type LetOrConstOrVarDeclaration = | ||
| LetOrVarDeclaredDeclaration | ||
| LetOrVarNonDeclaredDeclaration | ||
| ConstDeclaredDeclaration | ||
| ConstNonDeclaredDeclaration; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The biggest change: made LetOrConstOrVarDeclaration
much stricter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing that needs to be checked with these changes is like const x;
- do we have a syntax error or do we produce an AST.
If it's the latter then we can't refine the types as the types have to reflect the AST we output.
For the specific example of const x:
- we produce an AST so the types must allow the case of const
with an init: null
. playground
or eg const x! = 1;
- we produce an AST so the tyoes nust allow the case. playground
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of this is great!
But there's a chunk that I think causes drift between the types and the ASTs we produce.
The important distinction when looking at TS errors is "does TS crash, or does it produce an AST"? In most cases it's the latter.
TS's parser is very forgiving and most errors are semantic errors, not syntactic errors - meaning it produces an AST even if the code has errors.
If TS produces an AST then in most cases we do too. So we need to ensure that the types respect that.
We can always add hard errors to our AST translation step to enforce invariants so that we can change the types! But doing so will be a breaking change for the v8 branch.
Please also add tests for any cases / permutations that you think are missing. More AST snapshots is always good!
Really, even when the AST is nonsense and no program is not going to work? For example #7202 was not breaking |
It generally would require a quick chat with the prettier folk. We want to make sure that we're not breaking their experience by throwing on parsable code. If they're okay then we can land it non-breaking. In the past a lot of the invariants we've encoded were just the invariants that prettier itself encoded (allowing them to delete the checks and make assumptions). I'm all for a stricter AST transform - just don't want to kill downstream usecases with it. |
@bradzacher I am splitting normative changes out into separate PRs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a few lint errors @Josh-Cena otherwise ready to ship |
PR Checklist
Overview
Although the types are tighter, it contains no breaking tree shape changes.
Also added a bunch of documentation.