-
-
Notifications
You must be signed in to change notification settings - Fork 841
Description
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'));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>:
oxc/crates/oxc_ast/src/ast/ts.rs
Lines 1382 to 1389 in 35d0ee4
| 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:
oxc/crates/oxc_ast/src/serialize/ts.rs
Lines 506 to 538 in 91eb3f2
| /// 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.