#TypeScript 型アサーション

導入

型宣言のない値の場合、TypeScript は型推論を実行するため、多くの場合、開発者が望む結果が得られない可能性があります。

タイプ T = 'a'|'b'|'c';
foo = 'a' とします。

let bar:T = foo; // エラーを報告します。

上記の例では、TypeScript は変数 foo の型が string であるのに対し、変数 bar の型は 'a'|'b'|'c' であると推論するため、最後の行でエラーが報告されます。前者は後者の親タイプです。親タイプをサブタイプに割り当てることができないため、エラーが報告されます。

TypeScript は、開発者がコード内の特定の値の型を「アサート」し、その値がどのような型であるかをコンパイラに伝えることができる「型アサーション」の手段を提供します。 TypeScript は、型アサーションの存在を検出すると、値に対して型推論を実行せず、アサーションによって指定された型を直接採用します。

このアプローチの本質は、開発者が特定の場所でコンパイラの型推論を「バイパス」できるようにすることで、もともと型チェックに失敗したコードが合格して、コンパイラのエラーを回避できるようにすることです。これにより TypeScript の型システムの厳密性は弱まりますが、結局のところ、開発者はコンパイラよりも自分のコードのことをよく知っています。

上記の例に戻ると、解決策は、型アサーションを実行し、代入中に変数 foo の型をアサートすることです。

タイプ T = 'a'|'b'|'c';

foo = 'a' とします。
let bar:T = foo as T; // 修正します。

上記の例では、最後の行の foo as T は、変数 foo の型表明が T であることをコンパイラーに指示するため、この行では型推論が必要なくなります。 「T」の場合、エラーは報告されません。

つまり、型アサーションは実際には値の型を変更するのではなく、値を処理する方法をコンパイラーに指示します。

型アサーションには 2 つの構文があります。

// 構文 1: <type> 値
<タイプ>値

// 構文 2: 型としての値
タイプとしての値

上記 2 つの構文は同等で、「value」は値を表し、「Type」はタイプを表します。初期の頃は構文 1 のみでした。その後、TypeScript が React の JSX 構文 (山かっこは HTML 要素を表す) をサポートし始めたため、2 つの構文間の競合を避けるために構文 2 が導入されました。現時点では、Grammar 2 を使用することをお勧めします。

// 文法1
let bar:T = <T>foo;

// 文法 2
bar:T = foo を T としてみましょう。

上記の例は 2 つの型アサーションの構文です。構文の 1 つは JSX 構文と競合します。これを使用する場合は、TypeScript の React サポートをオフにする必要があります。オフにしないと認識されません。このため、現在では文法 2 が一般的に使用されています。

以下に例を見てみましょう。 「オブジェクト」の章で説明したように、オブジェクト型には厳密なリテラル チェックがあり、追加の属性がある場合はエラーが報告されます。

// エラーを報告する
const p:{ x: 数値 } = { x: 0, y: 0 };

上の例では、等号の右側が追加の属性 'y' を持つオブジェクト リテラルであるため、エラーが発生します。解決策は型アサーションを使用することです。2 つの異なるアサーションを使用できます。

// 正しい
const p0:{ x: 数値 } =
  { x: 0, y: 0 } as { x: 数値 };

// 正しい
const p1:{ x: 数値 } =
  { x: 0, y: 0 } as { x: 数値; y: 数値 };

上記の例では、どちらの型アサーションも正しいです。最初のアサーションは、等号の左側と一致するように型を変更します。2 番目のアサーションは、等号の右側の型を、親の型に割り当てることができます。同時に、型アサーションが存在するため、厳密なリテラル Checked が存在しないため、エラーは報告されません。

以下はWebプログラミングの実践例です。

const ユーザー名 = document.getElementById('ユーザー名');

if (ユーザー名) {
  (HTMLInputElement としてのユーザー名).value; // 正しい
}

上記の例では、変数 username の型は HTMLElement | null ですが、null の場合を除くと、HTMLElement 型には value 属性がありません。 username が入力ボックスの場合、型アサーションを使用してその型を HTMLInputElement に変更し、その後 value 属性を読み取ることができます。

上記の例の型アサーションの括弧は必須であることに注意してください。そうでない場合、usernameHTMLInputElement.value としてアサートされ、エラーが報告されます。

型アサーションは TypeScript の型チェックを変更し、バグを引き起こす可能性があるため、悪用しないでください。

const データ:オブジェクト = {
  答え: 1、
  b:2、
  c:3
};

data.length; // エラーレポート

(Array<string> としてのデータ).length; // 正しい

上の例では、変数 data はオブジェクトであり、length プロパティを持っていません。ただし、型アサーションを使用すると、その型を配列としてアサートできるため、長さ属性を使用して型チェックに合格できます。ただし、コンパイルされたコードは実行時にエラーを報告するため、型アサーションにより、誤ったコードがコンパイルに合格する可能性があります。

型アサーションの使用法の 1 つは、型が不明な変数の特定の型を指定することです。

const 値:unknown = 'Hello World';

const s1:string = value; // エラーレポート
const s2:string = 文字列としての値 // 正しい。

上記の例では、未知の型変数 value を他の型の変数に直接代入することはできませんが、他の型としてアサートできるため、他の変数に代入することができます。

型アサーションの条件

型アサーションは、値が任意の型であると表明できることを意味するものではありません。

定数n = 1;
const m:string = n 文字列として // エラー;

上記の例では、変数「n」は数値であるため、文字列としてアサートすることはできません。TypeScript はエラーを報告します。

型アサーションを使用する前提は、値の実際の型とアサートされた型が条件を満たさなければならないということです。

T として式

上記のコードでは、expr は実際の値、T は型アサーションであり、次の条件を満たしている必要があります: exprT のサブタイプ、または Texpr のサブタイプです。 。

つまり、型アサーションでは、実際の型がアサートされた型と互換性があることが必要です。実際の型は、より広範な型 (スーパータイプ) またはより正確な型 (サブタイプ) としてアサートできますが、完全に無関係であるとアサートすることはできません。ジャンル。

ただし、完全に無関係なタイプにアサートしたい場合は、それを行うことができます。つまり、2 つの連続した型アサーションを実行し、最初に未知の型または任意の型に対してアサートし、次にターゲット型に対してアサートします。 「any」型と「unknown」型は他のすべての型の親型であるため、完全に無関係な 2 つの型の間の仲介として機能します。

// または <T><unknown>expr と書きます
expr は T と同じくらい不明

上記のコードでは、expr は 2 つの連続した型アサーションを作成します。最初のアサーションは 'unknown' 型で、2 番目のアサーションは 'T' 型です。この場合、エラーを報告することなく、expr を任意の型 T にアサートできます。

以下は、このセクションの冒頭の例を言い換えたものです。

定数n = 1;
const m:string = n は文字列としては不明です。 // 正しいです。

上記の例では、2 つの型アサーションを通じて、変数 n の型が数値からまったく関係のない文字列に変更されるため、値を割り当てるときにエラーは報告されません。

const アサーションとして

変数の型が宣言されていない場合、let コマンドで宣言された変数は TypeScript の組み込み基本型の 1 つとして型推論され、const コマンドで宣言された変数は値型定数として推論されます。

//型推論は基本型文字列です
s1 = 'JavaScript'; とします。

//型は文字列「JavaScript」として推論されます
const s2 = 'JavaScript';

上記の例では、変数 s1 の型は string として推論され、変数 s2 の型は値型 JavaScript として推論されます。後者は前者のサブタイプであり、より強い制限効果を持つ const コマンドに相当し、変数の型範囲を狭めることができます。

let 変数に予期しないエラーが表示される場合がありますが、これらを const 変数に変更すると、エラーが解消されることがあります。

let s = 'JavaScript';

タイプ ラング =
  |'JavaScript'
  |'タイプスクリプト'
  |'Python';

関数 setLang(言語:Lang) {
  /* ... */
}

setLang(s); // エラーを報告する

上記の例では、関数 setLang() のパラメータ language が共用体型である Lang 型であるため、最後の行でエラーが報告されています。ただし、渡された文字列 s の型は、Lang のスーパータイプである string として推論されます。親タイプはサブタイプを置き換えることができないため、エラーが発生します。

解決策の 1 つは、let コマンドを const コマンドに変更することです。

const s = 'JavaScript';

この場合、変数 s の型は、共用体型 Lang のサブタイプである値型 JavaScript であり、関数 setLang() に渡すときにエラーは報告されません。

もう 1 つの解決策は、型アサーションを使用することです。 TypeScript は特別な型アサーション as const を提供します。これは、型を推論するときに、値を定数として推論できること、つまり let 変数が const 変数としてアサートされることをコンパイラに伝えるために使用され、それによってビルドされた変数が変更されます。 -in 基本型から値型へ。

let s = 'JavaScript' を const として指定します。
setLang(s); // 正しい

上記の例では、変数 s は let コマンドで宣言されていますが、as const アサーションを使用すると、変数 s の型は const コマンドで宣言されたことと同じになります。値の型は JavaScript です。

「as const」アサーションを使用した後は、let 変数はその値を変更できなくなります。

let s = 'JavaScript' を const として指定します。
s = 'Python' // エラーを報告します。

上記の例では、let コマンドによって宣言された変数 s の値は、as const を使用してアサートされた後は変更できません。変更しない場合は、エラーが報告されます。

「as const」アサーションはリテラルにのみ使用でき、変数には使用できないことに注意してください。

let s = 'JavaScript';
setLang(s as const); // エラーを報告します。

上記の例では、「as const」アサーションが変数「s」に使用されており、エラーが報告されます。これは以下でより明確に見ることができます。

s1 = 'JavaScript'; とします。
let s2 = s1 as const; // エラー

また、式では「as const」を使用できません。

let s = ('Java' + 'Script') as const; // エラーを報告します。

上記の例では、式で as const が使用されているため、エラーが発生します。

as const は接頭辞付き形式で記述することもできます。

// ポストフォーム
定数としての式

//プレフィックス形式
<const>式

「as const」アサーションは、オブジェクト全体に対しても、オブジェクトの単一のプロパティに対しても使用できます。この場合、その型削減効果は異なります。

const v1 = {
  ×:1、
  y: 2、
}; // 型は { x: 数値; }

const v2 = {
  x: 定数として 1、
  y:2、
}; // 型は { x: 1; }

const v3 = {
  ×:1、
  y: 2、
} as const; // 型は {readonly x: 1; }

上記の例では、2 番目の書き方は属性「x」の型を削減するもので、3 番目の書き方はオブジェクト全体の型を削減するものです。

つまり、「as const」はリテラルの型を不変型としてアサートし、TypeScript で許可される最小の型に絞り込みます。

以下は配列の例です。

// a1 の型は、number[] として推論されます。
const a1 = [1, 2, 3];

// a2 の型は readonly [1, 2, 3] として推論されます
const a2 = [1, 2, 3] as const;

上記の例では、配列リテラルが「as const」を使用してアサートされた後、型推論は読み取り専用タプルになります。

as const は配列を読み取り専用のタプルに変換するため、関数の残りのパラメータとして使用するのに適しています。

関数 add(x:数値, y:数値) {
  x + y を返します。
}

const nums = [1, 2];
const total = add(...nums); // エラー

上の例では、変数 nums の型は number[] として推論され、スプレッド演算子 ... を使用して関数 add() に渡すときにエラーが報告されます。 add() は 2 つのパラメータのみを受け入れることができ、...nums` はパラメータの数を保証しません。

実際、固定数のパラメータを持つ関数の場合、渡されたパラメータにスプレッド演算子が含まれている場合、スプレッド演算子はタプルでのみ使用できます。スプレッド演算子は、関数定義でrestパラメータが使用されている場合にのみ配列で使用できます。

解決策は、「as const」アサーションを使用して配列をタプルに変換することです。

const nums = [1, 2] を const として指定します。
const total = add(...nums); // 正しい

上記の例では、「as const」アサーションを使用した後、変数「nums」の型は、スプレッド演算子を使用して展開された後、「readonly [1, 2]」として推論され、そのパラメータの型と正確に一致します。関数「add()」。

Enum メンバーは、「as const」を使用してアサートすることもできます。

enum Foo {
  ×、
  Yさん
}
let e1 = Foo.X; // Foo;
let e2 = Foo.X as const;

上記の例では、「as const」アサーションが使用されない場合、変数「e1」の型は完全な Enum 型であると推論されます。「as const」アサーションが使用された後は、変数「e2」の型は次のようになります。これは、Enum のメンバーであると推測され、別のメンバーに変更できないことを意味します。

非 null アサーション

空の可能性がある変数 (つまり、「unknown」または「null」に等しい可能性があります) については、TypeScript はこれらの変数が空にならないことを保証するための非 null アサーションを提供します。これは感嘆符を追加することによって記述されます。変数名の後に「!」を付けます。

関数 f(x?:数値|null) {
  validateNumber(x); // x が数値であることを確認するカスタム関数
  console.log(x!.toFixed());
}

関数 validateNumber(e?:number|null) {
  if (typeof e !== '数値')
    throw new Error('数値ではありません');
}

上の例では、関数 f() のパラメータ x の型は number|null です。つまり、空であってもかまいません。空の場合は x.toFixed() メソッドが存在しないので、このように書くとエラーが報告されます。ただし、開発者は、validateNumber() の事前チェックの後、変数 x が空ではないことを確認できます。この時点では、非 null アサーションを使用して変数 にサフィックスを追加できます。関数本体内の x !x!.toFixed()` はエラーなしでコンパイルされます。

非 null アサーションは実際のプログラミングに非常に役立ち、追加の判断を省略できる場合があります。

const root = document.getElementById('root');

// エラーを報告する
root.addEventListener('クリック', e => {
  /* ... */
});

上記の例では、getElementById() はヌル値 null を返す可能性があります。つまり、変数 root が空である可能性があります。この場合、addEventListener() メソッドを呼び出すとエラーが報告されます。コンパイルに失敗します。ただし、開発者が Web ページに「root」要素が確実に存在することを確認できれば、非 null アサーションを使用できます。

const root = document.getElementById('root')!;

上記の例では、getElementById() メソッドにサフィックス ! が追加されており、このメソッドが空ではない結果を確実に返すことを示しています。

ただし、null 以外のアサーションはセキュリティ上のリスクを引き起こすため、式の値が null ではないことが確実な場合にのみ使用してください。手動で空かどうか確認した方が安全です。

const root = document.getElementById('root');

if (root === null) {
  throw new Error('DOM 要素 #root が見つかりません');
}

root.addEventListener('クリック', e => {
  /* ... */
});

上記の例では、「root」が空の場合、エラーがスローされますが、これは非 null アサーションより安全です。

null 以外のアサーションは、代入アサーションにも使用できます。 TypeScript には、クラスのプロパティを初期化する (つまり、初期値を持たせる) 必要があるコンパイル設定があります。プロパティに値が割り当てられていない場合、エラーが報告されます。

クラスポイント{
  x:number; // エラーレポート
  y:number; // エラーレポート

  コンストラクター(x:数値, y:数値) {
    // ...
  }
}

上記の例では、プロパティ xy は、TypeScript が初期化されていないと判断するため、エラーを報告します。

現時点では、null 以外のアサーションを使用して、これら 2 つの属性が確実に値を持つことを示し、エラーが報告されないようにすることができます。

クラスポイント{
  x!:数値; // 正しい
  y!:数値; // 正しい

  コンストラクター(x:数値, y:数値) {
    // ...
  }
}

さらに、非 null アサーションは、コンパイル オプション strictNullChecks がオンになっている場合にのみ意味を持ちます。このオプションがオンになっていない場合、コンパイラは変数が「未定義」または「null」であるかどうかをチェックしません。

アサーション関数

アサーション関数は、関数パラメータが特定の型に準拠していることを保証する特別な関数です。関数のパラメーターが要件を満たしていない場合、エラーがスローされ、プログラムの実行が中断されます。要件が満たされている場合、操作は実行されず、コードは通常のフローに従って実行されます。

関数 isString(値:不明):void {
  if (値の型 !== '文字列')
    throw new Error('文字列ではありません');
}

上記の例では、関数 isString() はアサーション関数であり、パラメータ value が文字列であることを確認するために使用されます。そうでない場合は、エラーがスローされ、プログラムの実行が中断されます。

使用方法は次のとおりです。

関数 toUpper(x: 文字列|数値) {
  isString(x);
  x.toUpperCase() を返します。
}

上記の例では、関数 toUpper() のパラメータ x は文字列または数値になります。ただし、関数本体の最後の行で toUpperCase() メソッドを呼び出すときは、x が文字列であることを確認する必要があります。そうでない場合は、エラーが報告されます。したがって、アサーション関数 isString() がこの行の前で呼び出されると、TypeScript は変数 x が数値ではなく文字列である必要があると判断できるため、エラーは報告されません。

アサーション関数 isString() を記述する従来の方法には、パラメータの型が unknown であり、戻り値の型が void である (つまり、戻り値がありません) という欠点があります。このような型宣言だけからは、isString() がアサーション関数であることを理解するのは困難です。

アサーション関数をより明確に表現するために、TypeScript 3.7 では新しい型の記述方法が導入されています。

function isString(value:unknown):値が string であることをアサートします {
  if (値の型 !== '文字列')
    throw new Error('文字列ではありません');
}

上記の例では、関数 isString() の戻り値の型は asserts value is string として記述されます。ここで、assertsis はキーワード、 value は関数のパラメータ名です。 string は関数のパラメータの型です。これが意味するのは、この関数はパラメータ value の型が string であることをアサートするために使用されるということです。要件が満たされない場合、プログラムはここで中断されます。

