Type Guardを使用すると、条件ブロック内のオブジェクトの型を制限することができます。
TypeScriptはJavaScriptのinstanceof
とtypeof
演算子の使用を認識しています。条件付きブロックでこれらを使用すると、TypeScriptはその条件ブロック内で異なる変数の型を理解します。ここでは、TypeScriptが特定の関数がstring
に存在せず、おそらくユーザーのタイプミスであったことを指摘する簡単な例を示します:
function doSomething(x: number | string) {if (typeof x === 'string') { // Within the block TypeScript knows that `x` must be a stringconsole.log(x.subtr(1)); // Error, 'subtr' does not exist on `string`console.log(x.substr(1)); // OK}x.substr(1); // Error: There is no guarantee that `x` is a `string`}
次はクラスと instanceof
の例です:
class Foo {foo = 123;common = '123';}class Bar {bar = 123;common = '123';}function doStuff(arg: Foo | Bar) {if (arg instanceof Foo) {console.log(arg.foo); // OKconsole.log(arg.bar); // Error!}if (arg instanceof Bar) {console.log(arg.foo); // Error!console.log(arg.bar); // OK}console.log(arg.common); // OKconsole.log(arg.foo); // Error!console.log(arg.bar); // Error!}doStuff(new Foo());doStuff(new Bar());
TypeScriptは else
を理解しています。そうすればif
の中は、特定の型であり、else
の中はその型でないことが分かります。次に例を示します。
class Foo {foo = 123;}class Bar {bar = 123;}function doStuff(arg: Foo | Bar) {if (arg instanceof Foo) {console.log(arg.foo); // OKconsole.log(arg.bar); // Error!}else { // MUST BE Bar!console.log(arg.foo); // Error!console.log(arg.bar); // OK}}doStuff(new Foo());doStuff(new Bar());
in
演算子は、オブジェクト上のプロパティの存在を安全にチェックし、Type Guardとして使用することができます。例えば。
interface A {x: number;}interface B {y: string;}function doStuff(q: A | B) {if ('x' in q) {// q: A}else {// q: B}}
異なるリテラル値を区別するには、===
/ ==
/ !==
/ !=
が利用できます。
type TriState = 'yes' | 'no' | 'unknown';function logOutState(state:TriState) {if (state == 'yes') {console.log('User selected yes');} else if (state == 'no') {console.log('User selected no');} else {console.log('User has not made a selection yet');}}
ユニオン型の中にリテラル型がある場合にも同じ方法が使えます。次のように共通するプロパティの値をチェックすることで、異なるユニオン型を区別できます。
type Foo = {kind: 'foo', // Literal typefoo: number}type Bar = {kind: 'bar', // Literal typebar: number}function doStuff(arg: Foo | Bar) {if (arg.kind === 'foo') {console.log(arg.foo); // OKconsole.log(arg.bar); // Error!}else { // MUST BE Bar!console.log(arg.foo); // Error!console.log(arg.bar); // OK}}
TypeScriptは十分賢いので、次のように== null
/ != null
チェックをすることでnull
とundefined
の両方を排除できます。
function foo(a?: number | null) {if (a == null) return;// a is number now.}
JavaScriptには非常に豊富な実行時の解析サポートが組み込まれていません。単純なJavaScriptオブジェクトだけを使用している場合(構造型を使用する場合)、 instanceof
またはtypeof
にアクセスすることさえできません。これらの場合、Type Guard関数をユーザーが定義することができます。これは、someArgumentName is SomeType
を返す関数です。次に例を示します。
/*** Just some interfaces*/interface Foo {foo: number;common: string;}interface Bar {bar: number;common: string;}/*** User Defined Type Guard!*/function isFoo(arg: any): arg is Foo {return arg.foo !== undefined;}/*** Sample usage of the User Defined Type Guard*/function doStuff(arg: Foo | Bar) {if (isFoo(arg)) {console.log(arg.foo); // OKconsole.log(arg.bar); // Error!}else {console.log(arg.foo); // Error!console.log(arg.bar); // OK}}doStuff({ foo: 123, common: '123' });doStuff({ bar: 123, common: '123' });
TypeScriptは、コールバック内でタイプガードがアクティブのままであることは危険なため、これを想定していません。例えば、
// Example Setupdeclare var foo:{bar?: {baz: string}};function immediate(callback: ()=>void) {callback();}// Type Guardif (foo.bar) {console.log(foo.bar.baz); // OkayfunctionDoingSomeStuff(() => {console.log(foo.bar.baz); // TS error: Object is possibly 'undefined'"});}
この修正は、推測された安全な値をローカル変数に格納し、自動的に外部から変更されないようにするのと同じくらい簡単で、TypeScript はそれを簡単に理解することができます。
// Type Guardif (foo.bar) {console.log(foo.bar.baz); // Okayconst bar = foo.bar;functionDoingSomeStuff(() => {console.log(bar.baz); // Okay});}