Skip to content

Commit e4c1e03

Browse files
authoredJan 23, 2025··
fix(typescript): Allow references to the global Symbol in computed property names under isolatedDeclarations (#9869)
fixes #9859 ref: microsoft/TypeScript#58771 Pass `unresolved_mark` to FastDts so it can check whether a `Symbol` ident refs to global Symbol.
1 parent 606ffe5 commit e4c1e03

File tree

13 files changed

+206
-7
lines changed

13 files changed

+206
-7
lines changed
 

‎.changeset/long-turtles-grow.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc: major
3+
swc_typescript: major
4+
---
5+
6+
fix(typescript): Allow references to the global Symbol in computed property names under isolatedDeclarations

‎crates/swc/benches/isolated_declarations.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fn bench_isolated_declarations(criterion: &mut Criterion) {
3636
let internal_annotations = FastDts::get_internal_annotations(&comments);
3737
let mut checker = FastDts::new(
3838
fm.name.clone(),
39+
unresolved_mark,
3940
FastDtsOptions {
4041
internal_annotations: Some(internal_annotations),
4142
},

‎crates/swc/src/config/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ impl Options {
812812
.into_bool(),
813813
codegen_inline_script,
814814
emit_isolated_dts: experimental.emit_isolated_dts.into_bool(),
815+
unresolved_mark,
815816
resolver,
816817
})
817818
}
@@ -1124,6 +1125,7 @@ pub struct BuiltInput<P: Pass> {
11241125
pub codegen_inline_script: bool,
11251126

11261127
pub emit_isolated_dts: bool,
1128+
pub unresolved_mark: Mark,
11271129
pub resolver: Option<(FileName, Arc<dyn ImportResolver>)>,
11281130
}
11291131

@@ -1156,6 +1158,7 @@ where
11561158
emit_assert_for_import_attributes: self.emit_assert_for_import_attributes,
11571159
codegen_inline_script: self.codegen_inline_script,
11581160
emit_isolated_dts: self.emit_isolated_dts,
1161+
unresolved_mark: self.unresolved_mark,
11591162
resolver: self.resolver,
11601163
}
11611164
}

