C# の Nullable が分かる人が TypeScript の null / undefined で混乱する5つ
みなさんこんにちは!ヒロポンです!
C# の null は、もう手に馴染んでる。int?(Nullable<int>)で「値が無いかも」を表して、?. でぬるぽを避けて、?? でデフォルト値を流す。これを業務で何年もやってきた人、多いと思います。
で、TypeScript を開く。null がある。お、C# と同じやん??……と思いきや、undefined というもう一つの「空」が出てくる。しかも設定ひとつで、型が null を許したり許さなかったりする。
C# は「空 = null」の1種類でシンプルでした。TS は「空」が2つあって、安全性も設定次第。ここを知らずに書くと、コンパイルは通ったのに実行時にぬるぽ、をやります。
この記事は、C# の null / Nullable<T> を知ってる前提で、TypeScript の null / undefined で詰まる5箇所を対応で翻訳します。C# の感覚は捨てない。ズレてる部分だけ上書きする。これが狙いです。
まず対応マップ: C# の null と TypeScript の null / undefined
細かい話の前に、全体の対応をこんな感じで1枚に。

この表のズレてる行を、コード付きで見ていきます。
混乱①: null と undefined は別物
C# の「空」は null の1種類。TypeScript には、それが2つあります。
let a: string | undefined; // 未代入のまま → undefined
let b: string | null = null; // 明示的に「空」を入れた → null
console.log(a); // undefined
console.log(b); // null
console.log(a === b); // false (別物)
慣習はこう。まだ値が入ってない・存在しない = undefined、意図的に「空っぽ」を入れた = null。
C# の null 1本で考えてると、「同じ空でしょ?」と混同します。undefined === null は false。型としても値としても別物。まずここを分けて考えるのが第一歩です。
混乱②: strictNullChecks を on にしないと、型が null を許す
ここが一番大事。
TypeScript は、strictNullChecks という設定が off だと、null / undefined をどの型にも代入できてしまいます。公式 Handbook にこう書かれています。
strictNullChecks が off の場合、null や undefined になりうる値も通常どおりアクセスでき、null と undefined の値はどんな型のプロパティにも代入できる。これは null チェックのない言語(例: C#、Java)の挙動に似ている。
俺の解釈はこう。strictNullChecks off は、ぬるぽ対策が入る前の古い C# とほぼ同じ世界です。null がどの型にも入って、実行時に初めて落ちる。
// strictNullChecks を off にすると、これがコンパイルを通ってしまう
let userName: string = null;
console.log(userName.length); // 実行時に "Cannot read properties of null" で落ちる
on にすると、こうなる。型が null / undefined を厳密に区別してくれる。C# の Nullable参照型(NRT)を on にした感覚に近いです。tsconfig.json で "strict": true(or "strictNullChecks": true)を入れておく。これを切ったまま書くと、TypeScript の一番の売りである null 安全が、まるごと効きません。
混乱③: ? は undefined であって、null じゃない
C# の int? は Nullable<int> で、null を表します。TypeScript の ? は似て非なるもの。
interface User {
name: string;
age?: number; // ← age は number | undefined。null ではない
}
const u: User = { name: "tanaka" };
console.log(u.age); // undefined (null ではない)
プロパティに付ける ?(オプショナル)は、number | undefined の意味。null は含みません。明示的に null も許したいなら、age: number | null と型で書く。
C# の int?(= null)の感覚で age?: number を「null が入る」と思っていると、ここでズレます。? は undefined、| null は null。別物です。
混乱④: ?. と ?? は C# とほぼ同じ。ただし ?? は null も undefined も拾う
ここは朗報。C# で使ってる ?.(null 条件)と ??(null 合体)は、TypeScript でもほぼ同じ感覚で使えます。
const len = user?.name?.length ?? 0; // 途中が空なら 0
user が null/undefined なら、その先を評価せずに止まって(?.)、最終的に空なら 0 を返す(??)。C# の user?.Name?.Length ?? 0 と、見た目もほぼ同じですよね。
違いは1つだけ。TypeScript の ?? は、null も undefined も両方「空」として拾います。C# の ?? は null だけ。TS では2種類の空があるぶん、?? が両方を面倒見てくれる。そう覚えておくと楽です。
混乱⑤: 非nullアサーション ! は「null 無視」。乱用すると実行時に落ちる
最後は、便利だけど危ないやつ。
値の後ろに付ける !(非nullアサーション)は、「これは null/undefined じゃない」とコンパイラに断言するだけの記号です。
const el = document.getElementById("app")!; // ! で「null じゃない」と断言
el.innerHTML = "hi"; // コンパイルは通る。でも要素が無ければ実行時に落ちる
もっと小さく再現すると、こんな感じ。
function find(): string | null {
return null; // 実際には null が返る
}
const s = find()!; // ! で「null じゃない」と断言。コンパイルは通る
console.log(s.length); // 実行時に TypeError: null... で落ちる
実行結果(! を付けても、中身が null なら実行時に落ちる):

Handbook も、「値が null/undefined にならないと確実に分かっている時だけ使え」と明記しています。! は型の上で null/undefined を消すだけ。実行時の中身は1ミリも変えない。
乱用すると、コンパイルは通るのに実行時にぬるぽ。C# でいう「null 許容警告を握りつぶして本番で落ちる」のと、まったく同じ事故です。! を付ける前に、本当に null じゃないと言い切れるか、一拍考える。これだけで事故はだいぶ減ります。
動かして確かめる
混乱②の strictNullChecks は、実際に tsc で挙動を見るとこんな感じで一発で腑に落ちます。
// nullcheck.ts
let userName: string = null;
console.log(userName.length);
# strictNullChecks on: コンパイル時点で弾いてくれる
tsc --strictNullChecks nullcheck.ts
# → error TS2322: Type 'null' is not assignable to type 'string'.
# strictNullChecks off にしたい時は tsconfig.json で "strictNullChecks": false にする
# (off だと型エラーは出ず、コンパイルが通って実行時に落ちる)
実行結果(strictNullChecks on で TS2322 が出る):

--strictNullChecks を付けると、null を string に入れたその行でコンパイルエラー。off だと素通りして実行時に落ちる。この差こそが、strictNullChecks の本体です!!
ちなみに、最近の tsc は何も設定しないと strictNullChecks が on 寄りの挙動をするものもあります。tsconfig.json の "strict": true を起点に、自分のプロジェクトでどっちが効いてるかは一度 tsc --showConfig で確認しておくと安心。
俺の現場メモ
C# の「空は null の1種類」の感覚のまま TS に入ると、まず①の null / undefined の使い分けで一度は「あれ?」となります。
俺もなりました。API から返ってきた値が、未設定の項目は undefined、明示的に空の項目は null で混在してて……=== null だけでチェックしてたら、undefined をすり抜けて落ちた。null チェックしてるのに、なんで落ちんねん??ってなったやつ。== null(緩い比較)なら両方拾えるんですが、それも知らないと普通に詰まる。
でも、やられたのはこの5箇所くらい。残りは C# の ?. ?? の感覚がそのまま効くし、strictNullChecks さえ on にしておけば、危ない代入はコンパイラが止めてくれます。C# で null と付き合ってきた経験は、ちゃんと土台になる。捨てなくていい。
おすすめは、新規プロジェクトなら最初に "strict": true を入れること。これだけで、null 起因の事故の大半は、こんな感じで書いてる最中に潰せます。
まとめ
C# の Nullable<T> 経験者が TypeScript の null / undefined で詰まる5つ、整理するとこうです。
- ① null と undefined は別物 — 未代入=undefined、明示的な空=null。
=== nullだけだと undefined をすり抜ける - ② strictNullChecks は on が前提 — off は「古い C#」相当でぬるぽ放置。
"strict": trueを入れる - ③
?は undefined、| nullは null —age?: numberはnumber | undefined - ④
?.と??は C# とほぼ同じ — ただし??は null も undefined も拾う - ⑤
!(非nullアサーション)は実行時の中身を変えない — 乱用するとコンパイルを通って本番で落ちる
上書きするのは「空が2つ」「strictNullChecks を on」。この2つだけ。あとは C# で培った null の知識が、そのまま TypeScript でいい感じに効いてくれます。
よくある質問
Q1. C# の int? と TypeScript の number? は同じですか?
違います。C# の int? は Nullable<int> で null を表します。TypeScript の number?(プロパティの ?)は number | undefined で、null ではありません。null も許したいなら number | null、両方なら number | null | undefined と型で書きます。
Q2. null と undefined、自分のコードではどっちを使えばいいですか?
慣習では、未設定・未初期化は undefined、意図的に「空」を入れる時は null を使い分けます。ただしチーム内で統一されていれば、片方に寄せる方針もありです。大事なのは、外部 API などで両方が混在しうる前提で、== null(緩い比較で両方拾う)や ?. でまとめて扱うことです。
Q3. strictNullChecks を後から on にしたら、エラーだらけになりました。
既存コードに null チェック漏れが大量にあった、という健全な発見です。一気に直すのが大変なら、ファイル単位で対応するか、まず ! や ?. で型を通しつつ、危ない箇所から実際のチェックに置き換えます。新規プロジェクトなら最初から on にしておくのが一番ラクです。
Q4. !(非nullアサーション)は使ってはいけないですか?
「null/undefined にならない」と確実に言える時だけ使うのが原則です。document.getElementById の直後など、存在が保証できない場面で乱用すると、コンパイルは通っても実行時に落ちます。迷うなら ?. や明示的な null チェックの方が安全です。
関連記事
- TypeScript for C#: LINQ と配列メソッドの対応表 — 同じ「C# の知識で TS を読む」シリーズ。LINQ ↔ 配列メソッドの対応
- C# の文字列比較 == / Equals / StringComparison でハマる3箇所 — C# の「等しい」の判定。TS の
===/==とセットで押さえると混乱しない - C# の TryParse 3パターン(int / DateTime / enum) — 変換の失敗を null じゃなく bool で受ける C# の作法。null ハンドリングの隣の話
- SESの年収と単価を逆算する — C# の知識を土台に TS まで広げると、案件の幅がどう変わるか。数字で考える話
- 「客先常駐しかない」業務SEが技術+αで抜ける道 — C# 既知から TS へ橋渡しできる SE は、現場でどう評価が変わるか。橋渡しのキャリア論
以上!
「C# の null 感覚で TS 書いて undefined にやられた」経験ある人いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務SE 7年(SIer 正社員2 / フリーランス5)。現職は SEO 直轄部の AI アドバイザー兼 PL、副業で中小 SIer の CTO。SIer の正社員からフリーランスに転じ、複数のエージェント経由で案件を回してきた経験ベースで「業務SE視点」の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524(日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


コメント