404 Not Found

404 Not Found


nginx

TypeScript の型アサーション

型アサーションは、コンパイラに対して「この値の型については、あなたよりも私がよく知っている」と伝えるものです。これは実行時の型を変更するものではなく、コンパイル時の型チェックにのみ影響を与えます。

1. 型アサーションの基礎

(1) 2つの構文

TYPESCRIPT
// as Grammar(Recommendations,JSX "Must" must be used in)
let value: any = "hello";
let length1: number = (value as string).length;

// Angle Bracket Syntax(Cannot be in JSX Used in)
let length2: number = (<string>value).length;
📌 推奨事項: as という表記を統一して使用してください。角括弧の表記は JSX/TSX のコンポーネントタグと競合するため、as の方がより汎用性の高い選択肢となります。

(2) アサーションの目的

「narrowing」または「broadening」のアサーション — 値を別の互換性のある型として扱うよう TypeScript に指示します:

TYPESCRIPT
// Narrow:From broad types to narrow types(Common)
let value: string | number = "hello";
let str = value as string;       // Tell the compiler"I'm sure this is string"
console.log(str.toUpperCase());  // ✅

// Broadening:From Narrow Types to Wide Types(Less commonly used)
let name = "Charlie" as string;    // "Charlie" Type from literal "Charlie" Generalize to string

(3) アサーションは実行時の型を変更しない

TYPESCRIPT
let value: any = 42;
let str = value as string;       // The compile-time type is string
console.log(typeof str);         // "number" —— At runtime, it is still number!
// console.log(str.toUpperCase()); // Runtime Crash!number None toUpperCase
🔥 基本原則:型アサーションは実行時に変換を行いません。これらはあくまでコンパイル時の型注釈に過ぎません。アサーションが失敗した場合でも、実行時にエラーが発生します。


2. 一般的なアサーションのシナリオ

(1) DOM要素の種類

TYPESCRIPT
// getElementById Back HTMLElement | null
let input = document.getElementById("myInput") as HTMLInputElement;
// After the assertion, you can use it directly. HTMLInputElement Properties of
console.log(input.value);        // ✅
console.log(input.placeholder);  // ✅

// A Safer Way to Write It——Check first null
let inputEl = document.getElementById("myInput");
if (inputEl instanceof HTMLInputElement) {
  console.log(inputEl.value);    // ✅ instanceof narrow,No assertion required
}

(2) APIの応答タイプ

TYPESCRIPT
interface User {
  id: number;
  name: string;
  email: string;
}

// JSON.parse Back any——Specify the type using an assertion
let response = JSON.parse('{"id":1,"name":"Charlie","email":"xiao@example.com"}') as User;
console.log(response.name);  // ✅ Type: string

// A Safer Way to Write It——Runtime Validation (See Lesson 26)
function isUser(obj: any): obj is User {
  return typeof obj.id === "number"
    && typeof obj.name === "string"
    && typeof obj.email === "string";
}

let data = JSON.parse('...');
if (isUser(data)) {
  console.log(data.name);  // ✅ Type Guard Narrowing,Safer than assertions
}

(3) 不要な属性チェックの回避

TYPESCRIPT
interface Config {
  host: string;
  port: number;
}

// Direct Assignment——Check for Unnecessary Attributes
// let cfg: Config = { host: "localhost", port: 3000, debug: true };  // ❌

// Method 1:Type Assertion Bypass
let cfg = { host: "localhost", port: 3000, debug: true } as Config;  // ✅

// Method 2:Assign a value to the variable first, then pass it(Using Structured Type Compatibility)
let options = { host: "localhost", port: 3000, debug: true };
let cfg2: Config = options;  // ✅ Variable assignments do not perform unnecessary property checks

▶ 例:複合型のアサーションの扱い

TYPESCRIPT
type SuccessResponse = {
  status: "success";
  data: { id: number; name: string };
};

type ErrorResponse = {
  status: "error";
  error: { code: number; message: string };
};

type ApiResponse = SuccessResponse | ErrorResponse;

function handleResponse(response: ApiResponse) {
  if (response.status === "success") {
    // Type Narrowing(Recommendations)
    console.log(response.data.name);
  } else {
    // Type Narrowing(Recommendations)
    console.log(response.error.message);
  }

  // Not recommended——Replace Narrowing with Assertions
  // let data = (response as SuccessResponse).data;  // Danger!
}

3. const アサーション

as const 値に対して、TypeScriptに最も正確なリテラル型を推論させる + readonly:

(1) 基本的な使い方

TYPESCRIPT
// None as const——widening Inference
let obj = { host: "localhost", port: 3000 };
// Type:{ host: string; port: number }
obj.host = "other";  // ✅ Can be modified

// With as const — exact literals + readonly
let config = { host: "localhost", port: 3000 } as const;
// Type:{ readonly host: "localhost"; readonly port: 3000 }
// config.host = "other";  // ❌ readonly
// config.port = 8080;     // ❌ readonly

(2) 配列用の as const

TYPESCRIPT
// Regular Arrays
let arr = [1, 2, 3];
// Type:number[]

// as const Array——become readonly Tuple
let tuple = [1, 2, 3] as const;
// Type:readonly [1, 2, 3]
// tuple[0] = 10;  // ❌ readonly
// tuple.push(4);  // ❌ readonly

(3) as constの実用的な活用法――定数とアクションタイプの定義

TYPESCRIPT
// Redux Stylistic action Type Definitions
const INCREMENT = "INCREMENT" as const;
const DECREMENT = "DECREMENT" as const;

// None as const → INCREMENT The type is string(Too wide)
// With as const → INCREMENT typee is "INCREMENT"(Exact Literals)

type Action = {
  type: typeof INCREMENT;
  payload: number;
} | {
  type: typeof DECREMENT;
  payload: number;
};

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case "INCREMENT": return state + action.payload;
    case "DECREMENT": return state - action.payload;
  }
}

4. 空でないというアサーション !

nullでないというアサーションは、TypeScriptに対して「この値がnullでもundefinedでもないことは確実です」と伝えます:

(1) 基本的な使い方

TYPESCRIPT
let value: string | null = "hello";

// Non-empty assertion——Tell the compiler"The value is not null"
console.log(value!.toUpperCase());  // ✅ Compilation successful

// Equivalent to asserting that
console.log((value as string).toUpperCase());

(2) よくあるシナリオ

TYPESCRIPT
// DOM Element
let el = document.getElementById("app");
el!.innerHTML = "Hello";  // Use ! to assert non-null

// Assignment After the Optional Chain
let user: { name?: string } = { name: "Charlie" };
let nameLength = user.name!.length;  // Assertion name No undefined

// Function Arguments
function process(value: string | undefined) {
  // We"Know"When called value It can't be... undefined
  console.log(value!.toUpperCase());
}

(3) 空でないアサーションに伴うリスク

TYPESCRIPT
let value: string | null = null;

// Compilation successful!But it crashes during runtime——value Actually, it is null
// console.log(value!.toUpperCase());  // Runtime Error:Cannot read property of null

// ✅ A Safer Way to Write It——Check first
if (value !== null) {
  console.log(value.toUpperCase());  // Type Narrowing,Compilation+All operations are safe
}
💡 推奨事項: null ではないことを確認するアサーションは、あくまで最後の手段として使用してください。可能な限り、チェックには if を、オプショナルチェーンには ?. を使用してください。これらはより安全な代替手段です。! は、値が null ではないと 100% 確信しているにもかかわらず、コンパイラがそれを推論できない場合にのみ使用してください。


5. 二重アサーションとアサーションの制限

(1) 主張の限界

TypeScript では、まったく無関係な型アサーションは許可されません:

TYPESCRIPT
let value: string = "hello";

// ✅ string → string | number(From subtype to supertype,Compatibility)
let wide = value as string | number;

// ✅ string | number → string(From Parent Type to Child Type,May not be safe, but allowed)
let narrow = wide as string;

// ❌ string → number(Completely unrelated,Not allowed)
// let num = value as number;

(2) 二重アサーション(制限を回避するため、使用は強く推奨されません)

TYPESCRIPT
let value: string = "hello";

// Through any Transit——Double Assertion
let num = value as unknown as number;  // Compilation successful!

// But at runtime value It's still string
console.log(typeof num);  // "string"
// num.toFixed(2);         // Runtime Crash!
⚠️ 警告: ダブルアサーション (as unknown as T) は、型安全性のチェックを完全にバイパスしてしまいます。これは、コンパイラに対して「まあいいや、責任はすべて私が負うから」と告げているのと同じであり、99%のケースで誤りです。ダブルアサーションを使用する必要が生じた場合、コードの設計に問題がある可能性が非常に高いため、型構造を見直す必要があります。**


6. アサーションのベストプラクティス

(1) 優先順位の決定

TEXT
Type Narrowing(if/typeof/instanceof/in)  → Safest,Use as a priority
Type Guard(Custom is Function)           → Safety,Collapse Complex Types
Non-empty assertion !                          → Use with caution,When it is determined that the value is not empty
as Assertion                             → Use less,When there is a clear reason
as const                            → Recommended for constant definitions
Double Assertion as unknown as T            → Highly not recommended,99%This is incorrect usage.

(2) アサーションの安全性チェックリスト

as アサーションを書く前に、次の3つの質問を自問してみてください:

  1. 代わりに型絞り込みを使えますか?(if/typeof/instanceof)
  2. オプションチェーンに切り替えることはできますか? (?.)
  3. アサーションが失敗した場合、プログラムは実行時にクラッシュしますか?

質問3への答えが「はい」の場合は、より安全な方法を検討してください。

▶ 例:安全なアサーションと安全でないアサーションの比較

TYPESCRIPT
interface User {
  id: number;
  name: string;
  email: string;
}

// ❌ Unsafe——Blind assertions API Response
function fetchUser1(id: number): User {
  let data = JSON.parse(localStorage.getItem(`user:${id}`) ?? "{}") as User;
  return data;  // data May not match User Structure
}

// ✅ Safety——Runtime Validation + Type Guard
function isUser(obj: any): obj is User {
  return obj
    && typeof obj.id === "number"
    && typeof obj.name === "string"
    && typeof obj.email === "string";
}

function fetchUser2(id: number): User | null {
  let raw = localStorage.getItem(`user:${id}`);
  if (!raw) return null;

  let data = JSON.parse(raw);
  if (isUser(data)) {
    return data;  // ✅ Type Guard: Return safely after confirmation
  }
  return null;
}

❓ よくある質問

Q 型アサーションと型変換の違いは何ですか?
A 型アサーションはコンパイル時の型チェックにのみ影響し、実行時の値を変更することはありません。つまり、x as string は x を文字列に変換しません。型変換は実行時に値の型を変更します—String(42)は実際に42を「42」に変換します。TypeScriptのasは変換ではなく、アサーションです。
Q as constreadonly の違いは何ですか?
A readonly は、単一のプロパティを読み取り専用としてマークします。as constは、オブジェクトまたは配列全体(すべてのレベル)をreadonlyによるリテラル型にします。const x = { a: 1 } as constlet x: { readonly a: 1 }よりも簡潔であり、深層的な読み取り専用保護を提供します。
Q 非空アサーション !? はどのような場合に適切ですか?
A 最も適切なシナリオは DOM の操作です。つまり、HTML 内に特定の要素が確実に存在することが分かっているものの、TypeScript ではそれを検証できない場合です。document.getElementById("app")!.innerHTML = "Hi" ページの構造が分かっている場合に適しています。それ以外のシナリオでは、代わりに if ステートメントを使用してチェックを行ってください。
Q as unknown as T(二重アサーション)の正当な用途にはどのようなものがありますか?
A ほとんどありません。唯一妥当なシナリオは、型システムが意図した挙動を本当に表現できない場合(特定の複雑なジェネリック操作や、サードパーティ製ライブラリの型定義にバグが含まれている場合など)に限られます。しかし、二重アサーションの99%は、型構造を再設計することで回避可能です。

📖 まとめ

📝 練習問題

  1. 基本問題(難易度 ⭐)as const を使用して方向定数 DIRECTIONS = ["north", "south", "east", "west"] as const を定義し、typeof DIRECTIONS[number] 型のパラメータを受け取る関数を記述して、有効な方向の値が 4 つだけであることを確認してください。
  2. 応用問題(難易度 ⭐⭐):内部で document.querySelector(selector) as T | null を使用する関数 querySelector<T extends HTMLElement>(selector: string): T | null を記述してください。次に、input要素を特定し、nullチェックを使用してvalueプロパティにアクセスする呼び出しを作成してください。さらに、nullチェックの代わりにifステートメントを使用する、より安全なバージョンを記述してください。
  3. 課題(難易度:⭐⭐⭐):単純なスキーマオブジェクトを用いて検証ルール({ name: "string", age: "number" }など)を記述し、実行時にunknownの値がそれに一致するかどうかをチェックする実行時型検証関数validate<T>(schema: Schema, value: unknown): value is Tを実装してください。一致する場合、型ガードは T に絞り込まれます。これを as T と比較し、安全性がどこにあるかを説明してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%