mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(isolated_declarations): drop required type check for private parameter properties on private constructors (#21515)
## Summary
A private parameter property on a private constructor doesn't need an explicit type annotation:
- The constructor signature is collapsed to `private constructor();` — the parameter is never emitted.
- The parameter-property class member is emitted as `private readonly name;` — type-erased.
Since the parameter's type does not appear in the `.d.ts` output in either place, no type is needed. This matches `tsc --isolatedDeclarations`. Non-private parameter properties on a private constructor still require a type, because the class member keeps the type.
## Example
```ts
export class GreyhoundServer {
private constructor(
private readonly logger = new ConsoleLogger(['greyhound-server']),
) {}
}
```
Before: `TS9011: Parameter must have an explicit type annotation`.
After: no error — emits `private readonly logger;` and `private constructor();`, matching tsc.
## Test plan
- [x] Added snapshot fixtures covering both the no-error case (private param property) and the still-errors case (public param property on private constructor)
- [x] `cargo test -p oxc_isolated_declarations`
Closes #21503 (case 1)
This commit is contained in:
1 parent
c1eeae3e12
commit
48967e8325
3 files changed
+73
-5
No files matched your search
@@ -41,6 +41,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
&self,
|
||||
param: &FormalParameter<'a>,
|
||||
is_remaining_params_have_required: bool,
|
||||
in_private_constructor: bool,
|
||||
) -> Option<FormalParameter<'a>> {
|
||||
let pattern = ¶m.pattern;
|
||||
if param.initializer.is_some()
|
||||
@@ -69,9 +70,14 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
.as_ref()
|
||||
.map(|type_annotation| type_annotation.type_annotation.clone_in(self.ast.allocator))
|
||||
.or_else(|| {
|
||||
// report error for has no type annotation
|
||||
let new_type = self.infer_type_from_formal_parameter(param);
|
||||
if new_type.is_none() {
|
||||
// A private parameter property on a private constructor needs no
|
||||
// explicit type: the constructor signature is collapsed to
|
||||
// `private constructor();` and the class member is emitted as
|
||||
// `private readonly name;` with no type annotation.
|
||||
let is_elided_private_param = in_private_constructor
|
||||
&& param.accessibility.is_some_and(TSAccessibility::is_private);
|
||||
if new_type.is_none() && !is_elided_private_param {
|
||||
self.error(parameter_must_have_explicit_type(param.span));
|
||||
}
|
||||
new_type
|
||||
@@ -132,7 +138,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
pub(crate) fn transform_formal_parameters(
|
||||
&self,
|
||||
params: &FormalParameters<'a>,
|
||||
skip_no_accessibility_param: bool,
|
||||
in_private_constructor: bool,
|
||||
) -> ArenaBox<'a, FormalParameters<'a>> {
|
||||
if params.kind.is_signature() || (params.rest.is_none() && params.items.is_empty()) {
|
||||
return self.ast.alloc(params.clone_in(self.ast.allocator));
|
||||
@@ -143,14 +149,18 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, item)| !skip_no_accessibility_param || item.has_modifier())
|
||||
.filter(|(_, item)| !in_private_constructor || item.has_modifier())
|
||||
.filter_map(|(index, item)| {
|
||||
let is_remaining_params_have_required = params
|
||||
.items
|
||||
.iter()
|
||||
.skip(index)
|
||||
.any(|item| !(item.optional || item.initializer.is_some()));
|
||||
self.transform_formal_parameter(item, is_remaining_params_have_required)
|
||||
self.transform_formal_parameter(
|
||||
item,
|
||||
is_remaining_params_have_required,
|
||||
in_private_constructor,
|
||||
)
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -174,3 +174,30 @@ export class AccessorPairWithProtectedPrivateSetter {
|
||||
|
||||
private set value(v: number) {}
|
||||
}
|
||||
|
||||
// Private parameter properties on a private constructor need no explicit type:
|
||||
// the constructor signature is collapsed and the class member is type-erased.
|
||||
class InferredDefault {}
|
||||
export class PrivateConstructorWithPrivateParamPropertyInferredDefault {
|
||||
private constructor(
|
||||
private readonly a = new InferredDefault(),
|
||||
private b = new InferredDefault(),
|
||||
) {}
|
||||
}
|
||||
|
||||
// Non-private parameter properties on a private constructor still need a type,
|
||||
// because the class member is still emitted.
|
||||
export class PrivateConstructorWithPublicParamPropertyInferredDefault {
|
||||
private constructor(
|
||||
public readonly a = new InferredDefault(),
|
||||
) {}
|
||||
}
|
||||
|
||||
// The suppression must be limited to `private` constructors — a `protected`
|
||||
// constructor keeps its signature, so a private parameter property with an
|
||||
// uninferrable default still requires an explicit type.
|
||||
export class ProtectedConstructorWithPrivateParamPropertyInferredDefault {
|
||||
protected constructor(
|
||||
private readonly a = new InferredDefault(),
|
||||
) {}
|
||||
}
|
||||
@@ -128,6 +128,19 @@ export declare class AccessorPairWithProtectedPrivateSetter {
|
||||
protected get value(): number;
|
||||
private set value(value);
|
||||
}
|
||||
export declare class PrivateConstructorWithPrivateParamPropertyInferredDefault {
|
||||
private readonly a;
|
||||
private b;
|
||||
private constructor();
|
||||
}
|
||||
export declare class PrivateConstructorWithPublicParamPropertyInferredDefault {
|
||||
readonly a;
|
||||
private constructor();
|
||||
}
|
||||
export declare class ProtectedConstructorWithPrivateParamPropertyInferredDefault {
|
||||
private readonly a;
|
||||
protected constructor(a?);
|
||||
}
|
||||
|
||||
|
||||
==================== Errors ====================
|
||||
@@ -150,5 +163,23 @@ export declare class AccessorPairWithProtectedPrivateSetter {
|
||||
80 | public get badGetter() {
|
||||
`----
|
||||
|
||||
x TS9011: Parameter must have an explicit type annotation with
|
||||
| --isolatedDeclarations.
|
||||
,-[192:3]
|
||||
191 | private constructor(
|
||||
192 | public readonly a = new InferredDefault(),
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
193 | ) {}
|
||||
`----
|
||||
|
||||
x TS9011: Parameter must have an explicit type annotation with
|
||||
| --isolatedDeclarations.
|
||||
,-[201:3]
|
||||
200 | protected constructor(
|
||||
201 | private readonly a = new InferredDefault(),
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
202 | ) {}
|
||||
`----
|
||||
|
||||
|
||||
```
|
||||
Reference in new issue
Block a user