Vitest Behavior Differs From Compiled JavaScript

40 Views Asked by At

Problem

I'm writing a package that relies on the fact that decorators will initialize a class object property name.

// index.ts

const Property = (_target: any, key: any) => {
}


class Student {
  @Property
  name!: string;
  

  age!: number;
}

console.log(Object.getOwnPropertyNames(new Student()) // Outputs ["name"]

I've confirmed that this behavior works by compiling the code to JavaScript. However, when I'm writing tests using Vitest, Object.getOwnPropertyNames(new Student()) returns an empty array.

// index.spec.ts

describe("Test decorator", () => {
  class Student {
    @Property
    name!: string;
  }

  it("Decorator initalize value", () => {
    expect(Object.getOwnPropertyNames(student)).toEqual(
      expect.arrayContaining(["name"]) // Fails
    );
  });
})

Environment

  • node: v20.5.1
  • typescript: v5.3.3
# package.json

{
  "devDependencies": {
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3",
    "vitest": "^1.2.0"
  }
}
# tsconfig.json
{
  "compilerOptions": {
    /* Language and Environment */
    "target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,

    /* Modules */
    "module": "commonjs" /* Specify what module code is generated. */,

    /* Emit */
    "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
    "outDir": "./dist" /* Specify an output folder for all emitted files. */,

    /* Interop Constraints */
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

    /* Type Checking */
    "strict": true /* Enable all strict type-checking options. */,

    /* Completeness */
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  },
  "include": ["src/**/*"],
  "exclude": ["src/**/*.spec.ts"]
}

What I've Tried

My initial thought was that tsconfig.json is not properly initialized for Vitest. I tried manually setting the path in vitest.config.ts, but it didn't resolve the problem.

import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    typecheck: {
      tsconfig: "./tsconfig.json",
    },
  },
});

I then tried scanning through their documentations and searching online, but haven't found any relevant resources. I'm not sure where else to go from here. I would appreciate any help or suggestions!

1

There are 1 best solutions below

0
Nick Doan On

I figured out a workaround using Jest. I had to install quite a lot Babel plugin in order to make it work.

module.exports = {
  presets: [
    ["@babel/preset-env", { targets: { node: "current" } }],
    "@babel/preset-typescript",
  ],
  plugins: [
    ["@babel/plugin-proposal-decorators", { version: "2023-05" }],
    ["@babel/plugin-transform-flow-strip-types"],
    ["@babel/plugin-transform-class-properties"],
    ["@babel/plugin-transform-class-static-block"],
  ],
};

I also came across this post, which I believe explain why decorators do not work with Vitest.