feat(semantic): check for invalid type import assignments (#4097)

Adds checks to `TSImportEqualsDeclaration` for invalid use of `import type` modifier.

```ts
import { Foo } from './foo'
namespace Bar {
  export class Baz {}
}

import type A = Foo.Baz;        // not allowed
import type B = Bar.Baz;        // not allowed
import type C = require('./c'); // allowed
```
This commit is contained in:
DonIsaac 2024-07-08 03:14:21 +00:00
parent 3fcad5e16f
commit e386b62331
4 changed files with 47 additions and 3 deletions

View file

@ -194,6 +194,13 @@ impl<'a> TSModuleBlock<'a> {
}
}
impl<'a> TSModuleReference<'a> {
/// Returns `true` if this is an [`TSModuleReference::ExternalModuleReference`].
pub fn is_external(&self) -> bool {
matches!(self, Self::ExternalModuleReference(_))
}
}
impl<'a> Decorator<'a> {
/// Get the name of the decorator
/// ```ts

View file

@ -99,6 +99,9 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) {
}
AstKind::TSModuleDeclaration(decl) => ts::check_ts_module_declaration(decl, ctx),
AstKind::TSEnumDeclaration(decl) => ts::check_ts_enum_declaration(decl, ctx),
AstKind::TSImportEqualsDeclaration(decl) => {
ts::check_ts_import_equals_declaration(decl, ctx);
}
_ => {}
}
}

View file

@ -168,3 +168,19 @@ pub fn check_ts_enum_declaration<'a>(decl: &TSEnumDeclaration<'a>, ctx: &Semanti
}
});
}
/// TS(1392)
fn import_alias_cannot_use_import_type(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("TS(1392): An import alias cannot use 'import type'").with_label(span)
}
pub fn check_ts_import_equals_declaration<'a>(
decl: &TSImportEqualsDeclaration<'a>,
ctx: &SemanticBuilder<'a>,
) {
// `import type Foo = require('./foo')` is allowed
// `import { Foo } from './foo'; import type Bar = Foo.Bar` is not allowed
if decl.import_kind.is_type() && !decl.module_reference.is_external() {
ctx.error(import_alias_cannot_use_import_type(decl.span));
}
}

View file

@ -3,7 +3,7 @@ commit: 12619ffe
parser_babel Summary:
AST Parsed : 2093/2101 (99.62%)
Positive Passed: 2083/2101 (99.14%)
Negative Passed: 1371/1501 (91.34%)
Negative Passed: 1373/1501 (91.47%)
Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js"
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions/input.js"
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js"
@ -86,8 +86,6 @@ Expect Syntax Error: "typescript/expect-plugin/export-type-named/input.js"
Expect Syntax Error: "typescript/export/equals-in-script/input.ts"
Expect Syntax Error: "typescript/import/equals-in-script/input.ts"
Expect Syntax Error: "typescript/import/equals-require-in-script/input.ts"
Expect Syntax Error: "typescript/import/export-import-type/input.ts"
Expect Syntax Error: "typescript/import/type-equals/input.ts"
Expect Syntax Error: "typescript/interface/get-set-invalid-optional-parameter/input.ts"
Expect Syntax Error: "typescript/interface/get-set-invalid-optional-parameter-babel-7/input.ts"
Expect Syntax Error: "typescript/interface/get-set-invalid-parameters/input.ts"
@ -10270,6 +10268,26 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts"
· ──
╰────
× TS(1392): An import alias cannot use 'import type'
╭─[typescript/import/export-import-type/input.ts:1:8]
1 │ export import type A = B.C;
· ────────────────────
╰────
× TS(1392): An import alias cannot use 'import type'
╭─[typescript/import/type-equals/input.ts:1:1]
1 │ import type A = B.C;
· ────────────────────
2 │ import type B = C;
╰────
× TS(1392): An import alias cannot use 'import type'
╭─[typescript/import/type-equals/input.ts:2:1]
1 │ import type A = B.C;
2 │ import type B = C;
· ──────────────────
╰────
× 'abstract' modifier cannot be used here.
╭─[typescript/interface/abstract/input.ts:1:1]
1 │ abstract interface Foo {