swc+TypeORMが動かなかったのを直したメモ

仕事でJestを使っているのですが、テストに合計5分以上かかるようになってきました。

そこでテストの時間を短縮するために「swc」というRust製のトランスパイラを使うことにしました。

ただ、swcとTypeORM(というか中で使われているデコレータ周り)の相性が悪いらしく、普通に導入しただけでは動きませんでした。

 

そこで色々四苦八苦した結果、「これをやったら動くようになったよ」というのをメモしておきたいと思います。

  • バージョンメモ
    ├── @swc/jest@0.2.24
    ├── ts-node@10.9.1
    ├── typeorm@0.2.45
    ├── typescript@4.8.2
    └── webpack@5.75.0
ここから先は、多分間違ったことを書いてます。

なぜなら、私が完全に雰囲気で「たぶんこんな感じなのだろう、知らんけど🙄」という理解度で書いているからです。

なぜそのような理解度で記事を書いてるかというと、ネットをググっても同じような問題の解決策を書いている人がいなくて自分は苦労したので、だったら「自分レベルの理解度の記事でも誰かの役に立つのでは?」と思ったからです。

ですので、ここから先はあくまで雰囲気をつかむためだけに見てください😑
(あと間違ってる箇所が分かる方はコメントで「これはこうだよ」というのを教えていただけると大変うれしいです)

entityの中で独自の型を使う場合は、@Column({ type: “varchar” })みたいにカラムのtypeを指定するようにする

結論から書くと、以下ではダメで、

❌Bad

type Gender = "MAN" | "WOMAN";

@Entity()
export class User {
  @Column({ unique: true, primary: true })
  readonly id: string;

  @Column()
  name: string;

  @Column()
  age: string;

  @Column() 👈👈👈👈👈👈👈ここがダメ
  gender: Gender;
  
  @CreateDateColumn()
  readonly createdAt: Date;
}

 

以下のように書くと、1つのエラーを解消できました。

✅Good

type Gender = "MAN" | "WOMAN";

@Entity()
export class User {
  @Column({ unique: true, primary: true })
  readonly id: string;

  @Column()
  name: string;

  @Column()
  age: string;

  @Column({ type: "varchar" }) 👈👈👈👈👈👈こうすると良い
  gender: Gender;
  
  @CreateDateColumn()
  readonly createdAt: Date;
}

よくわかっていないのですが、Entityの中でプリミティブ型じゃない独自の型を使用していると、型をうまく解決できずに全部undefinedになるらしいです。

(これはswc、reflect-metadata、TypeORMというパッケージいずれかの問題ぽいですがよく分かってません。。)

 

結果、TypeORMがundefinedを許してくれないのでエラーになるらしいので

undefinedになるかもしれないカラムに、こういうデータ型でカラムを作ってくれという指示をしておくと、undefinedでも通るようになる・・・みたいな感じぽいです。

②リレーションの()=>エンティティ名, (エンティティ)=>エンティティ.カラム名 みたいな書き方をやめて、文字列を直接書くようにする

結論から書くと、以下ではダメで、

❌Bad

type Gender = "MAN" | "WOMAN";

@Entity()
export class User {
  @Column({ unique: true, primary: true })
  readonly id: string;

  @Column()
  name: string;

  @Column()
  age: string;

  @Column({ type: "varchar" })
  gender: Gender;

  @CreateDateColumn()
  readonly createdAt: Date;

  @OneToMany(() => Post, (Post) => Post.createdBy) 👈👈👈👈👈ここがダメ
  posts: Post[];
}

@Entity()
export class Post {
  @Column({ unique: true, primary: true })
  readonly id: string;

  @Column()
  title: string;

  @Column()
  content: string;

  @CreateDateColumn()
  readonly createdAt: Date;

  @ManyToOne(() => User, (User) => User.posts) 👈👈👈👈👈ここがダメ
  createdBy: User;
}

 

以下のように書くと、1つのエラーを解消できました。

✅Good

type Gender = "MAN" | "WOMAN";

@Entity()
export class User {
  @Column({ unique: true, primary: true })
  readonly id: string;

  @Column()
  name: string;

  @Column()
  age: string;

  @Column({ type: "varchar" })
  gender: Gender;

  @CreateDateColumn()
  readonly createdAt: Date;

  @OneToMany("Post", "createdBy") 👈👈👈👈👈こうすると良いらしい
  posts: Post[];
}

@Entity()
export class Post {
  @Column({ unique: true, primary: true })
  readonly id: string;

  @Column()
  title: string;

  @Column()
  content: string;

  @CreateDateColumn()
  readonly createdAt: Date;

  @ManyToOne("User", "posts") 👈👈👈👈👈👈こうすると良いらしい
  createdBy: User;
}

よくわかっていないのですが、swcはファイルを1つずつトランスパイルをしていく(?)ので、循環参照が起こる書き方をしていると「循環参照してんぞ」なエラー(before initializeみたいなエラー)が出ちゃうらしい。

なので循環参照しないような書き方でリレーションを書くようにします。

そのための書き方が用意されてるぽかったので、これで書きなおしたらエラーが出なくなりました↓。
https://github.com/typeorm/typeorm/issues/4190

 

 

そんな感じでした。

 

おわり

Node.js
スポンサーリンク
この記事を書いた人
penpen

1991生まれ。WEBエンジニア。

技術スタック:TypeScript/Next.js/Express/Docker/AWS

フォローする
フォローする

コメント

タイトルとURLをコピーしました