TypeScript の catch で受け取る変数は unknown 型です。そのため e.message と書くと TS18046 になり、instanceof Error などで型を絞り込んでから使います。 JavaScript では Error 以外の値(文字列や null)も throw できるので、「何が入っているかわからない」という型になっています。

この記事では、TS18046 の出力全文と、instanceof Error・自作の型ガード・toError 変換関数の3つの直し方を、7種類の値を throw した実行結果で比べます。あわせて、カスタムエラーと Error.cause、Error.isError、Promise でのエラーの型、判別可能ユニオンで作る Result 型のコードと、「例外を投げる/Result を返す」の使い分けを表にまとめました。

確認環境:Ubuntu 24.04 / TypeScript 7.0.2(一部の比較に 5.9.3)/ Node.js 22.22.2 と 24.21.0 / @types/node 22.20.5。TypeScript のファイルは node ファイル名.ts でそのまま実行しています。

catchのerrorの扱い方の早見表

やりたいこと 書き方 ポイント
message を読む if (e instanceof Error) { e.message } いちばん短い。Error 以外は else で扱う
message を持つオブジェクトも読む 自作の型ガード hasMessage(e) Error でないオブジェクトの throw にも対応
何が来ても Error にそろえる const err = toError(e) 文字列や null も Error に変換して先へ渡す
エラーの種類で分ける class NotFoundError extends Error と instanceof name と独自のプロパティを持たせる
元のエラーを残して包み直す new Error("…", { cause: e }) cause の型は unknown
別の実行環境の Error も見分ける Error.isError(e) Node.js 24 で動作。lib は esnext
エラーを戻り値で表す Result<T, E>(判別可能ユニオン) 起こりうるエラーが型に現れる
場面 エラーの値の型(tsc 7.0.2)
catch (e) unknown
promise.catch((e) => …) の e any
Promise.allSettled の reason any
error.cause unknown

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「TypeScript try-catchのerrorはunknown 型の扱い方」

e.messageでTS18046が出る理由とエラー全文

try {
  JSON.parse("{");
} catch (e) {
  console.log(e.message);
}

try {
  JSON.parse("{");
} catch (e: Error) {
  console.log(e.message);
}

Promise.reject(new Error("x")).catch((e) => {
  console.log(e.message);
});
$ npx tsc -p .
src/e1.ts(4,15): error TS18046: 'e' is of type 'unknown'.
src/e1.ts(9,13): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.

--locale ja を付けたときの TS1196 は次のとおりです。

src/e1.ts(9,13): error TS1196: Catch 句の変数型の注釈を指定する場合は、'any' または 'unknown' にする必要があります。
番号 意味 直し方
TS18046 e が unknown のままプロパティを読んだ 絞り込んでから読む(次の章)
TS1196 catch の変数に Error などの型を書いた 注釈は書かないか、unknown にする
  • catch の変数が unknown になるのは、strict に含まれる useUnknownInCatchVariables の働きです。npx tsc -p . --useUnknownInCatchVariables false で試すと、TS18046 は消えて TS1196 だけが残りました。このときの e は any です。
  • catch (e: Error) とは書けません。 throw される値を tsc は追えないので、書けるのは any か unknown だけです。
  • 13〜15行目の .catch((e) => …) はエラーになりません。 こちらの e は any だからです(あとの章で扱います)。

unknownを絞り込む3つの直し方を比べる

3つの直し方を関数にして、7種類の値を throw した場合の結果を比べます。

// 直し方1:instanceof Error
function byInstanceof(e: unknown): string {
  if (e instanceof Error) {
    return e.message;
  }
  return "(Error ではない)";
}

// 直し方2:自作の型ガード(message を持つオブジェクトなら読む)
function hasMessage(e: unknown): e is { message: string } {
  return typeof e === "object" && e !== null && "message" in e && typeof e.message === "string";
}
function byGuard(e: unknown): string {
  if (hasMessage(e)) {
    return e.message;
  }
  return "(message がない)";
}

// 直し方3:何が来ても Error に変換する
function toError(e: unknown): Error {
  if (e instanceof Error) return e;
  if (typeof e === "string") return new Error(e);
  if (hasMessage(e)) return new Error(e.message, { cause: e });
  return new Error(`Error 以外が throw されました: ${String(e)}`, { cause: e });
}
function byToError(e: unknown): string {
  return toError(e).message;
}

const cases: [string, () => never][] = [
  ["new Error", () => { throw new Error("在庫がありません"); }],
  ["TypeError", () => { throw new TypeError("数値ではありません"); }],
  ["文字列", () => { throw "在庫がありません"; }],
  ["オブジェクト", () => { throw { code: "E001", message: "在庫がありません" }; }],
  ["null", () => { throw null; }],
  ["undefined", () => { throw undefined; }],
  ["数値", () => { throw 404; }],
];

console.log(`Node.js ${process.version}`);
for (const [label, run] of cases) {
  try {
    run();
  } catch (e) {
    console.log(`■ ${label}`);
    console.log(`  1.instanceof: ${byInstanceof(e)}`);
    console.log(`  2.型ガード  : ${byGuard(e)}`);
    console.log(`  3.toError   : ${byToError(e)}`);
  }
}
$ node src/compare.ts
Node.js v22.22.2
■ new Error
  1.instanceof: 在庫がありません
  2.型ガード  : 在庫がありません
  3.toError   : 在庫がありません
■ TypeError
  1.instanceof: 数値ではありません
  2.型ガード  : 数値ではありません
  3.toError   : 数値ではありません
■ 文字列
  1.instanceof: (Error ではない)
  2.型ガード  : (message がない)
  3.toError   : 在庫がありません
■ オブジェクト
  1.instanceof: (Error ではない)
  2.型ガード  : 在庫がありません
  3.toError   : 在庫がありません
■ null
  1.instanceof: (Error ではない)
  2.型ガード  : (message がない)
  3.toError   : Error 以外が throw されました: null
■ undefined
  1.instanceof: (Error ではない)
  2.型ガード  : (message がない)
  3.toError   : Error 以外が throw されました: undefined
■ 数値
  1.instanceof: (Error ではない)
  2.型ガード  : (message がない)
  3.toError   : Error 以外が throw されました: 404

Node.js 24.21.0 でも、1行目のバージョン表示以外は同じ出力でした。

throw した値 1. instanceof Error 2. 型ガード 3. toError
Error、TypeError message を読める message を読める message を読める
文字列 読めない(else 側) 読めない(else 側) 文字列が message になる
message を持つオブジェクト 読めない(else 側) message を読める message を読める
null、undefined、数値 読めない(else 側) 読めない(else 側) 何が来たかを message に入れる
直し方 向いている場面
1. instanceof Error 自分のコードと標準の機能だけが throw する範囲。まずはこれで十分
2. 型ガード Error を継承していないオブジェクトを throw するライブラリと組み合わせるとき
3. toError ログ出力や画面表示など、「必ず Error として扱いたい」場所の手前

toError は、元の値を cause に入れて残している点がポイントです。変換後も、もとが何だったかを調べられます。なお、型ガードの e is { message: string } は、関数の中身が正しいことを前提に tsc が信じる書き方なので、判定の式は丁寧に書きます。

カスタムエラーとError.causeで種類と原因を表す

エラーの種類で処理を分けたいときは、Error を継承したクラスを作り、instanceof で絞り込みます。

class AppError extends Error {
  override name = "AppError";
  readonly code: string;
  constructor(message: string, code: string, options?: ErrorOptions) {
    super(message, options);
    this.code = code;
  }
}

class NotFoundError extends AppError {
  override name = "NotFoundError";
  readonly resource: string;
  constructor(resource: string, options?: ErrorOptions) {
    super(`${resource} が見つかりません`, "NOT_FOUND", options);
    this.resource = resource;
  }
}

function loadConfig(path: string): string {
  try {
    JSON.parse("{ broken");
    return path;
  } catch (e) {
    // 元のエラーを cause に入れて、意味のわかるエラーに包み直す
    throw new NotFoundError(`設定ファイル ${path}`, { cause: e });
  }
}

try {
  loadConfig("app.json");
} catch (e) {
  console.log("instanceof Error        :", e instanceof Error);
  console.log("instanceof AppError     :", e instanceof AppError);
  console.log("instanceof NotFoundError:", e instanceof NotFoundError);
  if (e instanceof NotFoundError) {
    console.log("name    :", e.name);
    console.log("message :", e.message);
    console.log("code    :", e.code);
    console.log("resource:", e.resource);
    console.log("cause   :", e.cause instanceof Error ? `${e.cause.name}: ${e.cause.message}` : e.cause);
  }
  console.log(String(e));
}
$ node src/custom.ts
instanceof Error        : true
instanceof AppError     : true
instanceof NotFoundError: true
name    : NotFoundError
message : 設定ファイル app.json が見つかりません
code    : NOT_FOUND
resource: 設定ファイル app.json
cause   : SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)
NotFoundError: 設定ファイル app.json が見つかりません

Node.js 22.22.2 と 24.21.0 で同じ出力でした。

  • override name = "…" を書いておくと、String(e) やログの先頭がクラス名になります。書かないと、継承していても Error: … と表示されます。
  • instanceof で絞ると、code や resource といった独自のプロパティが型つきで読めます。
  • e.cause の型は unknown なので、cause を読むときも instanceof で絞ります。
  • 2番目の引数の型 ErrorOptions は、tsconfig.json の lib が es2022 以降のときに使えます。lib を es2021 にすると、次のエラーになりました。
es21/a.ts(1,32): error TS2554: Expected 0-1 arguments, but got 2.
es21/a.ts(2,15): error TS2550: Property 'cause' does not exist on type 'Error'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2022' or later.

コンストラクタの引数に readonly code: string と書く省略記法(パラメータプロパティ)は、Node.js で .ts をそのまま実行すると ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX で止まったため、上のコードではプロパティを別に宣言しています。

targetがES5のときinstanceofがfalseになる件

「Error を継承したクラスで instanceof が false になる」という話は、出力先(target)が ES5 のときに起きます。次のコードを TypeScript 5.9.3 で target を変えてコンパイルし、Node.js 22.22.2 で実行しました。

class AppError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "AppError";
  }
  describe() { return "AppError: " + this.message; }
}

class FixedError extends Error {
  constructor(message: string) {
    super(message);
    Object.setPrototypeOf(this, new.target.prototype); // プロトタイプを付け直す
    this.name = "FixedError";
  }
  describe() { return "FixedError: " + this.message; }
}

for (const E of [AppError, FixedError]) {
  try {
    throw new E("在庫がありません");
  } catch (e) {
    console.log(E === AppError ? "AppError" : "FixedError");
    console.log("  instanceof Error:", e instanceof Error);
    console.log("  instanceof 自分 :", e instanceof E);
    console.log("  describe の型   :", typeof (e as any).describe);
  }
}
== TS 5.9.3 --target es5
AppError
  instanceof Error: true
  instanceof 自分 : false
  describe の型   : undefined
FixedError
  instanceof Error: true
  instanceof 自分 : true
  describe の型   : function
== TS 5.9.3 --target es2015
AppError
  instanceof Error: true
  instanceof 自分 : true
  describe の型   : function
FixedError
  instanceof Error: true
  instanceof 自分 : true
  describe の型   : function
コンパイラと target e instanceof AppError メソッド describe
TypeScript 5.9.3、es5 false 呼べない(undefined)
TypeScript 5.9.3、es5、setPrototypeOf あり true 呼べる
TypeScript 5.9.3、es2015 / es2022 true 呼べる
TypeScript 7.0.2、es2015 true 呼べる
TypeScript 7.0.2、es5 コンパイルできない(TS5108) ―

target が es2015 以降なら、対策なしで instanceof は true です。TypeScript 7.0.2 では target の es5 そのものが指定できず、次のエラーになりました。

error TS5108: Option 'target=ES5' has been removed. Please remove it from your configuration.

以前の版で target を es5 にしている場合だけ、コンストラクタに Object.setPrototypeOf(this, new.target.prototype) を入れます。

Error.isErrorで別の実行環境のErrorも見分ける

instanceof は「同じ実行環境の Error から作られたか」を見るため、node:vm で作った別のコンテキストのように、別の環境で作られた Error には false を返します。Error.isError は、作られた場所に関係なく Error かどうかを判定します。

import vm from "node:vm";

// 別の実行環境(別の Realm)で作られた Error
const foreign: unknown = vm.runInNewContext("new Error('別の環境のエラー')");

console.log("instanceof Error:", foreign instanceof Error);
console.log("Error.isError   :", Error.isError(foreign));
console.log("Error.isError({ message: 'x' }):", Error.isError({ message: "x" }));

if (Error.isError(foreign)) {
  console.log(foreign.message);
}
$ node src/iserror.ts    (Node.js 24.21.0)
instanceof Error: false
Error.isError   : true
Error.isError({ message: 'x' }): false
別の環境のエラー

Error.isError(foreign) の if の中では、foreign が Error に絞り込まれ、message を読めています。使うときの条件は2つあります。

確認したこと 結果
tsconfig.json の lib が es2022 TS2550: Property 'isError' does not exist on type 'ErrorConstructor'. …Try changing the 'lib' compiler option to 'esnext' or later.
lib を esnext に変更 tsc が通る
Node.js 22.22.2 で実行 TypeError: Error.isError is not a function
Node.js 24.21.0 で実行 上の出力のとおり動く

同じ環境の中だけで完結するコードなら、instanceof Error で足ります。

Promiseのrejectとawaitでのエラーの型

非同期の処理では、書き方によってエラーの値の型が変わります。

書き方 エラーの値の型 対応
try { await p } catch (e) { } unknown 同期のときと同じく絞り込む
p.catch((e) => { }) any (e: unknown) と自分で書く
Promise.allSettled の r.reason any const reason: unknown = r.reason で受け直す

any のままだと、e.mesage のようなつづりの間違いも tsc を通ります。unknown と書いておくと、同期の catch と同じ絞り込みがそのまま使えます。

async function fetchStock(id: number): Promise<number> {
  if (id < 0) throw new RangeError("id は 0 以上で指定します");
  if (id === 0) return Promise.reject("not found"); // 文字列で reject する例
  return 10;
}

// 1. await + try...catch:e は unknown
try {
  await fetchStock(-1);
} catch (e) {
  console.log("1:", e instanceof RangeError ? e.message : e);
}

// 2. .catch():コールバックの引数は any(自分で unknown と書く)
await fetchStock(0).catch((e: unknown) => {
  console.log("2:", e instanceof Error ? e.message : `Error 以外: ${String(e)}`);
});

// 3. Promise.allSettled:reason は any
const results = await Promise.allSettled([fetchStock(1), fetchStock(-1), fetchStock(0)]);
for (const r of results) {
  if (r.status === "fulfilled") {
    console.log("3: 成功", r.value);
  } else {
    const reason: unknown = r.reason;
    console.log("3: エラー", reason instanceof Error ? reason.message : reason);
  }
}

// 4. await を付けないと try...catch では受け取れない
function withoutAwait(): Promise<number> {
  try {
    return fetchStock(-1);
  } catch (e) {
    console.log("4: ここには来ない");
    return Promise.resolve(0);
  }
}
async function withAwait(): Promise<number> {
  try {
    return await fetchStock(-1);
  } catch (e) {
    console.log("4: await ありなら catch に来る");
    return 0;
  }
}
await withoutAwait().catch(() => console.log("4: await なしは呼び出し元に届く"));
await withAwait();
$ node src/async.ts
1: id は 0 以上で指定します
2: Error 以外: not found
3: 成功 10
3: エラー id は 0 以上で指定します
3: エラー not found
4: await なしは呼び出し元に届く
4: await ありなら catch に来る
  • reject の理由にも、Error 以外の値を渡せます。 2 と 3 の not found は文字列です。自分で reject するときは new Error(…) を渡すと、受け取る側が instanceof で扱えます。
  • 4 のように、try の中で Promise を await せずに return すると、catch には来ません。 拒否された Promise がそのまま呼び出し元へ渡ります。try...catch で受けたいときは return await と書きます。

判別可能ユニオンで作るResult型

「在庫が足りない」「数量が正しくない」のように、起こることがあらかじめわかっているエラーは、戻り値の型で表す方法があります。成功と、そうでない場合を ok というプロパティで見分ける形(判別可能ユニオン)にします。

export type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

export const ok = <T>(value: T): Result<T, never> => ({ ok: true, value });
export const err = <E>(error: E): Result<never, E> => ({ ok: false, error });

/** 例外を投げる関数を Result に変える */
export function tryCatch<T>(fn: () => T): Result<T> {
  try {
    return ok(fn());
  } catch (e) {
    return err(e instanceof Error ? e : new Error(String(e), { cause: e }));
  }
}

/** Promise を Result に変える */
export async function tryAsync<T>(promise: Promise<T>): Promise<Result<T>> {
  try {
    return ok(await promise);
  } catch (e) {
    return err(e instanceof Error ? e : new Error(String(e), { cause: e }));
  }
}

// ---- 使う側 ----
type OrderError =
  | { kind: "out_of_stock"; item: string; stock: number }
  | { kind: "invalid_quantity"; quantity: number };

const stock: Record<string, number> = { pen: 3, note: 0 };

function order(item: string, quantity: number): Result<{ item: string; rest: number }, OrderError> {
  if (!Number.isInteger(quantity) || quantity <= 0) {
    return err({ kind: "invalid_quantity", quantity });
  }
  const current = stock[item] ?? 0;
  if (current < quantity) {
    return err({ kind: "out_of_stock", item, stock: current });
  }
  return ok({ item, rest: current - quantity });
}

function describe(e: OrderError): string {
  switch (e.kind) {
    case "out_of_stock":
      return `${e.item} は在庫が ${e.stock} 個です`;
    case "invalid_quantity":
      return `数量 ${e.quantity} は指定できません`;
    default: {
      const rest: never = e; // kind を増やして case を書き忘れると、ここが型エラーになる
      return rest;
    }
  }
}

for (const [item, quantity] of [["pen", 2], ["note", 1], ["pen", 0]] as const) {
  const r = order(item, quantity);
  if (r.ok) {
    console.log(`注文できました: ${r.value.item} 残り ${r.value.rest}`);
  } else {
    console.log(`注文できません: ${describe(r.error)}`);
  }
}

const parsed = tryCatch(() => JSON.parse("{ broken") as unknown);
console.log(parsed.ok ? parsed.value : `tryCatch: ${parsed.error.name}`);

const waited = await tryAsync(Promise.reject("timeout"));
console.log(waited.ok ? waited.value : `tryAsync: ${waited.error.message}`);
$ node src/result.ts
注文できました: pen 残り 1
注文できません: note は在庫が 0 個です
注文できません: 数量 0 は指定できません
tryCatch: SyntaxError
tryAsync: timeout

Result の良さは、確認を忘れると tsc が止めてくれることです。ok を確かめずに value を読むと、次のエラーになります。

import { type Result, ok } from "./result.ts";
declare function order(): Result<{ rest: number }, { kind: "out_of_stock" }>;
const r = order();
console.log(r.value.rest);
src/result-err.ts(4,15): error TS2339: Property 'value' does not exist on type 'Result<{ rest: number; }, { kind: "out_of_stock"; }>'.
  Property 'value' does not exist on type '{ ok: false; error: { kind: "out_of_stock"; }; }'.
  • error の型を kind つきのユニオンにすると、switch で種類ごとに必要な情報(在庫数、指定された数量)を型つきで取り出せます。
  • describe の default にある never への代入は、kind を増やしたのに case を書き足していない場合に型エラーで知らせる仕組みです。
  • tryCatch と tryAsync は、例外を投げる既存の関数や Promise を Result に変える橋渡しです。JSON.parse の SyntaxError も、文字列での reject も、Error を持つ Result になりました。

例外を投げるかResultを返すかの使い分け

観点 例外を投げる(throw) Result を返す
エラーの型 catch で unknown。型には現れない 関数の戻り値の型に現れる
確認を忘れたとき 実行時に上位まで伝わる tsc が TS2339 で知らせる
途中の関数 何も書かなくても上へ伝わる 各段で受けて返す必要がある
向いているエラー 想定していない不具合、続行できない状態(設定の読み込みに必要なファイルがない、引数の使い方の誤り) 起こることがわかっていて、呼び出し側が対応を決めるもの(入力の検証、在庫が足りない、該当なし)
標準の機能・ライブラリとの相性 そのまま合う tryCatch などで変換して受ける
状況 選び方
呼び出し側が、その場で分岐して画面やレスポンスを変える Result
エラーの種類ごとに渡したい情報が違う Result(kind つきのユニオン)
どの層でも対処できず、いちばん外側でまとめて記録する 例外
標準の機能が投げる例外を受ける境目 try...catch で受け、toError か tryCatch で形をそろえる

「想定している出来事は Result、想定していない不具合は例外」と分けると、Result の error には利用者に伝える意味のある種類だけが並び、例外は調査が必要なものだけになります。両方を1つのコードの中で使い分けて構いません。

この記事の tsc の出力は、次の tsconfig.json で取りました。

{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "lib": ["esnext"],
    "types": ["node"],
    "strict": true,
    "rootDir": "src",
    "noEmit": true,
    "allowImportingTsExtensions": true
  },
  "include": ["src"]
}

"lib": ["esnext"] は Error.isError のためで、それ以外のコードは "es2022" で通ります。"allowImportingTsExtensions" は、import に .ts の拡張子を書くための設定です。

よくある質問

Q. catch (e: any) と書いてもよいですか? A. 書けますが、e.message のつづりを間違えても tsc が気づけなくなります。unknown のまま instanceof Error で絞るほうが、1行増えるだけで安全に読めます。

Q. (e as Error).message ではいけませんか? A. tsc は通ります。ただし文字列や null が throw された場合、as は何も調べないので、message は undefined になるか、null のプロパティを読んで TypeError になります。instanceof なら、Error でない場合の分岐も一緒に書けます。

Q. Result 型はライブラリを使うべきですか? A. この記事の Result、ok、err、tryCatch、tryAsync だけなら 30 行ほどなので、まずは自作で十分です。map などの変換をつなげて書きたくなったら、専用のライブラリを検討する段階です。

Q. カスタムエラーと Result はどちらを使えばよいですか? A. 併用できます。上位まで伝えて止めたいものはカスタムエラーを throw し、呼び出し側に分岐してほしいものは Result で返します。Result の error にカスタムエラーのインスタンスを入れる形も取れます。

まとめ

  • catch の変数は unknown。e.message をそのまま読むと TS18046、catch (e: Error) は TS1196 になる
  • 直し方は instanceof Error・型ガード・toError の3つ。文字列や null が throw されても扱えるのは toError
  • カスタムエラーは override name を書き、instanceof で絞ると独自のプロパティを型つきで読める
  • new Error("…", { cause: e }) で元のエラーを残す。cause の型も unknown
  • instanceof が false になるのは target が es5 のとき(TypeScript 5.9.3 で確認)。es2015 以降は true
  • .catch((e) => …) と allSettled の reason は any。自分で unknown と書いて同じ絞り込みを使う
  • 想定している出来事は Result、想定していない不具合は例外。Result は確認を忘れると tsc が知らせてくれる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「TypeScript try-catchのerrorはunknown 型の扱い方」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?