Skip to content

TypeScript

Трансформер Oxc поддерживает преобразование TypeScript в 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

По умолчанию TypeScript удаляет неиспользуемые импорты иначе, чем предписывает спецификация JavaScript. Опция verbatimModuleSyntax выравнивает поведение со спецификацией JS.

Если она включена, задайте typescript.onlyRemoveTypeImports: true.

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

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

useDefineForClassFields

Раньше у TypeScript была другая семантика полей класса, чем в спецификации JavaScript. Опция useDefineForClassFields выравнивает поведение. По умолчанию она включается, если target в tsconfig — es2022 или выше.

Если вы её отключаете, задайте typescript.removeClassFieldsWithoutInitializer и assumptions.setPublicClassFields: true.

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

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

Декораторы

Трансформер Oxc поддерживает legacy-декораторы — в TypeScript они называются experimental decorators.

При experimentalDecorators в tsconfig используйте decorator.legacy. При emitDecoratorMetadatadecorator.emitDecoratorMetadata.

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

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

Метаданные декораторов: типы, требующие вывода типов, станут Object

Из-за неполной поддержки вывода типов трансформер подставит тип Object, если не может вычислить тип для метаданных декоратора.

Например, следующий код преобразуется так:

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,
);

Это согласуется с поведением компилятора TypeScript для внешнего типа.

Явно задать типы можно через 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. См. преобразование JSX.

Переписывание расширений импорта

Если в tsconfig включён rewriteRelativeImportExtensions, используйте typescript.rewriteImportExtensions.

  • "rewrite" или true: .ts и .tsx.js, .mts.mjs, .cts.cjs.
  • "remove": расширения .ts/.tsx/.mts/.cts полностью убираются.
  • false (по умолчанию): без изменений.
js
import { transform } from "oxc-transform";

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

Оптимизация enum

Трансформер может инлайнить значения членов enum в местах использования.

  • optimizeConstEnums: инлайнит значения const enum и удаляет объявление.
  • optimizeEnums: инлайнит обращения к обычным (не const) enum, если все члены удовлетворяют ограничениям const enum (значения статически вычислимы). Неэкспортируемые enum удаляются, если все члены вычислимы и на enum как на значение времени выполнения нет ссылок.
js
import { transform } from "oxc-transform";

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

Декларации (declaration)

Генерация файлов .d.ts вместе с результатом преобразования. Исходный файл должен удовлетворять всем требованиям isolatedDeclarations.

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

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

console.log(result.declaration); // the .d.ts content
console.log(result.declarationMap); // the declaration source map (if sourcemap is enabled)

Ограничения

Изолированные модули

Трансформер обрабатывает каждый файл отдельно, поэтому часть возможностей TypeScript недоступна. Чтобы их избегать, включите isolatedModules в tsconfig.json.

Частичная поддержка namespace

В TypeScript есть устаревшие namespaces. Для новых проектов рекомендуют ES-модули; в Oxc поддержка namespace частичная.

Экспорт переменной через var или let не поддерживается

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

Обходной путь — const. Если нужна мутабельность, используйте объект с внутренней мутабельностью:

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

Пространства имён с одинаковым именем не делят одну область видимости

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 = {}));

Здесь во втором namespace ссылка bar в выводе компилятора TypeScript указывает на bar из первого namespace, а в выводе Oxc — нет.

Явно обращайтесь через объект namespace:

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