‎crates/swc/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,8 @@ impl Compiler {
983983
let trailing = std::rc::Rc::new(RefCell::new(trailing.clone()));
984984

985985
let comments = SingleThreadedComments::from_leading_and_trailing(leading, trailing);
986-
let mut checker = FastDts::new(fm.name.clone(), Default::default());
986+
let mut checker =
987+
FastDts::new(fm.name.clone(), config.unresolved_mark, Default::default());
987988
let mut program = program.clone();
988989

989990
if let Some((base, resolver)) = config.resolver {

‎crates/swc_typescript/examples/isolated_declarations.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub fn main() {
3434
let internal_annotations = FastDts::get_internal_annotations(&comments);
3535
let mut checker = FastDts::new(
3636
fm.name.clone(),
37+
unresolved_mark,
3738
FastDtsOptions {
3839
internal_annotations: Some(internal_annotations),
3940
},

‎crates/swc_typescript/src/fast_dts/class.rs

+32-2
Original file line numberDiff line numberDiff line change
@@ -501,12 +501,42 @@ impl FastDts {
501501

502502
pub(crate) fn report_property_key(&mut self, key: &PropName) -> bool {
503503
if let Some(computed) = key.as_computed() {
504+
let is_symbol = self.is_global_symbol_object(&computed.expr);
504505
let is_literal = Self::is_literal(&computed.expr);
505-
if !is_literal {
506+
if !is_symbol && !is_literal {
506507
self.computed_property_name(key.span());
507508
}
508-
return !is_literal;
509+
return !is_symbol && !is_literal;
509510
}
510511
false
511512
}
513+
514+
pub(crate) fn is_global_symbol_object(&self, expr: &Expr) -> bool {
515+
let Some(obj) = (match expr {
516+
Expr::Member(member) => Some(&member.obj),
517+
Expr::OptChain(opt_chain) => opt_chain.base.as_member().map(|member| &member.obj),
518+
_ => None,
519+
}) else {
520+
return false;
521+
};
522+
523+
// https://github.com/microsoft/TypeScript/blob/cbac1ddfc73ca3b9d8741c1b51b74663a0f24695/src/compiler/transformers/declarations.ts#L1011
524+
if let Some(ident) = obj.as_ident() {
525+
// Exactly `Symbol.something` and `Symbol` either does not resolve
526+
// or definitely resolves to the global Symbol
527+
return ident.sym.as_str() == "Symbol" && ident.ctxt.has_mark(self.unresolved_mark);
528+
}
529+
530+
if let Some(member_expr) = obj.as_member() {
531+
// Exactly `globalThis.Symbol.something` and `globalThis` resolves
532+
// to the global `globalThis`
533+
if let Some(ident) = member_expr.obj.as_ident() {
534+
return ident.sym.as_str() == "globalThis"
535+
&& ident.ctxt.has_mark(self.unresolved_mark)
536+
&& member_expr.prop.is_ident_with("Symbol");
537+
}
538+
}
539+
540+
false
541+
}
512542
}

‎crates/swc_typescript/src/fast_dts/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::{borrow::Cow, mem::take, sync::Arc};
33
use rustc_hash::{FxHashMap, FxHashSet};
44
use swc_atoms::Atom;
55
use swc_common::{
6-
comments::SingleThreadedComments, util::take::Take, BytePos, FileName, Span, Spanned, DUMMY_SP,
6+
comments::SingleThreadedComments, util::take::Take, BytePos, FileName, Mark, Span, Spanned,
7+
DUMMY_SP,
78
};
89
use swc_ecma_ast::{
910
BindingIdent, Decl, DefaultDecl, ExportDefaultExpr, Id, Ident, ImportSpecifier, ModuleDecl,
@@ -38,6 +39,7 @@ mod visitors;
3839
/// The original code is MIT licensed.
3940
pub struct FastDts {
4041
filename: Arc<FileName>,
42+
unresolved_mark: Mark,
4143
diagnostics: Vec<DtsIssue>,
4244
// states
4345
id_counter: u32,
@@ -53,10 +55,11 @@ pub struct FastDtsOptions {
5355

5456
/// Diagnostics
5557
impl FastDts {
56-
pub fn new(filename: Arc<FileName>, options: FastDtsOptions) -> Self {
58+
pub fn new(filename: Arc<FileName>, unresolved_mark: Mark, options: FastDtsOptions) -> Self {
5759
let internal_annotations = options.internal_annotations;
5860
Self {
5961
filename,
62+
unresolved_mark,
6063
diagnostics: Vec::new(),
6164
id_counter: 0,
6265
is_top_level: true,

‎crates/swc_typescript/tests/deno_test.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! Tests copied from deno
22
//! Make some changes to align with tsc
33
4+
use swc_common::Mark;
45
use swc_ecma_ast::EsVersion;
56
use swc_ecma_codegen::to_code;
67
use swc_ecma_parser::{parse_file_as_program, Syntax, TsSyntax};
8+
use swc_ecma_transforms_base::resolver;
79
use swc_typescript::fast_dts::FastDts;
810

911
#[track_caller]
@@ -14,8 +16,8 @@ fn transform_dts_test(source: &str, expected: &str) {
1416
source.to_string(),
1517
);
1618

17-
let mut checker = FastDts::new(fm.name.clone(), Default::default());
18-
19+
let unresolved_mark = Mark::new();
20+
let top_level_mark = Mark::new();
1921
let mut program = parse_file_as_program(
2022
&fm,
2123
Syntax::Typescript(TsSyntax {
@@ -25,8 +27,11 @@ fn transform_dts_test(source: &str, expected: &str) {
2527
None,
2628
&mut Vec::new(),
2729
)
30+
.map(|program| program.apply(resolver(unresolved_mark, top_level_mark, true)))
2831
.unwrap();
2932

33+
let mut checker = FastDts::new(fm.name.clone(), unresolved_mark, Default::default());
34+
3035
let _issues = checker.transform(&mut program);
3136

3237
let code = to_code(&program);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
```==================== .D.TS ====================
2+
3+
export declare class NumberRange implements Iterable<number> {
4+
private start;
5+
private end;
6+
constructor(start: number, end: number);
7+
[Symbol.iterator](): Iterator<number>;
8+
}
9+
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export class NumberRange implements Iterable<number> {
2+
private start: number;
3+
private end: number;
4+
5+
constructor(start: number, end: number) {
6+
this.start = start;
7+
this.end = end;
8+
}
9+
10+
[Symbol.iterator](): Iterator<number> {
11+
let current = this.start;
12+
const end = this.end;
13+
14+
return {
15+
next(): IteratorResult<number> {
16+
if (current <= end) {
17+
return { value: current++, done: false };
18+
} else {
19+
return { value: undefined, done: true };
20+
}
21+
},
22+
};
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
```==================== .D.TS ====================
2+
3+
// Correct
4+
export declare const foo: {
5+
[Symbol.iterator]: () => void;
6+
[Symbol.asyncIterator]: () => Promise<void>;
7+
[globalThis.Symbol.iterator]: () => void;
8+
[Symbol.toStringTag]: string;
9+
};
10+
export declare abstract class Foo {
11+
[Symbol.iterator](): void;
12+
[Symbol.asyncIterator](): Promise<void>;
13+
[globalThis.Symbol.iterator](): void;
14+
get [Symbol.toStringTag](): string;
15+
}
16+
// Incorrect
17+
export declare namespace Foo {
18+
const foo: {
19+
};
20+
}
21+
export declare function bar(Symbol: {
22+
}, globalThis: {
23+
}): {
24+
};
25+
26+
27+
==================== Errors ====================
28+
x TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations.
29+
,-[$DIR/tests/fixture/symbol-properties.ts:46:1]
30+
45 | export const foo = {
31+
46 | [Symbol.iterator]: (): void => {},
32+
: ^^^^^^^^^^^^^^^^^
33+
47 | [globalThis.Symbol.iterator]: (): void => {},
34+
`----
35+
x TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations.
36+
,-[$DIR/tests/fixture/symbol-properties.ts:47:1]
37+
46 | [Symbol.iterator]: (): void => {},
38+
47 | [globalThis.Symbol.iterator]: (): void => {},
39+
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
48 | };
41+
`----
42+
x TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations.
43+
,-[$DIR/tests/fixture/symbol-properties.ts:53:1]
44+
52 | return {
45+
53 | [Symbol.iterator]: (): void => {},
46+
: ^^^^^^^^^^^^^^^^^
47+
54 | [globalThis.Symbol.iterator]: (): void => {},
48+
`----
49+
x TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations.
50+
,-[$DIR/tests/fixture/symbol-properties.ts:54:1]
51+
53 | [Symbol.iterator]: (): void => {},
52+
54 | [globalThis.Symbol.iterator]: (): void => {},
53+
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
54+
55 | };
55+
`----
56+
57+
58+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Correct
2+
export const foo = {
3+
[Symbol.iterator]: (): void => {},
4+
[Symbol.asyncIterator]: async (): Promise<void> => {},
5+
[globalThis.Symbol.iterator]: (): void => {},
6+
get [Symbol.toStringTag]() {
7+
return "foo";
8+
},
9+
};
10+
11+
export abstract class Foo {
12+
[Symbol.iterator](): void {}
13+
async [Symbol.asyncIterator](): Promise<void> {}
14+
[globalThis.Symbol.iterator](): void {}
15+
get [Symbol.toStringTag]() {
16+
return "Foo";
17+
}
18+
}
19+
20+
// OK because these are not exported
21+
22+
namespace Foo {
23+
const Symbol = {};
24+
const globalThis = {};
25+
26+
export const foo = {
27+
[Symbol.iterator]: (): void => {},
28+
[globalThis.Symbol.iterator]: (): void => {},
29+
};
30+
}
31+
32+
function bar(Symbol: {}, globalThis: {}) {
33+
return {
34+
[Symbol.iterator]: (): void => {},
35+
[globalThis.Symbol.iterator]: (): void => {},
36+
};
37+
}
38+
39+
// Incorrect
40+
41+
export namespace Foo {
42+
const Symbol = {};
43+
const globalThis = {};
44+
45+
export const foo = {
46+
[Symbol.iterator]: (): void => {},
47+
[globalThis.Symbol.iterator]: (): void => {},
48+
};
49+
}
50+
51+
export function bar(Symbol: {}, globalThis: {}) {
52+
return {
53+
[Symbol.iterator]: (): void => {},
54+
[globalThis.Symbol.iterator]: (): void => {},
55+
};
56+
}

‎crates/swc_typescript/tests/typescript.rs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ fn fixture(input: PathBuf) {
3535
let internal_annotations = FastDts::get_internal_annotations(&comments);
3636
let mut checker = FastDts::new(
3737
fm.name.clone(),
38+
unresolved_mark,
3839
FastDtsOptions {
3940
internal_annotations: Some(internal_annotations),
4041
},

0 commit comments

Comments
 (0)
Please sign in to comment.