Skip to content

Commit

Permalink
feat(es/typescript): Support const modifier on type parameters (#6672)
Browse files Browse the repository at this point in the history
**Related issue:**

 - microsoft/TypeScript#51865.
  • Loading branch information
magic-akari committed Jan 10, 2023
1 parent b102504 commit 019cf37
Show file tree
Hide file tree
Showing 784 changed files with 11,175 additions and 19 deletions.
@@ -0,0 +1,25 @@
//// [typeParameterConstModifiers.ts]
//!
//! x 'const' modifier can only appear on a type parameter of a function, method or class
//! ,-[41:1]
//! 41 | const fx1 = <const T>(x: T) => x;
//! 42 | const fx2 = <const T,>(x: T) => x;
//! 43 |
//! 44 | interface I1<const T> { x: T } // Error
//! : ^^^^^
//! 45 |
//! 46 | interface I2 {
//! 47 | f<const T>(x: T): T;
//! `----
//!
//! x 'const' modifier can only appear on a type parameter of a function, method or class
//! ,-[47:1]
//! 47 | f<const T>(x: T): T;
//! 48 | }
//! 49 |
//! 50 | type T1<const T> = T; // Error
//! : ^^^^^
//! 51 |
//! 52 | type T2 = <const T>(x: T) => T;
//! 53 | type T3 = { <const T>(x: T): T };
//! `----
@@ -0,0 +1,25 @@
//// [typeParameterConstModifiers.ts]
//!
//! x 'const' modifier can only appear on a type parameter of a function, method or class
//! ,-[41:1]
//! 41 | const fx1 = <const T>(x: T) => x;
//! 42 | const fx2 = <const T,>(x: T) => x;
//! 43 |
//! 44 | interface I1<const T> { x: T } // Error
//! : ^^^^^
//! 45 |
//! 46 | interface I2 {
//! 47 | f<const T>(x: T): T;
//! `----
//!
//! x 'const' modifier can only appear on a type parameter of a function, method or class
//! ,-[47:1]
//! 47 | f<const T>(x: T): T;
//! 48 | }
//! 49 |
//! 50 | type T1<const T> = T; // Error
//! : ^^^^^
//! 51 |
//! 52 | type T2 = <const T>(x: T) => T;
//! 53 | type T3 = { <const T>(x: T): T };
//! `----
3 changes: 3 additions & 0 deletions crates/swc_ecma_ast/src/typescript.rs
Expand Up @@ -54,6 +54,9 @@ pub struct TsTypeParam {
#[serde(default, rename = "out")]
pub is_out: bool,

#[serde(default, rename = "const")]
pub is_const: bool,

#[serde(default)]
pub constraint: Option<Box<TsType>>,

Expand Down
5 changes: 5 additions & 0 deletions crates/swc_ecma_codegen/src/typescript.rs
Expand Up @@ -971,6 +971,11 @@ where
fn emit_ts_type_param(&mut self, n: &TsTypeParam) -> Result {
self.emit_leading_comments_of_span(n.span(), false)?;

if n.is_const {
keyword!("const");
space!();
}

if n.is_in {
keyword!("in");
space!();
Expand Down
6 changes: 6 additions & 0 deletions crates/swc_ecma_parser/src/error.rs
Expand Up @@ -255,6 +255,7 @@ pub enum SyntaxError {
TS1267,
TS1273(JsWord),
TS1274(JsWord),
TS1277(JsWord),
TS1383,
TS2206,
TS2207,
Expand Down Expand Up @@ -650,6 +651,11 @@ impl SyntaxError {
word
)
.into(),
SyntaxError::TS1277(word) => format!(
"'{}' modifier can only appear on a type parameter of a function, method or class",
word
)
.into(),
SyntaxError::TS1383 => "Only named exports may use 'export type'.".into(),
SyntaxError::TS2206 => "The 'type' modifier cannot be used on a named import when \
'import type' is used on its import statement."
Expand Down
8 changes: 4 additions & 4 deletions crates/swc_ecma_parser/src/parser/class_and_fn.rs
Expand Up @@ -93,7 +93,7 @@ impl<I: Tokens> Parser<I> {
}

let type_params = if p.input.syntax().typescript() {
p.try_parse_ts_type_params(true)?
p.try_parse_ts_type_params(true, true)?
} else {
None
};
Expand Down Expand Up @@ -716,7 +716,7 @@ impl<I: Tokens> Parser<I> {
self.emit_err(span!(self, start), SyntaxError::TS1098);
self.emit_err(span!(self, start2), SyntaxError::TS1092);
} else {
let type_params = self.try_parse_ts_type_params(false)?;
let type_params = self.try_parse_ts_type_params(false, true)?;

if let Some(type_params) = type_params {
for param in type_params.params {
Expand Down Expand Up @@ -1184,7 +1184,7 @@ impl<I: Tokens> Parser<I> {
trace_cur!(p, parse_fn_args_body__type_params);

Ok(if is!(p, '<') {
Some(p.parse_ts_type_params(false)?)
Some(p.parse_ts_type_params(false, true)?)
} else if is!(p, JSXTagStart) {
debug_assert_eq!(
p.input.token_context().current(),
Expand All @@ -1197,7 +1197,7 @@ impl<I: Tokens> Parser<I> {
);
p.input.token_context_mut().pop();

Some(p.parse_ts_type_params(false)?)
Some(p.parse_ts_type_params(false, true)?)
} else {
None
})
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_parser/src/parser/expr.rs
Expand Up @@ -104,7 +104,7 @@ impl<I: Tokens> Parser<I> {
}
}

let type_parameters = p.parse_ts_type_params(false)?;
let type_parameters = p.parse_ts_type_params(false, false)?;
let mut arrow = p.parse_assignment_expr_base()?;
match *arrow {
Expr::Arrow(ArrowExpr {
Expand Down
50 changes: 36 additions & 14 deletions crates/swc_ecma_parser/src/parser/typescript.rs
Expand Up @@ -37,7 +37,7 @@ impl<I: Tokens> Parser<I> {
let pos = {
let modifier = match *cur!(self, true)? {
Token::Word(ref w @ Word::Ident(..))
| Token::Word(ref w @ Word::Keyword(Keyword::In)) => w.cow(),
| Token::Word(ref w @ Word::Keyword(Keyword::In | Keyword::Const)) => w.cow(),

_ => return Ok(None),
};
Expand Down Expand Up @@ -369,11 +369,16 @@ impl<I: Tokens> Parser<I> {
}

/// `tsParseTypeParameter`
fn parse_ts_type_param(&mut self, allow_modifier: bool) -> PResult<TsTypeParam> {
fn parse_ts_type_param(
&mut self,
permit_in_out: bool,
permit_const: bool,
) -> PResult<TsTypeParam> {
debug_assert!(self.input.syntax().typescript());

let mut is_in = false;
let mut is_out = false;
let mut is_const = false;

let start = cur_pos!(self);

Expand All @@ -392,8 +397,18 @@ impl<I: Tokens> Parser<I> {
false,
)? {
match modifer {
"const" => {
if !permit_const {
self.emit_err(
self.input.prev_span(),
SyntaxError::TS1277(js_word!("const")),
);
} else {
is_const = true;
}
}
"in" => {
if !allow_modifier {
if !permit_in_out {
self.emit_err(self.input.prev_span(), SyntaxError::TS1274(js_word!("in")));
} else if is_in {
self.emit_err(self.input.prev_span(), SyntaxError::TS1030(js_word!("in")));
Expand All @@ -407,7 +422,7 @@ impl<I: Tokens> Parser<I> {
}
}
"out" => {
if !allow_modifier {
if !permit_in_out {
self.emit_err(self.input.prev_span(), SyntaxError::TS1274(js_word!("out")));
} else if is_out {
self.emit_err(self.input.prev_span(), SyntaxError::TS1030(js_word!("out")));
Expand All @@ -428,6 +443,7 @@ impl<I: Tokens> Parser<I> {
name,
is_in,
is_out,
is_const,
constraint,
default,
})
Expand All @@ -436,7 +452,8 @@ impl<I: Tokens> Parser<I> {
/// `tsParseTypeParameter`
pub(super) fn parse_ts_type_params(
&mut self,
allow_modifier: bool,
permit_in_out: bool,
permit_const: bool,
) -> PResult<Box<TsTypeParamDecl>> {
self.in_type().parse_with(|p| {
p.ts_in_no_context(|p| {
Expand All @@ -449,7 +466,7 @@ impl<I: Tokens> Parser<I> {

let params = p.parse_ts_bracketed_list(
ParsingContext::TypeParametersOrArguments,
|p| p.parse_ts_type_param(allow_modifier), // bracket
|p| p.parse_ts_type_param(permit_in_out, permit_const), // bracket
false,
// skip_first_token
true,
Expand Down Expand Up @@ -1066,7 +1083,7 @@ impl<I: Tokens> Parser<I> {
_ => {}
}

let type_params = self.try_parse_ts_type_params(true)?;
let type_params = self.try_parse_ts_type_params(true, false)?;

let extends = if eat!(self, "extends") {
self.parse_ts_heritage_clause()?
Expand Down Expand Up @@ -1108,7 +1125,7 @@ impl<I: Tokens> Parser<I> {
debug_assert!(self.input.syntax().typescript());

let id = self.parse_ident_name()?;
let type_params = self.try_parse_ts_type_params(true)?;
let type_params = self.try_parse_ts_type_params(true, false)?;
let type_ann = self.expect_then_parse_ts_type(&tok!('='), "=")?;
expect!(self, ';');
Ok(Box::new(TsTypeAliasDecl {
Expand Down Expand Up @@ -1269,7 +1286,7 @@ impl<I: Tokens> Parser<I> {
}

// ----- inlined self.tsFillSignature(tt.colon, node);
let type_params = self.try_parse_ts_type_params(false)?;
let type_params = self.try_parse_ts_type_params(false, true)?;
expect!(self, '(');
let params = self.parse_ts_binding_list_for_signature()?;
let type_ann = if is!(self, ':') {
Expand Down Expand Up @@ -1406,7 +1423,7 @@ impl<I: Tokens> Parser<I> {
let optional = eat!(self, '?');

if !readonly && is_one_of!(self, '(', '<') {
let type_params = self.try_parse_ts_type_params(false)?;
let type_params = self.try_parse_ts_type_params(false, true)?;
expect!(self, '(');
let params = self.parse_ts_binding_list_for_signature()?;
let type_ann = if is!(self, ':') {
Expand Down Expand Up @@ -1606,6 +1623,7 @@ impl<I: Tokens> Parser<I> {
name,
is_in: false,
is_out: false,
is_const: false,
constraint,
default: None,
})
Expand Down Expand Up @@ -1817,7 +1835,7 @@ impl<I: Tokens> Parser<I> {
}

// ----- inlined `self.tsFillSignature(tt.arrow, node)`
let type_params = self.try_parse_ts_type_params(false)?;
let type_params = self.try_parse_ts_type_params(false, true)?;
expect!(self, '(');
let params = self.parse_ts_binding_list_for_signature()?;
let type_ann = self.parse_ts_type_or_type_predicate_ann(&tok!("=>"))?;
Expand Down Expand Up @@ -1985,14 +2003,17 @@ impl<I: Tokens> Parser<I> {
/// `tsTryParseTypeParameters`
pub(super) fn try_parse_ts_type_params(
&mut self,
allow_modifier: bool,
permit_in_out: bool,
permit_const: bool,
) -> PResult<Option<Box<TsTypeParamDecl>>> {
if !cfg!(feature = "typescript") {
return Ok(None);
}

if is!(self, '<') {
return self.parse_ts_type_params(allow_modifier).map(Some);
return self
.parse_ts_type_params(permit_in_out, permit_const)
.map(Some);
}
Ok(None)
}
Expand Down Expand Up @@ -2251,6 +2272,7 @@ impl<I: Tokens> Parser<I> {
name: type_param_name,
is_in: false,
is_out: false,
is_const: false,
constraint,
default: None,
};
Expand Down Expand Up @@ -2604,7 +2626,7 @@ impl<I: Tokens> Parser<I> {

let res = if is_one_of!(self, '<', JSXTagStart) {
self.try_parse_ts(|p| {
let type_params = p.parse_ts_type_params(false)?;
let type_params = p.parse_ts_type_params(false, false)?;
// Don't use overloaded parseFunctionParams which would look for "<" again.
expect!(p, '(');
let params: Vec<Pat> = p
Expand Down
9 changes: 9 additions & 0 deletions crates/swc_ecma_parser/tests/tsc/1.0lib-noErrors.json
Expand Up @@ -19821,6 +19821,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -20058,6 +20059,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": {
"type": "TsArrayType",
"span": {
Expand Down Expand Up @@ -22339,6 +22341,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -23191,6 +23194,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -23802,6 +23806,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -24120,6 +24125,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -24256,6 +24262,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -24423,6 +24430,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down Expand Up @@ -24559,6 +24567,7 @@
},
"in": false,
"out": false,
"const": false,
"constraint": null,
"default": null
}
Expand Down

0 comments on commit 019cf37

Please sign in to comment.