Skip to content

AST: Make source field of TSImportType a StringLiteral #16111

@overlookmotel

Description

@overlookmotel

Continuation of #16074.

The expression inside import(...) in TS context (TSImportType) cannot legally be anything other than a string literal.

Our parser is too liberal. We currently accept all kind of crazy stuff like:

type A = typeof import(`react`);
type B = typeof import(A);
type C = typeof import(`${A} ${B}`);
type D = typeof import(typeof import('react'));

Playground

TypeScript says these are all errors: TS Playground

AST change

We should alter the type of the source field from TSType<'a> to StringLiteral<'a>:

pub struct TSImportType<'a> {
pub span: Span,
#[estree(via = TSImportTypeSource)]
pub source: TSType<'a>,
pub options: Option<Box<'a, ObjectExpression<'a>>>,
pub qualifier: Option<TSImportTypeQualifier<'a>>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}

pub struct TSImportType<'a> { 
    pub span: Span, 
-   #[estree(via = TSImportTypeSource)] 
-   pub source: TSType<'a>, 
+   pub source: StringLiteral<'a>, 
    pub options: Option<Box<'a, ObjectExpression<'a>>>, 
    pub qualifier: Option<TSImportTypeQualifier<'a>>, 
    pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>, 
} 

Parser changes

If we change the AST first and run just ast to regenerate traits code, then all the places which rely on the type of source field will become apparent. We'll then need to alter the parser to throw an error if it encounters something that isn't a string literal.

ESTree serialization

Currently there's a custom deserializer to alter the AST on JS side from TSType to StringLiteral:

/// Serializer for `source` field of `TSImportType`.
///
/// * Field is named `argument` in Oxc AST.
/// * Serialized as a `StringLiteral` - all other values are illegal syntax.
#[ast_meta]
#[estree(
ts_type = "StringLiteral",
raw_deser = "
let source = DESER[TSType](POS_OFFSET.argument);
if (source.type === 'TSLiteralType') {
source = source.literal;
if (PARENT) source.parent = parent;
} else {
// Should be unreachable - illegal syntax
}
source
"
)]
pub struct TSImportTypeSource<'a, 'b>(pub &'b TSImportType<'a>);
impl ESTree for TSImportTypeSource<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let source = &self.0.argument;
if let TSType::TSLiteralType(ts_lit_type) = source
&& let TSLiteral::StringLiteral(str_lit) = &ts_lit_type.literal
{
str_lit.serialize(serializer);
return;
}
// Should be unreachable - illegal syntax
source.serialize(serializer);
}
}

Once we've changed the field type in AST, this code can be deleted - it's dead code once #[estree(via = TSImportTypeSource)] attribute is removed from the source field in AST struct.

Metadata

Metadata

Assignees

Labels

A-astArea - ASTA-parserArea - Parsergood first issueExperience Level - Good for newcomers

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions