mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(transformer-dts): transform namespace support (#3683)
This PR implements namespace transform:
Look at this example
```ts
let internal = 0;
export namespace ns {
namespace internal {
export class Foo {}
}
export namespace nested {
export import inner = internal;
}
}
// to
export declare namespace ns {
namespace internal {
class Foo {
}
}
export namespace nested {
export import inner = internal;
}
export {};
}
```
The `let internal = 0` is unexported, and is unreferenced in other types. So we need to remove it.
I refactored `scope` because the previous implementation could not correctly remove unexported and unreferenced declarations.
For example, in this case
```ts
type T = string;
export function foo<T>(): T {
}
// to
type T = string;
export declare function foo<T>(): T;
```
The `type T = string` should be deleted, Because the `T` is not used in the function return type.
This commit is contained in:
parent
215826874d
commit
b22b59ae12
7 changed files with 374 additions and 54 deletions
|
|
@ -1335,6 +1335,12 @@ impl<'a> Modifiers<'a> {
|
|||
list.retain(|m| !m.kind.is_typescript_syntax());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_modifier(&mut self, modifier: Modifier) {
|
||||
if let Some(list) = self.0.as_mut() {
|
||||
list.push(modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Export Assignment in non-module files
|
||||
|
|
|
|||
|
|
@ -242,14 +242,9 @@ impl<'a> TransformerDts<'a> {
|
|||
|
||||
let body = self.ctx.ast.class_body(decl.body.span, elements);
|
||||
|
||||
let modifiers = if decl.modifiers.is_contains_abstract() {
|
||||
let modifiers = self.ctx.ast.new_vec_from_iter([
|
||||
Modifier { span: SPAN, kind: ModifierKind::Declare },
|
||||
Modifier { span: SPAN, kind: ModifierKind::Abstract },
|
||||
]);
|
||||
Modifiers::new(modifiers)
|
||||
} else {
|
||||
self.modifiers_declare()
|
||||
let mut modifiers = self.modifiers_declare();
|
||||
if decl.modifiers.is_contains_abstract() {
|
||||
modifiers.add_modifier(Modifier { span: SPAN, kind: ModifierKind::Abstract });
|
||||
};
|
||||
|
||||
Some(self.ctx.ast.class(
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
use oxc_ast::ast::*;
|
||||
|
||||
use oxc_allocator::Box;
|
||||
use oxc_ast::Visit;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_span::{GetSpan, SPAN};
|
||||
use oxc_syntax::scope::ScopeFlags;
|
||||
|
||||
use crate::TransformerDts;
|
||||
|
||||
|
|
@ -116,6 +118,53 @@ impl<'a> TransformerDts<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
fn transform_ts_module_block(
|
||||
&mut self,
|
||||
block: &Box<'a, TSModuleBlock<'a>>,
|
||||
) -> Box<'a, TSModuleBlock<'a>> {
|
||||
// We need to enter a new scope for the module block, avoid add binding to the parent scope
|
||||
self.scope.enter_scope(ScopeFlags::TsModuleBlock);
|
||||
let stmts = self.transform_statements_on_demand(&block.body);
|
||||
self.scope.leave_scope();
|
||||
self.ctx.ast.ts_module_block(SPAN, stmts)
|
||||
}
|
||||
|
||||
pub fn transform_ts_module_declaration(
|
||||
&mut self,
|
||||
decl: &Box<'a, TSModuleDeclaration<'a>>,
|
||||
) -> Box<'a, TSModuleDeclaration<'a>> {
|
||||
if decl.modifiers.is_contains_declare() {
|
||||
return self.ctx.ast.copy(decl);
|
||||
}
|
||||
|
||||
let Some(body) = &decl.body else {
|
||||
return self.ctx.ast.copy(decl);
|
||||
};
|
||||
|
||||
match body {
|
||||
TSModuleDeclarationBody::TSModuleDeclaration(decl) => {
|
||||
let inner = self.transform_ts_module_declaration(decl);
|
||||
return self.ctx.ast.ts_module_declaration(
|
||||
decl.span,
|
||||
self.ctx.ast.copy(&decl.id),
|
||||
Some(TSModuleDeclarationBody::TSModuleDeclaration(inner)),
|
||||
decl.kind,
|
||||
self.modifiers_declare(),
|
||||
);
|
||||
}
|
||||
TSModuleDeclarationBody::TSModuleBlock(block) => {
|
||||
let body = self.transform_ts_module_block(block);
|
||||
return self.ctx.ast.ts_module_declaration(
|
||||
decl.span,
|
||||
self.ctx.ast.copy(&decl.id),
|
||||
Some(TSModuleDeclarationBody::TSModuleBlock(body)),
|
||||
decl.kind,
|
||||
self.modifiers_declare(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_declaration(
|
||||
&mut self,
|
||||
decl: &Declaration<'a>,
|
||||
|
|
@ -175,7 +224,9 @@ impl<'a> TransformerDts<'a> {
|
|||
if self.scope.has_reference(&ident.name)
|
||||
)
|
||||
{
|
||||
Some(Declaration::TSModuleDeclaration(self.ctx.ast.copy(decl)))
|
||||
Some(Declaration::TSModuleDeclaration(
|
||||
self.transform_ts_module_declaration(decl),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use oxc_ast::ast::{
|
|||
TSType, TSTypeAnnotation,
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_span::SPAN;
|
||||
use oxc_span::{GetSpan, SPAN};
|
||||
|
||||
use crate::{return_type::FunctionReturnType, TransformerDts};
|
||||
|
||||
|
|
@ -78,7 +78,10 @@ impl<'a> TransformerDts<'a> {
|
|||
} else {
|
||||
if let Expression::TSAsExpression(expr) = &pattern.right {
|
||||
if !expr.type_annotation.is_keyword_or_literal() {
|
||||
self.ctx.error(OxcDiagnostic::error("Parameter must have an explicit type annotation with --isolatedDeclarations."));
|
||||
self.ctx.error(
|
||||
OxcDiagnostic::error("Parameter must have an explicit type annotation with --isolatedDeclarations.")
|
||||
.with_label(expr.type_annotation.span())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,13 +46,31 @@ impl<'a> TransformerDts<'a> {
|
|||
_trivias: Trivias,
|
||||
) -> Self {
|
||||
let ctx = Rc::new(TransformDtsCtx::new(allocator));
|
||||
Self { ctx, scope: ScopeTree::new() }
|
||||
Self { ctx, scope: ScopeTree::new(allocator) }
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Vec<Error>` if any errors were collected during the transformation.
|
||||
pub fn build(mut self, program: &Program<'a>) -> TransformerDtsReturn {
|
||||
let source_type = SourceType::default().with_module(true).with_typescript_definition(true);
|
||||
let directives = self.ctx.ast.new_vec();
|
||||
let stmts = self.transform_program(program);
|
||||
let program = self.ctx.ast.program(SPAN, source_type, directives, None, stmts);
|
||||
let source_text =
|
||||
Codegen::<false>::new("", "", Trivias::default(), CodegenOptions::default())
|
||||
.build(&program)
|
||||
.source_text;
|
||||
|
||||
TransformerDtsReturn { source_text, errors: self.ctx.take_errors() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TransformerDts<'a> {
|
||||
pub fn transform_program(
|
||||
&mut self,
|
||||
program: &Program<'a>,
|
||||
) -> oxc_allocator::Vec<'a, Statement<'a>> {
|
||||
let has_import_or_export = program.body.iter().any(|stmt| {
|
||||
matches!(
|
||||
stmt,
|
||||
|
|
@ -63,30 +81,24 @@ impl<'a> TransformerDts<'a> {
|
|||
)
|
||||
});
|
||||
|
||||
let stmts = if has_import_or_export {
|
||||
self.transform_program(program)
|
||||
if has_import_or_export {
|
||||
self.transform_statements_on_demand(&program.body)
|
||||
} else {
|
||||
self.transform_program_without_module_declaration(program)
|
||||
};
|
||||
|
||||
let source_type = SourceType::default().with_module(true).with_typescript_definition(true);
|
||||
let directives = self.ctx.ast.new_vec();
|
||||
let program = self.ctx.ast.program(SPAN, source_type, directives, None, stmts);
|
||||
let source_text =
|
||||
Codegen::<false>::new("", "", Trivias::default(), CodegenOptions::default())
|
||||
.build(&program)
|
||||
.source_text;
|
||||
TransformerDtsReturn { source_text, errors: self.ctx.take_errors() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modifiers_declare(&self) -> Modifiers<'a> {
|
||||
Modifiers::new(
|
||||
self.ctx.ast.new_vec_single(Modifier { span: SPAN, kind: ModifierKind::Declare }),
|
||||
)
|
||||
if self.scope.is_ts_module_block_flag() {
|
||||
// If we are in a module block, we don't need to add declare
|
||||
Modifiers::empty()
|
||||
} else {
|
||||
Modifiers::new(
|
||||
self.ctx.ast.new_vec_single(Modifier { span: SPAN, kind: ModifierKind::Declare }),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TransformerDts<'a> {
|
||||
pub fn transform_program_without_module_declaration(
|
||||
&mut self,
|
||||
program: &Program<'a>,
|
||||
|
|
@ -104,9 +116,9 @@ impl<'a> TransformerDts<'a> {
|
|||
new_ast_stmts
|
||||
}
|
||||
|
||||
pub fn transform_program(
|
||||
pub fn transform_statements_on_demand(
|
||||
&mut self,
|
||||
program: &Program<'a>,
|
||||
stmts: &oxc_allocator::Vec<'a, Statement<'a>>,
|
||||
) -> oxc_allocator::Vec<'a, Statement<'a>> {
|
||||
let mut new_stmts = Vec::new();
|
||||
let mut variables_declarations = VecDeque::new();
|
||||
|
|
@ -116,7 +128,7 @@ impl<'a> TransformerDts<'a> {
|
|||
// 2. Transform export declarations
|
||||
// 3. Collect all bindings / reference from module declarations
|
||||
// 4. Collect transformed indexes
|
||||
program.body.iter().for_each(|stmt| match stmt {
|
||||
stmts.iter().for_each(|stmt| match stmt {
|
||||
match_declaration!(Statement) => {
|
||||
match stmt.to_declaration() {
|
||||
Declaration::VariableDeclaration(decl) => {
|
||||
|
|
@ -229,9 +241,9 @@ impl<'a> TransformerDts<'a> {
|
|||
}
|
||||
|
||||
// 6. Transform variable/using declarations, import statements, remove unused imports
|
||||
// 7. Generate code
|
||||
let mut new_ast_stmts = self.ctx.ast.new_vec::<Statement<'a>>();
|
||||
for (index, stmt) in new_stmts.drain(..).enumerate() {
|
||||
// 7. Return transformed statements
|
||||
let mut new_ast_stmts = self.ctx.ast.new_vec_with_capacity(transformed_indexes.len());
|
||||
for (index, stmt) in new_stmts.into_iter().enumerate() {
|
||||
match stmt {
|
||||
_ if transformed_indexes.contains(&index) => {
|
||||
new_ast_stmts.push(stmt);
|
||||
|
|
@ -283,7 +295,7 @@ impl<'a> TransformerDts<'a> {
|
|||
if decl.specifiers.is_none() {
|
||||
new_ast_stmts.push(Statement::ImportDeclaration(decl));
|
||||
} else if let Some(decl) = self.transform_import_declaration(&decl) {
|
||||
new_ast_stmts.push(Statement::ImportDeclaration(self.ctx.ast.alloc(decl)));
|
||||
new_ast_stmts.push(Statement::ImportDeclaration(decl));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::ast::*;
|
||||
|
||||
use oxc_allocator::Box;
|
||||
use oxc_span::{GetSpan, SPAN};
|
||||
|
||||
use crate::TransformerDts;
|
||||
|
|
@ -90,7 +91,7 @@ impl<'a> TransformerDts<'a> {
|
|||
pub fn transform_import_declaration(
|
||||
&self,
|
||||
decl: &ImportDeclaration<'a>,
|
||||
) -> Option<ImportDeclaration<'a>> {
|
||||
) -> Option<Box<'a, ImportDeclaration<'a>>> {
|
||||
let specifiers = decl.specifiers.as_ref()?;
|
||||
|
||||
let mut specifiers = self.ctx.ast.copy(specifiers);
|
||||
|
|
@ -109,13 +110,13 @@ impl<'a> TransformerDts<'a> {
|
|||
// We don't need to print this import statement
|
||||
None
|
||||
} else {
|
||||
Some(ImportDeclaration {
|
||||
span: decl.span,
|
||||
specifiers: Some(specifiers),
|
||||
source: self.ctx.ast.copy(&decl.source),
|
||||
with_clause: self.ctx.ast.copy(&decl.with_clause),
|
||||
import_kind: decl.import_kind,
|
||||
})
|
||||
Some(self.ctx.ast.import_declaration(
|
||||
decl.span,
|
||||
Some(specifiers),
|
||||
self.ctx.ast.copy(&decl.source),
|
||||
self.ctx.ast.copy(&decl.with_clause),
|
||||
decl.import_kind,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +1,298 @@
|
|||
use oxc_allocator::{Allocator, Vec};
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_ast::{visit::walk::walk_export_default_declaration, Visit};
|
||||
use oxc_ast::AstBuilder;
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::{visit::walk::*, Visit};
|
||||
use oxc_span::Atom;
|
||||
use oxc_syntax::scope::ScopeFlags;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScopeTree<'a> {
|
||||
references: FxHashSet<Atom<'a>>,
|
||||
type_bindings: Vec<'a, FxHashSet<Atom<'a>>>,
|
||||
value_bindings: Vec<'a, FxHashSet<Atom<'a>>>,
|
||||
type_references: Vec<'a, FxHashSet<Atom<'a>>>,
|
||||
value_references: Vec<'a, FxHashSet<Atom<'a>>>,
|
||||
flags: Vec<'a, ScopeFlags>,
|
||||
}
|
||||
|
||||
impl<'a> ScopeTree<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { references: FxHashSet::default() }
|
||||
pub fn new(allocator: &'a Allocator) -> Self {
|
||||
let ast = AstBuilder::new(allocator);
|
||||
let mut scope = Self {
|
||||
type_bindings: ast.new_vec(),
|
||||
value_bindings: ast.new_vec(),
|
||||
type_references: ast.new_vec(),
|
||||
value_references: ast.new_vec(),
|
||||
flags: ast.new_vec(),
|
||||
};
|
||||
scope.enter_scope(ScopeFlags::Top);
|
||||
scope
|
||||
}
|
||||
|
||||
pub fn is_ts_module_block_flag(&self) -> bool {
|
||||
self.flags.last().unwrap().contains(ScopeFlags::TsModuleBlock)
|
||||
}
|
||||
|
||||
pub fn has_reference(&self, name: &Atom<'a>) -> bool {
|
||||
self.references.contains(name)
|
||||
self.value_references.last().is_some_and(|rs| rs.contains(name))
|
||||
|| self.type_references.last().is_some_and(|rs| rs.contains(name))
|
||||
}
|
||||
|
||||
pub fn references_len(&self) -> usize {
|
||||
self.references.len()
|
||||
self.value_references.last().unwrap().len() + self.type_references.last().unwrap().len()
|
||||
}
|
||||
|
||||
fn add_value_binding(&mut self, ident: &Atom<'a>) {
|
||||
self.value_bindings.last_mut().unwrap().insert(ident.clone());
|
||||
}
|
||||
|
||||
fn add_type_binding(&mut self, ident: &Atom<'a>) {
|
||||
self.type_bindings.last_mut().unwrap().insert(ident.clone());
|
||||
}
|
||||
|
||||
fn add_unresolved_value_reference(&mut self, ident: &Atom<'a>) {
|
||||
self.value_references.last_mut().unwrap().insert(ident.clone());
|
||||
}
|
||||
|
||||
fn add_unresolved_type_reference(&mut self, ident: &Atom<'a>) {
|
||||
self.type_references.last_mut().unwrap().insert(ident.clone());
|
||||
}
|
||||
|
||||
/// resolve references in the current scope
|
||||
/// and merge unresolved references to the parent scope
|
||||
/// and remove the current scope
|
||||
fn resolve_references(&mut self) {
|
||||
let current_value_bindings = self.value_bindings.pop().unwrap_or_default();
|
||||
let current_value_references = self.value_references.pop().unwrap_or_default();
|
||||
|
||||
self.type_references
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.extend(current_value_references.difference(¤t_value_bindings).cloned());
|
||||
|
||||
let current_type_bindings = self.type_bindings.pop().unwrap_or_default();
|
||||
let current_type_references = self.type_references.pop().unwrap_or_default();
|
||||
self.type_references
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.extend(current_type_references.difference(¤t_type_bindings).cloned());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for ScopeTree<'a> {
|
||||
fn enter_scope(&mut self, flags: ScopeFlags) {
|
||||
self.flags.push(flags);
|
||||
self.value_bindings.push(FxHashSet::default());
|
||||
self.type_bindings.push(FxHashSet::default());
|
||||
self.type_references.push(FxHashSet::default());
|
||||
self.value_references.push(FxHashSet::default());
|
||||
}
|
||||
|
||||
fn leave_scope(&mut self) {
|
||||
self.resolve_references();
|
||||
self.flags.pop();
|
||||
}
|
||||
|
||||
fn visit_identifier_reference(&mut self, ident: &IdentifierReference<'a>) {
|
||||
self.references.insert(ident.name.clone());
|
||||
self.add_unresolved_value_reference(&ident.name);
|
||||
}
|
||||
|
||||
fn visit_binding_pattern(&mut self, pattern: &BindingPattern<'a>) {
|
||||
if let BindingPatternKind::BindingIdentifier(ident) = &pattern.kind {
|
||||
self.add_value_binding(&ident.name);
|
||||
}
|
||||
walk_binding_pattern(self, pattern);
|
||||
}
|
||||
|
||||
fn visit_ts_type_name(&mut self, name: &TSTypeName<'a>) {
|
||||
if let TSTypeName::IdentifierReference(ident) = name {
|
||||
self.add_unresolved_type_reference(&ident.name);
|
||||
} else {
|
||||
walk_ts_type_name(self, name);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_type_query(&mut self, ty: &TSTypeQuery<'a>) {
|
||||
if let Some(type_name) = ty.expr_name.as_ts_type_name() {
|
||||
let ident = TSTypeName::get_first_name(type_name);
|
||||
self.add_unresolved_value_reference(&ident.name);
|
||||
} else {
|
||||
walk_ts_type_query(self, ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) {
|
||||
for specifier in &decl.specifiers {
|
||||
if let ModuleExportName::Identifier(ident) = &specifier.local {
|
||||
self.references.insert(ident.name.clone());
|
||||
self.add_type_binding(&ident.name);
|
||||
self.add_value_binding(&ident.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration<'a>) {
|
||||
if let ExportDefaultDeclarationKind::Identifier(ident) = &decl.declaration {
|
||||
self.references.insert(ident.name.clone());
|
||||
self.add_type_binding(&ident.name);
|
||||
self.add_value_binding(&ident.name);
|
||||
} else {
|
||||
walk_export_default_declaration(self, decl);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_declaration(&mut self, declaration: &Declaration<'a>) {
|
||||
match declaration {
|
||||
Declaration::VariableDeclaration(_) | Declaration::UsingDeclaration(_) => {
|
||||
// add binding in BindingPattern
|
||||
}
|
||||
Declaration::FunctionDeclaration(decl) => {
|
||||
if let Some(id) = decl.id.as_ref() {
|
||||
self.add_value_binding(&id.name);
|
||||
}
|
||||
}
|
||||
Declaration::ClassDeclaration(decl) => {
|
||||
if let Some(id) = decl.id.as_ref() {
|
||||
self.add_value_binding(&id.name);
|
||||
}
|
||||
}
|
||||
Declaration::TSTypeAliasDeclaration(decl) => {
|
||||
self.add_type_binding(&decl.id.name);
|
||||
}
|
||||
Declaration::TSInterfaceDeclaration(decl) => {
|
||||
self.add_type_binding(&decl.id.name);
|
||||
}
|
||||
Declaration::TSEnumDeclaration(decl) => {
|
||||
self.add_value_binding(&decl.id.name);
|
||||
self.add_type_binding(&decl.id.name);
|
||||
}
|
||||
Declaration::TSModuleDeclaration(decl) => {
|
||||
if let TSModuleDeclarationName::Identifier(ident) = &decl.id {
|
||||
self.add_value_binding(&ident.name);
|
||||
self.add_type_binding(&ident.name);
|
||||
}
|
||||
}
|
||||
Declaration::TSImportEqualsDeclaration(decl) => {
|
||||
self.add_value_binding(&decl.id.name);
|
||||
}
|
||||
}
|
||||
walk_declaration(self, declaration);
|
||||
}
|
||||
|
||||
// // TODO: handle ts infer and ts mapped type
|
||||
|
||||
// ==================== TSTypeParameter ====================
|
||||
|
||||
/// ```ts
|
||||
/// function foo<T>(x: T): T {
|
||||
/// ^^^
|
||||
/// `T` is a type parameter
|
||||
/// return x;
|
||||
/// }
|
||||
/// ```
|
||||
/// We should create a new scope for TSTypeParameterDeclaration
|
||||
/// Because the type parameter is can be used in following nodes
|
||||
/// until the end of the function. So we leave the scope in the parent node (Function)
|
||||
fn visit_ts_type_parameter_declaration(&mut self, decl: &TSTypeParameterDeclaration<'a>) {
|
||||
self.enter_scope(ScopeFlags::empty());
|
||||
walk_ts_type_parameter_declaration(self, decl);
|
||||
// exit scope in parent AST node
|
||||
}
|
||||
|
||||
fn visit_class(&mut self, class: &Class<'a>) {
|
||||
walk_class(self, class);
|
||||
if class.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, func: &Function<'a>, flags: Option<ScopeFlags>) {
|
||||
walk_function(self, func, flags);
|
||||
if func.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_arrow_expression(&mut self, expr: &ArrowFunctionExpression<'a>) {
|
||||
walk_arrow_expression(self, expr);
|
||||
if expr.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_type_alias_declaration(&mut self, decl: &TSTypeAliasDeclaration<'a>) {
|
||||
walk_ts_type_alias_declaration(self, decl);
|
||||
if decl.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_interface_declaration(&mut self, decl: &TSInterfaceDeclaration<'a>) {
|
||||
walk_ts_interface_declaration(self, decl);
|
||||
if decl.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_call_signature_declaration(&mut self, signature: &TSCallSignatureDeclaration<'a>) {
|
||||
walk_ts_call_signature_declaration(self, signature);
|
||||
if signature.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_method_signature(&mut self, signature: &TSMethodSignature<'a>) {
|
||||
walk_ts_method_signature(self, signature);
|
||||
if signature.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_construct_signature_declaration(
|
||||
&mut self,
|
||||
signature: &TSConstructSignatureDeclaration<'a>,
|
||||
) {
|
||||
walk_ts_construct_signature_declaration(self, signature);
|
||||
if signature.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ts_function_type(&mut self, signature: &TSFunctionType<'a>) {
|
||||
walk_ts_function_type(self, signature);
|
||||
if signature.type_parameters.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
|
||||
/// `type D<T> = { [K in keyof T]: K };`
|
||||
/// ^^^^^^^^^^^^^^^^^^^^
|
||||
/// `K` is a type parameter
|
||||
/// We need to add `K` to the scope
|
||||
fn visit_ts_mapped_type(&mut self, ty: &TSMappedType<'a>) {
|
||||
// copy from walk_ts_mapped_type
|
||||
self.enter_scope(ScopeFlags::empty());
|
||||
self.add_type_binding(&ty.type_parameter.name.name);
|
||||
if let Some(name) = &ty.name_type {
|
||||
self.visit_ts_type(name);
|
||||
}
|
||||
if let Some(type_annotation) = &ty.type_annotation {
|
||||
self.visit_ts_type(type_annotation);
|
||||
}
|
||||
self.leave_scope();
|
||||
}
|
||||
|
||||
/// `export type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;`
|
||||
/// ^^^^^^^^^^
|
||||
/// `Item` is a type parameter
|
||||
/// We need to add `Item` to the scope
|
||||
fn visit_conditional_expression(&mut self, expr: &ConditionalExpression<'a>) {
|
||||
self.enter_scope(ScopeFlags::empty());
|
||||
walk_conditional_expression(self, expr);
|
||||
self.leave_scope();
|
||||
}
|
||||
|
||||
fn visit_ts_infer_type(&mut self, ty: &TSInferType<'a>) {
|
||||
// copy from walk_ts_infer_type
|
||||
self.add_type_binding(&ty.type_parameter.name.name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue