TypeScriptのasync関数の戻り値の型は必ず Promise<T> で、await を付けるとその中身の T が取り出されます。Promise型から中身の型だけを取り出したいときは Awaited<T> を使います。 覚えることはこの3つが中心ですが、Promise.all や allSettled を組み合わせると「結局この変数は何型なのか」がわかりにくくなります。

この記事は、「書いたコード」と「推論される型」を並べた早見表から始めます。そのあとで、await を付け忘れたときのエラー、forEach に async 関数を渡したときの実行順序、catch の変数が unknown になる理由を再現し、それぞれに修正コードを付けました。最後に、fetch の結果を as T で決めつけず、型ガードで実行時に確かめる最小コードを紹介します。型は TypeScript 7.0.2 が実際に推論した結果、出力は Node.js 22.22.2 で動かした結果です。エラー文はファイル名と行番号の表示だけ省いています。

async/awaitの型の早見表

前提として、次の3つの関数があるとします。

type User = { id: number; name: string };
declare function fetchUser(id: number): Promise<User>;
declare function fetchCount(): Promise<number>;
declare function fetchTitle(): Promise<string>;

async関数の戻り値の型

書いたコード 推論される戻り値の型
async function f() { return 1; } Promise<number>
async function f() { }(return なし) Promise<void>
async function f() { return fetchUser(1); } Promise<User>(Promise は二重にならない)
async function f(flag: boolean) { if (flag) return "a"; return 1; } Promise<"a" | 1>
const f = async (id: number) => ({ id, ok: true }); Promise<{ id: number; ok: boolean }>

await した結果と Promise のメソッドの型

書いたコード 変数の型
const u = await fetchUser(1); User
const u = fetchUser(1);(await なし) Promise<User>
await Promise.all([fetchUser(1), fetchCount(), fetchTitle()]) [User, number, string](順番どおりのタプル)
await Promise.all(ids.map((id) => fetchUser(id))) User[]
await Promise.all([fetchUser(1), 5, "x" as const]) [User, number, "x"](Promise でない値はそのまま)
await Promise.allSettled([fetchUser(1), fetchCount()]) [PromiseSettledResult<User>, PromiseSettledResult<number>]
await Promise.race([fetchUser(1), fetchCount()]) number | User
await Promise.any([fetchUser(1), fetchCount()]) number | User
fetchUser(1).then((u) => u.name) Promise<string>
fetchUser(1).catch(() => null) Promise<User | null>
await res.json()(fetch の Response) any

Awaited で取り出した型

書いたコード 結果の型
Awaited<Promise<string>> string
Awaited<Promise<Promise<number>>> number(入れ子も最後までほどく)
Awaited<string | Promise<number>> string | number
ReturnType<typeof fetchUser> Promise<User>
Awaited<ReturnType<typeof fetchUser>> User

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

(記載例)出典:株式会社RJC「TypeScript async/awaitの型|Promise・Awaited」

確認した環境とコマンド

項目 内容
TypeScript 7.0.2(npm で作業フォルダにインストール)
Node.js 22.22.2(.ts ファイルをそのまま実行)
型チェックのコマンド npx tsc --noEmit --strict --target es2022 --module nodenext ファイル名.ts
package.json {"type": "module"}(ファイルの一番外側で await を使うため)

早見表の型は、tsc に型定義ファイル(.d.ts)を出力させる方法と、わざと合わない型に代入して出たエラー文を読む方法で確かめました。

package.json の "type": "module" を書かずに、ファイルの一番外側で await を使うと、次のエラーになりました。サンプルを試すときは先に用意しておくと進めやすくなります。

error TS1309: The current file is a CommonJS module and cannot use 'await' at the top level.

async関数の戻り値の型を書く

型注釈は Promise で包んだ形で書く

async 関数の戻り値に型注釈を付けるときは、中身の型ではなく Promise<中身の型> を書きます。

async function c(): User {
  return fetchUser(1);
}
error TS1064: The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<User>'?

メッセージが直し方まで教えてくれます。async function c(): Promise<User> にすれば通ります。

async を付けない関数で Promise を返すとき

async を付けずに戻り値を Promise<User> と注釈した関数は、自分で Promise を返す必要があります。

function f(): Promise<User> {
  return { id: 1, name: "a" };
}
error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Promise<User>'.

async を付ければ、return した値が自動で Promise に包まれます。 付けないなら return Promise.resolve({ id: 1, name: "a" }) のように自分で包みます。

戻り値の型は書いたほうがよいか

推論に任せても正しい型になりますが、次の表を目安にすると決めやすくなります。

関数の種類 型注釈 理由
ほかのファイルから呼ばれる関数 書く 中の実装を変えたときに、戻り値の型が意図せず変わったことに気づける
複数の return がある関数 書く 早見表の Promise<"a" | 1> のように、推論結果が思ったより広がることがある
その場で使う短いコールバック 省略してよい 推論結果がそのまま読み取れる
何も返さない関数 Promise<void> と書く 「結果は使わない」という意図が読み手に伝わる

return で Promise を返しても二重にならない

async 関数の中で Promise をそのまま return しても、戻り値は Promise<Promise<User>> にはならず Promise<User> です(早見表の3行目)。async 関数が返す Promise は、return された Promise の結果を引き継ぐためです。

awaitを付け忘れたときのエラーと直し方

await を忘れると、変数には中身ではなく Promise そのものが入ります。TypeScript はこれをいくつかの形で知らせてくれます。

declare function isAdmin(id: number): Promise<boolean>;

async function a() {
  const user = fetchUser(1);
  console.log(user.name);             // (1)
  const n: User = fetchUser(1);       // (2)
  if (isAdmin(1)) { console.log("admin"); }   // (3)
}
(1) error TS2339: Property 'name' does not exist on type 'Promise<User>'.
(2) error TS2739: Type 'Promise<User>' is missing the following properties from type 'User': id, name
(3) error TS2801: This condition will always return true since this 'Promise<boolean>' is always defined.

(番号は対応がわかるように付けたものです。)エラー文の中に Promise<...> が出てきたら、まず await の付け忘れを確かめるのが近道です。

エラーコード 状況 直し方
TS2339 Promise のままプロパティを読んだ const user = await fetchUser(1);
TS2739 / TS2322 Promise を中身の型の変数に入れた 右辺に await を付ける
TS2801 Promise を if の条件に書いた if (await isAdmin(1))

(3) は特に見つけにくい例です。Promise はオブジェクトなので、条件としては常に真になります。型チェックがなければ「全員が管理者として扱われる」動きになってしまうところを、コンパイル時に止めてくれます。

await が書けない場所(TS1308)

function b() {
  const user = await fetchUser(1);
}
error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.

await を書けるのは、async を付けた関数の中か、モジュールの一番外側です。関数に async を付けると通ります。そのとき関数の戻り値は Promise に変わるので、呼び出す側にも await が必要になります。

map に async 関数を渡すと Promise の配列になる

async function g(ids: number[]) {
  const users: User[] = ids.map(async (id) => await fetchUser(id));
}
error TS2322: Type 'Promise<User>[]' is not assignable to type 'User[]'.
  Type 'Promise<User>' is missing the following properties from type 'User': id, name

コールバックの中で await していても、async 関数の戻り値は Promise なので、map の結果は Promise<User>[] です。配列全体を Promise.all に渡して待ちます。

const users: User[] = await Promise.all(ids.map((id) => fetchUser(id)));

Promise.all・allSettled・race・anyの型と使い分け

4つのメソッドは「複数の Promise をまとめて待つ」点は同じで、結果の形と、エラーが混ざったときの動きが違います。

メソッド await した結果の型 1つでもエラーになると 向いている場面
Promise.all 各結果のタプル(または配列) その時点でエラーとして抜ける 全部そろって初めて意味があるとき
Promise.allSettled PromiseSettledResult<T> のタプル(または配列) エラーにならず、1件ずつ成否がわかる 一部が取得できなくても続けたいとき
Promise.race 各結果のユニオン型 最初に決着したものがエラーなら抜ける タイムアウトを付けたいとき
Promise.any 各結果のユニオン型 全部がエラーのときだけ AggregateError 複数の取得先のうち、最初に成功したものを使いたいとき

