Skip to content

Commit

Permalink
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 24 deletions.
6 changes: 6 additions & 0 deletions .changeset/twelve-colts-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: minor
swc_typescript: minor
---

feat(typescript): Check computed property names of ts signatures
22 changes: 2 additions & 20 deletions crates/swc_typescript/src/fast_dts/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use swc_ecma_ast::{

use super::{
type_ann,
util::ast_ext::{PatExt, PropNameExit},
util::ast_ext::{MemberExprExt, PatExt, PropNameExit},
FastDts,
};

Expand All @@ -17,25 +17,7 @@ impl FastDts {
if let Some(super_class) = &class.super_class {
let is_not_allowed = match super_class.as_ref() {
Expr::Ident(_) => false,
Expr::Member(member_expr) => {
let mut object = &member_expr.obj;
loop {
match object.as_ref() {
Expr::Member(member_expr) => {
object = &member_expr.obj;
continue;
}
Expr::OptChain(opt_chain) => {
if let Some(member_expr) = opt_chain.base.as_member() {
object = &member_expr.obj;
continue;
}
}
_ => {}
}
break !object.is_ident();
}
}
Expr::Member(member_expr) => !member_expr.get_first_object().is_ident(),
_ => true,
};

Expand Down
19 changes: 17 additions & 2 deletions crates/swc_typescript/src/fast_dts/decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,27 @@ impl FastDts {
);
}
}
Decl::TsInterface(_) | Decl::TsTypeAlias(_) => {
Decl::TsInterface(ts_interface) => {
if let Some(internal_annotations) = self.internal_annotations.as_ref() {
decl.visit_mut_children_with(&mut InternalAnnotationTransformer::new(
ts_interface.visit_mut_children_with(&mut InternalAnnotationTransformer::new(
internal_annotations,
))
}
for type_element in ts_interface.body.body.iter() {
self.check_ts_signature(type_element);
}
}
Decl::TsTypeAlias(ts_type_alias) => {
if let Some(internal_annotations) = self.internal_annotations.as_ref() {
ts_type_alias.visit_mut_children_with(&mut InternalAnnotationTransformer::new(
internal_annotations,
))
}
if let Some(ts_lit) = ts_type_alias.type_ann.as_ts_type_lit() {
for type_element in ts_lit.members.iter() {
self.check_ts_signature(type_element);
}
}
}
}
}
Expand Down
48 changes: 47 additions & 1 deletion crates/swc_typescript/src/fast_dts/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{
inferrer::ReturnTypeInferrer,
type_ann,
util::{
ast_ext::PatExt,
ast_ext::{MemberExprExt, PatExt},
types::{ts_keyword_type, ts_lit_type},
},
FastDts,
Expand Down Expand Up @@ -315,6 +315,52 @@ impl FastDts {
}
}

pub(crate) fn check_ts_signature(&mut self, signature: &TsTypeElement) {
match signature {
TsTypeElement::TsPropertySignature(ts_property_signature) => {
self.report_signature_property_key(
&ts_property_signature.key,
ts_property_signature.computed,
);
}
TsTypeElement::TsGetterSignature(ts_getter_signature) => {
self.report_signature_property_key(
&ts_getter_signature.key,
ts_getter_signature.computed,
);
}
TsTypeElement::TsSetterSignature(ts_setter_signature) => {
self.report_signature_property_key(
&ts_setter_signature.key,
ts_setter_signature.computed,
);
}
TsTypeElement::TsMethodSignature(ts_method_signature) => {
self.report_signature_property_key(
&ts_method_signature.key,
ts_method_signature.computed,
);
}
_ => {}
}
}

pub(crate) fn report_signature_property_key(&mut self, key: &Expr, computed: bool) {
if !computed {
return;
}

let is_not_allowed = match key {
Expr::Ident(_) => false,
Expr::Member(member) => !member.get_first_object().is_ident(),
_ => !Self::is_literal(key),
};

if is_not_allowed {
self.signature_computed_property_name(key.span());
}
}

pub(crate) fn tpl_to_string(&mut self, tpl: &Tpl) -> Option<Str> {
if !tpl.exprs.is_empty() {
return None;
Expand Down
30 changes: 29 additions & 1 deletion crates/swc_typescript/src/fast_dts/util/ast_ext.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::borrow::Cow;

use swc_atoms::Atom;
use swc_ecma_ast::{BindingIdent, Expr, Lit, MemberProp, ObjectPatProp, Pat, PropName, TsTypeAnn};
use swc_ecma_ast::{
BindingIdent, Expr, Lit, MemberExpr, MemberProp, ObjectPatProp, Pat, PropName, TsTypeAnn,
};

pub trait PatExt {
fn get_type_ann(&self) -> &Option<Box<TsTypeAnn>>;
Expand Down Expand Up @@ -117,3 +119,29 @@ impl MemberPropExt for MemberProp {
}
}
}

pub trait MemberExprExt {
fn get_first_object(&self) -> &Expr;
}

impl MemberExprExt for MemberExpr {
fn get_first_object(&self) -> &Expr {
let mut object = &self.obj;
loop {
match object.as_ref() {
Expr::Member(member_expr) => {
object = &member_expr.obj;
continue;
}
Expr::OptChain(opt_chain) => {
if let Some(member_expr) = opt_chain.base.as_member() {
object = &member_expr.obj;
continue;
}
}
_ => {}
}
break object;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
```==================== .D.TS ====================
export interface A {
["foo" as string]: number;
["bar" as string](a: number): string;
}
export type B = {
["foo" as string]: number;
["bar" as string](a: number): string;
};
==================== Errors ====================
x TS9014: Computed properties must be number or string literals, variables or dotted expressions with --isolatedDeclarations.
,-[$DIR/tests/fixture/signature-computed-property-name.ts:2:1]
1 | export interface A {
2 | ["foo" as string]: number;
: ^^^^^^^^^^^^^^^
3 | ["bar" as string](a: number): string;
`----
x TS9014: Computed properties must be number or string literals, variables or dotted expressions with --isolatedDeclarations.
,-[$DIR/tests/fixture/signature-computed-property-name.ts:3:1]
2 | ["foo" as string]: number;
3 | ["bar" as string](a: number): string;
: ^^^^^^^^^^^^^^^
4 | }
`----
x TS9014: Computed properties must be number or string literals, variables or dotted expressions with --isolatedDeclarations.
,-[$DIR/tests/fixture/signature-computed-property-name.ts:7:1]
6 | export type B = {
7 | ["foo" as string]: number;
: ^^^^^^^^^^^^^^^
8 | ["bar" as string](a: number): string;
`----
x TS9014: Computed properties must be number or string literals, variables or dotted expressions with --isolatedDeclarations.
,-[$DIR/tests/fixture/signature-computed-property-name.ts:8:1]
7 | ["foo" as string]: number;
8 | ["bar" as string](a: number): string;
: ^^^^^^^^^^^^^^^
9 | };
`----
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface A {
["foo" as string]: number;
["bar" as string](a: number): string;
}

export type B = {
["foo" as string]: number;
["bar" as string](a: number): string;
};

0 comments on commit caed78a

Please sign in to comment.