From 7126adb5e8652cffebd024ed916aadc07551779a Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Fri, 23 May 2025 11:51:28 +0000 Subject: [PATCH] fix(isolated-declarations): type of class setter/getter cannot be inferred when the key is a global `Symbol.xxx` expression (#11236) Similar to #11229, using `PropertyKey` to store accessor annotation because `[Symbol.xxx]` is not a static name. --- crates/oxc_isolated_declarations/src/class.rs | 67 +++++++++---------- .../tests/fixtures/set-get-accessor.ts | 39 +++++++++++ .../tests/snapshots/set-get-accessor.snap | 29 ++++++++ 3 files changed, 99 insertions(+), 36 deletions(-) diff --git a/crates/oxc_isolated_declarations/src/class.rs b/crates/oxc_isolated_declarations/src/class.rs index c618a64e5501d..16cbf7009f0b5 100644 --- a/crates/oxc_isolated_declarations/src/class.rs +++ b/crates/oxc_isolated_declarations/src/class.rs @@ -1,9 +1,6 @@ -use std::borrow::Cow; - use oxc_allocator::{Box as ArenaBox, CloneIn, Vec as ArenaVec}; use oxc_ast::{NONE, ast::*}; -use oxc_span::{GetSpan, SPAN}; -use rustc_hash::FxHashMap; +use oxc_span::{ContentEq, GetSpan, SPAN}; use crate::{ IsolatedDeclarations, @@ -51,12 +48,16 @@ impl<'a> IsolatedDeclarations<'a> { } } + pub(crate) fn is_valid_property_key(key: &PropertyKey<'a>) -> bool { + Self::is_literal_key(key) || Self::is_global_symbol(key) + } + pub(crate) fn report_property_key(&self, key: &PropertyKey<'a>) -> bool { - if !Self::is_literal_key(key) && !Self::is_global_symbol(key) { + if Self::is_valid_property_key(key) { + false + } else { self.error(computed_property_name(key.span())); true - } else { - false } } @@ -291,24 +292,20 @@ impl<'a> IsolatedDeclarations<'a> { /// ### Setter /// /// 1. If it has no parameter type, infer it from the getter method's return type - fn collect_getter_or_setter_annotations( + fn collect_accessor_annotations( &self, decl: &Class<'a>, - ) -> FxHashMap, ArenaBox<'a, TSTypeAnnotation<'a>>> { - let mut method_annotations = FxHashMap::default(); + ) -> Vec<(PropertyKey<'a>, ArenaBox<'a, TSTypeAnnotation<'a>>)> { + let mut method_annotations = Vec::new(); for element in &decl.body.body { if let ClassElement::MethodDefinition(method) = element { if (method.key.is_private_identifier() || method.accessibility.is_some_and(TSAccessibility::is_private)) - || (method.computed && !Self::is_literal_key(&method.key)) + || (method.computed && !Self::is_valid_property_key(&method.key)) { continue; } - let Some(name) = method.key.static_name() else { - continue; - }; - match method.kind { MethodDefinitionKind::Set => { let Some(first_param) = method.value.params.items.first() else { @@ -317,17 +314,15 @@ impl<'a> IsolatedDeclarations<'a> { if let Some(annotation) = first_param.pattern.type_annotation.clone_in(self.ast.allocator) { - method_annotations.insert(name, annotation); + method_annotations + .push((method.key.clone_in(self.ast.allocator), annotation)); } } MethodDefinitionKind::Get => { let function = &method.value; - if let Some(annotation) = function - .return_type - .clone_in(self.ast.allocator) - .or_else(|| self.infer_function_return_type(function)) - { - method_annotations.insert(name, annotation); + if let Some(annotation) = self.infer_function_return_type(function) { + method_annotations + .push((method.key.clone_in(self.ast.allocator), annotation)); } } _ => {} @@ -356,7 +351,7 @@ impl<'a> IsolatedDeclarations<'a> { } } - let setter_getter_annotations = self.collect_getter_or_setter_annotations(decl); + let accessor_annotations = self.collect_accessor_annotations(decl); let mut has_private_key = false; let mut elements = self.ast.vec(); let mut is_function_overloads = false; @@ -400,10 +395,12 @@ impl<'a> IsolatedDeclarations<'a> { let mut params = params.clone_in(self.ast.allocator); if let Some(param) = params.items.first_mut() { if let Some(annotation) = - method.key.static_name().and_then(|name| { - setter_getter_annotations - .get(&name) - .map(|a| a.clone_in(self.ast.allocator)) + accessor_annotations.iter().find_map(|(key, annotation)| { + if method.key.content_eq(key) { + Some(annotation.clone_in(self.ast.allocator)) + } else { + None + } }) { param.pattern.type_annotation = Some(annotation); @@ -448,15 +445,13 @@ impl<'a> IsolatedDeclarations<'a> { rt } MethodDefinitionKind::Get => { - let rt = method.value.return_type.clone_in(self.ast.allocator).or_else( - || { - method - .key - .static_name() - .and_then(|name| setter_getter_annotations.get(&name)) - .map(|a| a.clone_in(self.ast.allocator)) - }, - ); + let rt = accessor_annotations.iter().find_map(|(key, annotation)| { + if method.key.content_eq(key) { + Some(annotation.clone_in(self.ast.allocator)) + } else { + None + } + }); if rt.is_none() { self.error(accessor_must_have_explicit_return_type( method.key.span(), diff --git a/crates/oxc_isolated_declarations/tests/fixtures/set-get-accessor.ts b/crates/oxc_isolated_declarations/tests/fixtures/set-get-accessor.ts index 3a67ba0057b71..4dc0c293d657d 100644 --- a/crates/oxc_isolated_declarations/tests/fixtures/set-get-accessor.ts +++ b/crates/oxc_isolated_declarations/tests/fixtures/set-get-accessor.ts @@ -28,3 +28,42 @@ class ClsBad { set a(v) { } } + + +/// Infer type from the function body of the getter +class GlobalSymbol1 { + set [Symbol.toStringTag](v) { + } + get [Symbol.toStringTag]() { + return "string"; + } +} + +// Infer type from the parameter type of the setter +class GlobalSymbol2 { + set [Symbol.toStringTag](v: number) { + } + get [Symbol.toStringTag]() { + return GlobalSymbol2 + } +} + +// Infer type from the parameter type of the setter +class GlobalSymbol3 { + set [Symbol.toStringTag](v: number) { + } + get [Symbol.toStringTag](): string { + return "string"; + } +} + +// Cannot infer type from the function body of the getter +class GlobalSymbol4 { + set [Symbol.toStringTag](v) { + } + get [Symbol.toStringTag]() { + return GlobalSymbol4; + } +} + + diff --git a/crates/oxc_isolated_declarations/tests/snapshots/set-get-accessor.snap b/crates/oxc_isolated_declarations/tests/snapshots/set-get-accessor.snap index 0892ab614acc7..d0f27a74eae54 100644 --- a/crates/oxc_isolated_declarations/tests/snapshots/set-get-accessor.snap +++ b/crates/oxc_isolated_declarations/tests/snapshots/set-get-accessor.snap @@ -22,6 +22,26 @@ declare class ClsBad { get a(); set a(v); } +/// Infer type from the function body of the getter +declare class GlobalSymbol1 { + set [Symbol.toStringTag](v: string); + get [Symbol.toStringTag](): string; +} +// Infer type from the parameter type of the setter +declare class GlobalSymbol2 { + set [Symbol.toStringTag](v: number); + get [Symbol.toStringTag](): number; +} +// Infer type from the parameter type of the setter +declare class GlobalSymbol3 { + set [Symbol.toStringTag](v: number); + get [Symbol.toStringTag](): number; +} +// Cannot infer type from the function body of the getter +declare class GlobalSymbol4 { + set [Symbol.toStringTag](v); + get [Symbol.toStringTag](); +} ==================== Errors ==================== @@ -35,5 +55,14 @@ declare class ClsBad { 26 | return; `---- + x TS9009: At least one accessor must have an explicit return type annotation + | with --isolatedDeclarations. + ,-[64:8] + 63 | } + 64 | get [Symbol.toStringTag]() { + : ^^^^^^^^^^^^^^^^^^ + 65 | return GlobalSymbol4; + `---- + ```