コピペ用:4つの動きを確かめるコード

// extra.ts
export {};
const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
async function slow<T>(v: T, ms: number, fail = false): Promise<T> {
  await sleep(ms);
  if (fail) throw new Error(`${v} は取得できませんでした`);
  return v;
}

const all = await Promise.all([slow("A", 30), slow(1, 10)]);
console.log("all:", all);

const settled = await Promise.allSettled([slow("A", 30), slow("B", 10, true)]);
for (const r of settled) {
  if (r.status === "fulfilled") console.log("settled: fulfilled", r.value);
  else console.log("settled: rejected", (r.reason as Error).message);
}

console.log("race:", await Promise.race([slow("A", 30), slow(1, 10)]));
console.log("any:", await Promise.any([slow("A", 30), slow("B", 10, true)]));

try { await Promise.all([slow("A", 30), slow("B", 10, true)]); }
catch (e) { console.log("all(1件エラー):", e instanceof Error ? e.message : e); }

try { await Promise.any([slow("A", 30, true), slow("B", 10, true)]); }
catch (e) { console.log("any(全件エラー):", e instanceof AggregateError ? `${e.name} ${e.errors.length}件` : e); }
$ node extra.ts
all: [ 'A', 1 ]
settled: fulfilled A
settled: rejected B は取得できませんでした
race: 1
any: A
all(1件エラー): B は取得できませんでした
any(全件エラー): AggregateError 2件

all は終わった順ではなく、渡した順で結果が並びます(A は 30 ミリ秒、1 は 10 ミリ秒ですが、結果は A が先)。race は先に終わった 1 を、any は先にエラーになった B を飛ばして、成功した A を返しています。

allSettled の結果は status で絞り込んでから読む

allSettled の結果は、成功(fulfilled)と不成立(rejected)のどちらかを表すユニオン型です。確認せずに value を読むと、次のエラーになります。

const settled = await Promise.allSettled([fetchUser(1)]);
console.log(settled[0].value);
error TS2339: Property 'value' does not exist on type 'PromiseSettledResult<User>'.
  Property 'value' does not exist on type 'PromiseRejectedResult'.

r.status === "fulfilled" で絞り込むと value が、そうでない側では reason が読めます。 成功したものだけを集める次の書き方は、TypeScript 7.0.2 では型アサーションなしで User[] と推論されました。

const results = await Promise.allSettled([fetchUser(1), fetchUser(2)]);
const ok = results
  .filter((r) => r.status === "fulfilled")
  .map((r) => r.value);   // ok は User[]

reason の型は any なので、使うときは catch の変数と同じように instanceof Error で確かめると安心です。

race でタイムアウトを付ける

function withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
  const timer = new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error(`${ms}ms を超えました`)), ms));
  return Promise.race([p, timer]);
}

try { await withTimeout(slow("A", 50), 20); }
catch (e) { console.log("timeout:", e instanceof Error ? e.message : e); }
timeout: 20ms を超えました

型のポイントは Promise<never> です。タイマー側は値を返すことがなく、必ずエラーで終わるので never と書きます。race の結果は T | never となり、never はユニオンから消えるため、戻り値の型は元の Promise<T> のまま保たれます。

forEachにasync関数を渡したときの順序

配列の各要素に対して非同期処理をしたいとき、forEach に async 関数を渡すと、型チェックは通りますが思った順序で動きません。

// foreach.ts
const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
async function save(id: number): Promise<void> {
  await sleep(30 - id * 10);        // id 1 は 20ms、id 2 は 10ms かかる
  console.log(`saved ${id}`);
}

async function withForEach() {
  [1, 2].forEach(async (id) => {
    await save(id);
  });
  console.log("forEach: done");
}
async function withForOf() {
  for (const id of [1, 2]) {
    await save(id);
  }
  console.log("for...of: done");
}
async function withAll() {
  await Promise.all([1, 2].map((id) => save(id)));
  console.log("Promise.all: done");
}

await withForEach();
await sleep(50);
console.log("---");
await withForOf();
console.log("---");
await withAll();
$ node foreach.ts
forEach: done
saved 2
saved 1
---
saved 1
saved 2
for...of: done
---
saved 2
saved 1
Promise.all: done

forEach では、保存が1件も終わらないうちに「done」が出ています。 forEach はコールバックの戻り値(ここでは Promise<void>)を受け取っても待たずに次へ進み、自身は何も返しません。型の上でも、forEach が受け取るコールバックの戻り値は void とされており、void を返す関数の場所には Promise を返す関数も渡せるため、エラーになりません。このファイルは型チェックをエラーなしで通りました。

書き方 完了を待つか 実行のされ方 向いている場面
forEach(async ...) 待たない 同時に始まり、終わりを知る手段がない 非同期処理には向かない
for...of と await 待つ 1件ずつ順番に 順序が大事な処理、1件ずつ確実に進めたい処理
Promise.all と map 待つ 同時に始めて、全部終わるまで待つ 互いに関係のない処理をまとめて速く終えたい

「順番に」なら for...of、「まとめて」なら Promise.all と map と覚えておくと、どちらの場合も「done」は最後に出ます。

try-catchの変数がunknownになる理由と絞り込み

strict をオンにしていると、catch の変数は unknown 型になります。

async function d() {
  try {
    await fetchUser(1);
  } catch (e) {
    console.log(e.message);
  }
}
error TS18046: 'e' is of type 'unknown'.

unknown になっているのは、JavaScript では Error 以外の値も throw できるからです。

try { throw "文字列を投げた"; } catch (e) { console.log(typeof e, e instanceof Error); }
string false

文字列が飛んでくれば、e.message は undefined になります。そこで、使う前に確かめる形にします。

try {
  await fetchUser(1);
} catch (e) {
  if (e instanceof Error) {
    console.log(e.message);       // ここでは Error 型
  } else {
    console.log(String(e));       // Error 以外は文字列にして出す
  }
}

catch の変数に Error とは書けない(TS1196)

try { await fetchUser(1); }
catch (e: Error) { }
error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.

catch の変数に書ける型注釈は any か unknown だけです。上のように instanceof で絞り込むのが基本の形です。

設定と場所による違い

場所 エラーの変数の型 補足
catch (e)(strict オン) unknown useUnknownInCatchVariables が strict に含まれるため
catch (e)(useUnknownInCatchVariables をオフ) any 同じコードで TS18046 が出なくなることを確認
.catch((err) => ...) のコールバック any strict でも err.message がエラーなしで通ることを確認
allSettled の reason any 同上

try-catch では unknown、.catch() のコールバックでは any という違いがあります。.catch() の側は型チェックが助けてくれないので、.catch((err: unknown) => ...) と自分で注釈しておくと、どちらでも同じ書き方にそろえられます。

fetchの結果を型ガードで検証する

早見表の最後の行のとおり、await res.json() の型は any です。any はどんな型の変数にも入るので、型注釈や as User を書けば型チェックは通ります。ただし、実際に届いた JSON がその形かどうかは、だれも確かめていません。

確認用のサーバー

動きを比べるために、3種類のレスポンスを返す小さなサーバーを用意します。

// server.mjs
import http from "node:http";
http.createServer((req, res) => {
  res.setHeader("content-type", "application/json");
  if (req.url === "/users/1") res.end(JSON.stringify({ id: 1, name: "sato" }));
  else if (req.url === "/users/2") res.end(JSON.stringify({ id: "2", fullName: "suzuki" }));
  else { res.statusCode = 404; res.end(JSON.stringify({ message: "not found" })); }
}).listen(8140, "127.0.0.1");

/users/2 は、id が文字列で、name の代わりに fullName が入っている「想定と違う形」のデータです。

as で決めつけた場合

// fetch_as.ts
type User = { id: number; name: string };

async function getUser(id: number): Promise<User> {
  const res = await fetch(`http://127.0.0.1:8140/users/${id}`);
  return (await res.json()) as User; // 型を「信じる」だけで、中身は調べていない
}

const u = await getUser(2);
console.log(u);
console.log(u.name.toUpperCase());
$ node fetch_as.ts
{ id: '2', fullName: 'suzuki' }
/home/taro/app/fetch_as.ts:10
console.log(u.name.toUpperCase());
                   ^