アサーション関数の新しい記述方法を使用した後、関数が実行される限り、TypeScript はそれを自動的に認識します。対応する変数はアサートされた型になります。

関数の戻り値のアサーション記述メソッドは、関数の意図をより明確に表現するためにのみ使用されることに注意してください。実際のチェックは開発者自身がデプロイする必要があります。さらに、TypeScript は、内部チェックがアサーションと矛盾する場合でもエラーを報告しません。

function isString(value:unknown):値が string であることをアサートします {
  if (値の型 !== '数値')
    throw new Error('数値ではありません');
}

上記の例では、関数のアサーションはパラメータ value が文字列型であることを示していますが、実際には内部チェックはそれが数値であるかどうかであり、数値でない場合はエラーがスローされます。このコードは正常にコンパイルされます。つまり、TypeScript はアサーションが実際の型チェックと一致しているかどうかをチェックしません。

また、アサーション関数のasserts文はvoid型に相当するため、未定義、null以外の値が返された場合はエラーとなります。

function isString(value:unknown):値が string であることをアサートします {
  if (値の型 !== '文字列')
    throw new Error('文字列ではありません');
  true を返す // エラーレポート
}

上の例では、アサーション関数が「true」を返したため、エラーが発生しました。

別の例を示します。

タイプ アクセスレベル = 'r' |

関数 allowedReadAccess(
  レベル:アクセスレベル
):レベルが 'r' であることをアサートします |
  if (!level.includes('r'))
    throw new Error('読み取りは許可されていません');
}

上記の例では、関数 allowsReadAccess() を使用して、パラメータ levelr または rw に等しい必要があることをアサートしています。

パラメータが null ではないことを主張したい場合は、ユーティリティ タイプ NonNullable<T> を使用できます。

関数assertIsDefined<T>(
  値:T
):asserts 値が NonNullable<T> {
  if (値 === 未定義 || 値 === null) {
    新しいエラーをスローします(`${value} が定義されていません`)
  }
}

上記の例では、ツール型 NonNullable<T> は、null 型を削除した後の残りの型 T 型に対応します。

関数式の中でアサーション関数を使用したい場合は、以下の書き方が可能です。

//書き方その1
constassertIsNumber = (
  値:不明
):値が数値であることをアサートします => {
  if (値の型 !== '数値')
    throw Error('数値ではありません');
};

//書き方2
タイプ AssertIsNumber =
  (値:不明) => 値が数値であることをアサートします。

constassertIsNumber:AssertIsNumber = (値) => {
  if (値の型 !== '数値')
    throw Error('数値ではありません');
};

アサーション関数と型ガード関数は 2 つの異なる関数であることに注意してください。これらの違いは、アサートされた関数は値を返さないのに対し、型保護された関数は常にブール値を返すことです。

関数 isString(
  値:不明
):値は文字列 {
  戻り値の型 === '文字列';
}

上の例は型保護関数 isString() で、パラメータ value が文字列であるかどうかをチェックするために使用されます。はいの場合は「true」を返し、それ以外の場合は「false」を返します。この関数の戻り値の型は「value is string」です。「is」は型演算子です。左側の値が右側の型と一致する場合は「true」を返し、それ以外の場合は「false」を返します。

パラメーターが true であることが保証されている (つまり、「false」、「unknown」、および「null」に等しくない) ことをアサートしたい場合、TypeScript はアサーション関数の短縮形を提供します。

関数assert(x:unknown):asserts x {
  // ...
}

上記の例では、関数 assert() のアサーション部分で、asserts x が述語と目的語を省略しており、パラメータ x が真 (true) であることが保証されていることを示しています。

同様に、パラメータが true であるかどうかの実際のチェックは、開発者が実装する必要があります。

関数assert(x:unknown):asserts x {
  if (!x) {
    throw new Error(`${x} は真の値である必要があります。`);
  }
}

アサーション関数のこの短縮形式は、通常、操作が成功したかどうかを確認するために使用されます。

タイプ人 = {
  名前: 文字列;
  電子メール?: 文字列;
};

関数loadperson(): 人null {
  null を返します。
}

let person =loadPerson();

関数アサート(
  状態:不明、
  メッセージ: 文字列
):条件をアサートします{
  if (!condition) 新しいエラー (メッセージ) をスローします。
}

// エラー: 個人が定義されていません
assert(人, '人は定義されていません');
console.log(人名);

上記の例では、loadPerson() が true を返した場合 (つまり、操作が成功した場合) のみ、assert() はエラーを報告しません。

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0