Skip to content

Commit de23541

Browse files
committed
feat(napi/transform): support enabling removeClassFieldsWithoutInitializer
1 parent 23c0ece commit de23541

3 files changed

Lines changed: 131 additions & 2 deletions

File tree

napi/transform/index.d.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,42 @@ export interface TypeScriptOptions {
427427
onlyRemoveTypeImports?: boolean
428428
allowNamespaces?: boolean
429429
allowDeclareFields?: boolean
430+
/**
431+
* When enabled, class fields without initializers are removed.
432+
*
433+
* For example:
434+
* ```ts
435+
* class Foo {
436+
* x: number;
437+
* y: number = 0;
438+
* }
439+
* ```
440+
* // transform into
441+
* ```js
442+
* class Foo {
443+
* x: number;
444+
* y: number = 0;
445+
* }
446+
* ```
447+
*
448+
* The option is used to align with the behavior of TypeScript's `useDefineForClassFields: false` option.
449+
* When you want to enable this, you also need to set [`crate::CompilerAssumptions::set_public_class_fields`]
450+
* to `true`. The `set_public_class_fields: true` + `remove_class_fields_without_initializer: true` is
451+
* equivalent to `useDefineForClassFields: false` in TypeScript.
452+
*
453+
* When `set_public_class_fields: true`, the above example transforms into:
454+
*
455+
* ```js
456+
* class Foo {
457+
* constructor() {
458+
* this.y = 0;
459+
* }
460+
* }
461+
* ```
462+
*
463+
* Defaults to `false`.
464+
*/
465+
removeClassFieldsWithoutInitializer?: boolean
430466
/**
431467
* Also generate a `.d.ts` declaration file for TypeScript files.
432468
*

napi/transform/src/transformer.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,40 @@ pub struct TypeScriptOptions {
220220
pub only_remove_type_imports: Option<bool>,
221221
pub allow_namespaces: Option<bool>,
222222
pub allow_declare_fields: Option<bool>,
223+
/// When enabled, class fields without initializers are removed.
224+
///
225+
/// For example:
226+
/// ```ts
227+
/// class Foo {
228+
/// x: number;
229+
/// y: number = 0;
230+
/// }
231+
/// ```
232+
/// // transform into
233+
/// ```js
234+
/// class Foo {
235+
/// x: number;
236+
/// y: number = 0;
237+
/// }
238+
/// ```
239+
///
240+
/// The option is used to align with the behavior of TypeScript's `useDefineForClassFields: false` option.
241+
/// When you want to enable this, you also need to set [`crate::CompilerAssumptions::set_public_class_fields`]
242+
/// to `true`. The `set_public_class_fields: true` + `remove_class_fields_without_initializer: true` is
243+
/// equivalent to `useDefineForClassFields: false` in TypeScript.
244+
///
245+
/// When `set_public_class_fields: true`, the above example transforms into:
246+
///
247+
/// ```js
248+
/// class Foo {
249+
/// constructor() {
250+
/// this.y = 0;
251+
/// }
252+
/// }
253+
/// ```
254+
///
255+
/// Defaults to `false`.
256+
pub remove_class_fields_without_initializer: Option<bool>,
223257
/// Also generate a `.d.ts` declaration file for TypeScript files.
224258
///
225259
/// The source file must be compliant with all
@@ -252,8 +286,9 @@ impl From<TypeScriptOptions> for oxc::transformer::TypeScriptOptions {
252286
allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces),
253287
allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields),
254288
optimize_const_enums: false,
255-
// TODO: Implement
256-
remove_class_fields_without_initializer: false,
289+
remove_class_fields_without_initializer: options
290+
.remove_class_fields_without_initializer
291+
.unwrap_or(ops.remove_class_fields_without_initializer),
257292
rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| {
258293
match value {
259294
Either::A(v) => {

napi/transform/test/transform.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,61 @@ describe('worker', () => {
340340
expect(code).toBe(0);
341341
});
342342
});
343+
344+
describe('typescript', () => {
345+
describe('options', () => {
346+
test('removeClassFieldsWithoutInitializer', () => {
347+
const code = `
348+
class Foo {
349+
a: number;
350+
b: number = 1;
351+
}
352+
`;
353+
const ret = transform('test.ts', code, {
354+
typescript: {
355+
removeClassFieldsWithoutInitializer: true,
356+
},
357+
});
358+
expect(ret.code).toMatchInlineSnapshot(`
359+
"class Foo {
360+
b = 1;
361+
}
362+
"
363+
`);
364+
});
365+
366+
test('align `useDefineForClassFields: false`', () => {
367+
const code = `
368+
class Foo {
369+
a: number;
370+
b: number = 1;
371+
@dec
372+
c: number;
373+
}
374+
`;
375+
const ret = transform('test.ts', code, {
376+
assumptions: {
377+
setPublicClassFields: true,
378+
},
379+
target: 'es2020',
380+
typescript: {
381+
removeClassFieldsWithoutInitializer: true,
382+
},
383+
decorator: {
384+
legacy: true,
385+
},
386+
});
387+
expect(ret.code).toMatchInlineSnapshot(`
388+
"import _decorate from "@oxc-project/runtime/helpers/decorate";
389+
class Foo {
390+
constructor() {
391+
this.b = 1;
392+
this.c = void 0;
393+
}
394+
}
395+
_decorate([dec], Foo.prototype, "c", void 0);
396+
"
397+
`);
398+
});
399+
});
400+
});

0 commit comments

Comments
 (0)