TypeError: Cannot read properties of undefined (reading 'toUpperCase')

(パスの表示は環境に合わせて置き換えています。)型チェックはエラーなしで通りますが、実行すると、データを受け取った場所から離れた行で止まります。 原因がレスポンスの形にあることが、エラー文からは読み取りにくくなります。

型ガードで確かめる最小コード

// fetch_guard.ts
type User = { id: number; name: string };

// unknown を受け取り、User の形かどうかを実行時に調べる型ガード
function isUser(v: unknown): v is User {
  if (typeof v !== "object" || v === null) return false;
  const o = v as Record<string, unknown>;
  return typeof o.id === "number" && typeof o.name === "string";
}

async function getUser(id: number): Promise<User> {
  const res = await fetch(`http://127.0.0.1:8140/users/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data: unknown = await res.json();
  if (!isUser(data)) throw new TypeError(`User の形ではありません: ${JSON.stringify(data)}`);
  return data; // ここでは User に絞り込まれている
}

for (const id of [1, 2, 3]) {
  try {
    const u = await getUser(id);
    console.log("OK", u.name.toUpperCase());
  } catch (e) {
    if (e instanceof Error) console.log("NG", e.name, e.message);
    else console.log("NG", String(e));
  }
}
$ node fetch_guard.ts
OK SATO
NG TypeError User の形ではありません: {"id":"2","fullName":"suzuki"}
NG Error HTTP 404

ポイントは3つです。

  • const data: unknown = await res.json(); と、any を unknown で受け直す。これで、確かめる前に使おうとするとコンパイルエラーになる
  • v is User を返す型ガード関数で、実行時に形を調べる。true を返した後は、変数が User 型に絞り込まれる
  • res.ok を先に確かめる。fetch は 404 などの応答でもエラーにならず、Response を返す

想定と違う形のデータは、受け取った場所で、届いた中身つきのメッセージとして見つかるようになりました。項目が多い型や入れ子の深い型では、検証用のライブラリでスキーマから型ガードを作る方法もありますが、考え方はこの最小コードと同じです(ライブラリを使った例は今回は未実行です)。

方法 型チェック 実行時の確認 形が違ったとき
as User 通る なし 離れた場所で TypeError
const u: User = await res.json() 通る(any のため) なし 同上
unknown で受けて型ガード 通る あり 受け取った場所で、理由のわかるエラー

Awaitedで非同期関数の結果の型を取り出す

Awaited<T> は、await したときに得られる型を計算するユーティリティ型です。いちばんよく使うのは、ReturnType と組み合わせて「ある非同期関数が最終的に返すデータの型」を取り出す場面です。

// aw.ts
export {};
type User = { id: number; name: string };
async function fetchUser(id: number): Promise<User> { return { id, name: "sato" }; }
const f6 = async (id: number) => ({ id, ok: true });

type R1 = ReturnType<typeof fetchUser>;            // Promise<User>
type R2 = Awaited<ReturnType<typeof fetchUser>>;   // User
type R3 = Awaited<ReturnType<typeof f6>>;          // { id: number; ok: boolean }

const r1: R1 = { id: 1, name: "a" };   // (1)
const r2: R2 = { id: 1, name: "a" };   // 通る
const r3: R3 = null;                   // (2) 型を表示させるための代入
(1) error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Promise<User>'.
(2) error TS2322: Type 'null' is not assignable to type '{ id: number; ok: boolean; }'.

ReturnType だけでは Promise<User> のままなので、User のオブジェクトを代入できません。Awaited で包むと中身の User が取り出せます。

役に立つ場面

場面 書き方
戻り値の型に名前が付いていない関数の結果を、別の関数の引数にしたい function render(u: Awaited<ReturnType<typeof fetchUser>>)
ライブラリの非同期関数の結果の型を、型をインポートせずに使いたい type Result = Awaited<ReturnType<typeof 関数名>>
「値か Promise のどちらか」を受け取る関数の結果の型を書きたい Awaited<T>(早見表の string | Promise<number> の行)

型に名前を付けて export してある場合(この記事の User のような場合)は、その名前を直接使うほうが読みやすくなります。Awaited は、型の名前が手元にないときに、関数から型を逆算する道具と考えるとちょうどよい使いどころになります。

つまずきやすいエラーの一覧

エラーコード 出る場面 直し方
TS2339(Promise<...> を含む) await を忘れてプロパティを読んだ await を付ける
TS2739 / TS2322(Promise<...> を含む) Promise を中身の型の変数に入れた await を付ける。配列なら Promise.all で待つ
TS2801 Promise を if の条件に書いた if (await ...) にする
TS1308 async でない関数の中で await を書いた 関数に async を付ける
TS1309 CommonJS のファイルの一番外側で await を書いた package.json に "type": "module" を書く、または async 関数で包む
TS1064 async 関数の戻り値の注釈が Promise でない Promise<T> と書く
TS18046 catch の変数をそのまま使った instanceof Error で絞り込む
TS1196 catch の変数に Error などの型を書いた 注釈を外して、中で絞り込む
TS2339(PromiseSettledResult) allSettled の結果から直接 value を読んだ status === "fulfilled" で絞り込む

非同期処理を書くときのチェックリスト

  • [ ] ほかのファイルから呼ばれる async 関数に、Promise<T> の戻り値注釈を書いた
  • [ ] エラー文に Promise<...> が出たら、await の付け忘れを最初に確かめた
  • [ ] 配列の各要素を処理するとき、forEach ではなく for...of か Promise.all と map を使った
  • [ ] 一部が取得できなくても続けたい処理は allSettled にして、status で絞り込んだ
  • [ ] catch の変数は instanceof Error で確かめてから使った
  • [ ] fetch の結果は unknown で受けて、型ガードで形を確かめた
  • [ ] res.ok を確かめてから JSON を読んだ

よくある質問

Q. async 関数の戻り値の型注釈は省略してもよいですか? A. 省略しても正しく推論されます。ほかのファイルから呼ばれる関数や、return が複数ある関数では書いておくのがおすすめです。実装を変えたときに、戻り値の型が変わったことを関数の定義の場所で知らせてくれます。

Q. Promise.all と allSettled はどちらを使えばよいですか? A. 全部そろって初めて意味があるなら all、取得できたものだけでも表示や処理を続けたいなら allSettled です。all は1件でもエラーがあると全体がエラーになり、ほかの結果は受け取れません。allSettled はエラーにならない代わりに、結果を status で絞り込む手間が1つ増えます。

Q. Promise<void> と Promise<undefined> は違いますか? A. 何も返さない async 関数は Promise<void> と推論されます(早見表の2行目)。void は「戻り値を使わない」という意図を表す型なので、結果を使わない関数には Promise<void> と書くのが一般的です。

Q. Awaited は自分で使う場面がありますか? A. await を書くだけなら TypeScript が自動で計算してくれるので、意識する必要はありません。自分で書くのは、Awaited<ReturnType<typeof 関数>> のように、非同期関数の結果の型を別の場所で使い回したいときです。

まとめ

  • async 関数の戻り値は必ず Promise<T>。注釈も Promise<T> の形で書く(中身の型だけを書くと TS1064)
  • await すると中身の T が取り出される。エラー文に Promise<...> が出たら、await の付け忘れを確かめる
  • Promise.all はタプル、allSettled は PromiseSettledResult のタプル、race と any はユニオン型になる
  • forEach は async 関数の完了を待たない。順番に進めるなら for...of、まとめて待つなら Promise.all と map
  • catch の変数は strict で unknown。instanceof Error で絞り込んでから使う。.catch() のコールバックは any なので自分で unknown と注釈する
  • res.json() は any。unknown で受けて型ガードで確かめると、形の違うデータを受け取った場所で見つけられる
  • Awaited<ReturnType<typeof 関数>> で、非同期関数が最終的に返すデータの型を取り出せる

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

(記載例)出典:株式会社RJC「TypeScript async/awaitの型|Promise・Awaited」

株式会社RJCでは、TypeScriptのasync/awaitとPromiseの型の扱いを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社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で一緒に開発しながら、 成長を楽しみませんか?