Skip to content

TypeScript

O transformador Oxc pode transformar TypeScript em JavaScript.

js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  typescript: {
    jsxPragma: "React.createElement",
    jsxPragmaFrag: "React.Fragment",
    onlyRemoveTypeImports: false,
    allowNamespaces: true,
    removeClassFieldsWithoutInitializer: false,
    rewriteImportExtensions: false,
    optimizeConstEnums: false,
    optimizeEnums: false,
  },
});

verbatimModuleSyntax

Por padrão, o TypeScript remove imports não usados com semântica diferente da especificação JavaScript. A opção verbatimModuleSyntax alinha com o comportamento JS.

Se você usa essa opção, defina typescript.onlyRemoveTypeImports: true.

js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  typescript: {
    onlyRemoveTypeImports: true,
  },
});

useDefineForClassFields

O TypeScript já teve semântica de campos de classe diferente da especificação. useDefineForClassFields alinha com a spec — fica ligado por padrão quando target no tsconfig é es2022 ou maior.

Se desligar, use typescript.removeClassFieldsWithoutInitializer e assumptions.setPublicClassFields: true.

js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  typescript: {
    removeClassFieldsWithoutInitializer: true,
  },
  assumptions: {
    setPublicClassFields: true,
  },
});

Decoradores

Suporte a decorators legados (“experimental decorators” no TypeScript).

Com experimentalDecorators: decorator.legacy: true.
Com emitDecoratorMetadata: decorator.emitDecoratorMetadata: true.

js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  decorator: {
    legacy: true,
    emitDecoratorMetadata: true,
  },
});

Metadados de decorator: tipos que exigem inferência caem em Object

Sem inferência de tipos completa, o Oxc usa Object quando não consegue calcular o tipo para metadados do decorator.

Exemplo:

ts
import { Something1 } from "./somewhere";

type Something2 = Exclude<string | number, string>;

export class Foo {
  @test
  foo(input1: Something1, input2: Something2) {}
}
js
// omit helper functions
import { Something1 } from "./somewhere";
var _ref;
export class Foo {
  foo(input1, input2) {}
}
_decorate(
  [
    test,
    _decorateMetadata("design:type", Function),
    _decorateMetadata("design:paramtypes", [
      typeof (_ref = typeof Something1 !== "undefined" && Something1) === "function"
        ? _ref
        : Object,
      Object, 
    ]),
    _decorateMetadata("design:returntype", void 0),
  ],
  Foo.prototype,
  "foo",
  null,
);
js
// omit helper functions
var _a;
import { Something1 } from "./somewhere";
export class Foo {
  foo(input1, input2) {}
}
__decorate(
  [
    test,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [
      typeof (_a = typeof Something1 !== "undefined" && Something1) === "function" ? _a : Object,
      Number, 
    ]),
    __metadata("design:returntype", void 0),
  ],
  Foo.prototype,
  "foo",
  null,
);

Alinhado ao TypeScript com tipo externo.

Você pode fixar tipos com Reflect.metadata:

ts
import { Something1 } from "./somewhere";

type Something2 = Exclude<string | number, string>;

export class Foo {
  @test
  @Reflect.metadata("design:paramtypes", [Something1, Number])
  foo(input1: Something1, input2: Something2) {}
}
js
// omit helper functions
import { Something1 } from "./somewhere";
var _ref;
export class Foo {
  foo(input1, input2) {}
}
_decorate(
  [
    test,
    Reflect.metadata("design:paramtypes", [Something1, Number]),
    _decorateMetadata("design:type", Function),
    _decorateMetadata("design:paramtypes", [
      typeof (_ref = typeof Something1 !== "undefined" && Something1) === "function"
        ? _ref
        : Object,
      Object,
    ]),
    _decorateMetadata("design:returntype", void 0),
  ],
  Foo.prototype,
  "foo",
  null,
);

TSX

TSX também é suportado. Veja transformação JSX.

Reescrever extensões de import

Com rewriteRelativeImportExtensions no tsconfig, use typescript.rewriteImportExtensions:

  • "rewrite" ou true: .ts/.tsx.js, .mts.mjs, .cts.cjs.
  • "remove": remove extensões .ts/.tsx/.mts/.cts.
  • false (padrão): sem mudanças.
js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  typescript: {
    rewriteImportExtensions: "rewrite", // ou "remove", true, false
  },
});

Otimizar enums

  • optimizeConstEnums: inlined valores de const enum e remove a declaração.
  • optimizeEnums: inlined acessos a membros de enum comum quando todos são estaticamente conhecidos; enums não exportados também podem ser removidos se não há uso como valor de runtime.
js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  typescript: {
    optimizeConstEnums: true,
    optimizeEnums: true,
  },
});

Declaração (.d.ts)

Gerar .d.ts junto à saída. O arquivo precisa obedecer a isolatedDeclarations.

js
import { transform } from "oxc-transform";

const result = await transform("lib.ts", sourceCode, {
  typescript: {
    declaration: {
      stripInternal: false,
    },
  },
});

console.log(result.declaration); // conteúdo .d.ts
console.log(result.declarationMap); // declaration source map (se sourcemap ligado)

Ressalvas

Módulos isolados

Cada arquivo é transformado só; alguns recursos TypeScript não existem aqui. Habilite isolatedModules no tsconfig.json.

Namespaces só parciais

Namespaces são legado; prefira ES modules para projetos novos. O transformador Oferece suporte parcial.

Exportar com var ou let não é suportado

ts
namespace Foo {
  export let bar = 1; 
}
console.log(Foo.bar);

Workaround: const. Se precisar de mutação, use objeto com campo interno mutável:

ts
namespace Foo {
  export const bar = { value: 1 }; 
}
console.log(Foo.bar.value);

Namespaces com o mesmo nome não compartilham escopo entre blocos namespace Foo

ts
namespace Foo {
  export const bar = 1;
}
namespace Foo {
  export const baz = bar;
}
js
let foo;
(function (_Foo) {
  const bar = (_Foo.bar = 1);
})(Foo || (Foo = {}));
(function (_Foo2) {
  const baz = (_Foo2.baz = bar); 
})(Foo || (Foo = {}));
js
var Foo;
(function (Foo) {
  Foo.bar = 1;
})(Foo || (Foo = {}));
(function (Foo) {
  Foo.baz = Foo.bar; 
})(Foo || (Foo = {}));

No segundo bloco, o compilador TypeScript resolve bar do primeiro namespace; no Oxc, não.

Workaround — referência explícita pelo objeto namespace:

ts
namespace Foo {
  export const bar = 1;
}
namespace Foo {
  export const baz = Foo.bar; 
}