ホームページ > 記事 > ウェブフロントエンド > 構築不可能な型を使用した TypeScript での豊富なコンパイル時例外
TypeScript の型システムは強力ですが、エラー メッセージが難解で理解しにくい場合があります。この記事では、構築不可能な型を使用して明確で説明的なコンパイル時例外を作成するパターンを検討します。このアプローチは、有用なエラー メッセージで無効な状態を表現できないようにすることで、実行時エラーを防ぐのに役立ちます。
まず、核となるパターンを分析してみましょう:
// Create a unique symbol for our type exception declare const TypeException: unique symbol; // Basic type definitions type Struct = Record<string, any>; type Funct<T, R> = (arg: T) => R; type Types<T> = keyof T & string; type Sanitize<T> = T extends string ? T : never; // The core pattern for type-level exceptions export type Unbox<T extends Struct> = { [Type in Types<T>]: T[Type] extends Funct<any, infer Ret> ? (arg: Ret) => any : T[Type] extends Struct ? { [TypeException]: `Variant <${Sanitize<Type>}> is of type <Union>. Migrate logic to <None> variant to capture <${Sanitize<Type>}> types.`; } : (value: T[Type]) => any; };
バリアント型でこのパターンを使用する方法を示す例を次に示します。
type DataVariant = | { type: 'text'; content: string } | { type: 'number'; value: number } | { type: 'complex'; nested: { data: string } }; type VariantHandler = Unbox<{ text: (content: string) => void; number: (value: number) => void; complex: { // This will trigger our custom error [TypeException]: `Variant <complex> is of type <Union>. Migrate logic to <None> variant to capture <complex> types.` }; }>; // This will show our custom error at compile time const invalidHandler: VariantHandler = { text: (content) => console.log(content), number: (value) => console.log(value), complex: (nested) => console.log(nested) // Error: Type has unconstructable signature };
これは、再帰型でパターンを使用する方法を示す、より複雑な例です。
type TreeNode<T> = { value: T; children?: TreeNode<T>[]; }; type TreeHandler<T> = Unbox<{ leaf: (value: T) => void; node: TreeNode<T> extends Struct ? { [TypeException]: `Cannot directly handle node type. Use leaf handler for individual values.`; } : never; }>; // Usage example - will show custom error const invalidTreeHandler: TreeHandler<string> = { leaf: (value) => console.log(value), node: (node) => console.log(node) // Error: Cannot directly handle node type };
パターンを使用して有効な型の状態遷移を強制する方法は次のとおりです。
type LoadingState<T> = { idle: null; loading: null; error: Error; success: T; }; type StateHandler<T> = Unbox<{ idle: () => void; loading: () => void; error: (error: Error) => void; success: (data: T) => void; // Prevent direct access to state object state: LoadingState<T> extends Struct ? { [TypeException]: `Cannot access state directly. Use individual handlers for each state.`; } : never; }>; // This will trigger our custom error const invalidStateHandler: StateHandler<string> = { idle: () => {}, loading: () => {}, error: (e) => console.error(e), success: (data) => console.log(data), state: (state) => {} // Error: Cannot access state directly };
このパターンは、次の場合に特に役立ちます。
パターンが内部的にどのように機能するかを詳しく見てみましょう:
// Create a unique symbol for our type exception declare const TypeException: unique symbol; // Basic type definitions type Struct = Record<string, any>; type Funct<T, R> = (arg: T) => R; type Types<T> = keyof T & string; type Sanitize<T> = T extends string ? T : never; // The core pattern for type-level exceptions export type Unbox<T extends Struct> = { [Type in Types<T>]: T[Type] extends Funct<any, infer Ret> ? (arg: Ret) => any : T[Type] extends Struct ? { [TypeException]: `Variant <${Sanitize<Type>}> is of type <Union>. Migrate logic to <None> variant to capture <${Sanitize<Type>}> types.`; } : (value: T[Type]) => any; };
カスタム エラー メッセージで構築不可能な型を使用することは、自己文書化型制約を作成するための強力なパターンです。 TypeScript の型システムを活用してコンパイル時に明確なガイダンスを提供し、開発者が実行時の問題になる前に問題を見つけて修正できるようにします。
このパターンは、特定の組み合わせが無効である必要がある複雑な型システムを構築する場合に特に役立ちます。無効な状態を表現できないようにし、明確なエラー メッセージを提供することで、より保守しやすく開発者にとって使いやすい TypeScript コードを作成できます。
以上が構築不可能な型を使用した TypeScript での豊富なコンパイル時例外の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。