web-dev-qa-db-ja.com

タイプスクリプト:カスタムタイプに対して「typeof」をチェック

カスタムタイプがあります。

export type Fruit = "Apple" | "banana" | "grape";

文字列がFruitタイプの一部であるかどうかを判断したいと思います。どうすればこれを達成できますか?

以下は機能しません。

let myfruit = "pear";
if (typeof myfruit === "Fruit") {
    console.log("My fruit is of type 'Fruit'");
}

どんな考えでも感謝します!

14
maia

特にtypeof演算子に関連するため、TypeScriptの値と型の違いについて混乱する場合があります。お気づきかもしれませんが、TypeScriptは静的な型システムをJavaScriptに追加し、 コードが変換されるとその型システムが消去されます 。 TypeScriptの構文では、一部の式とステートメントは実行時に存在するvaluesを参照し、他の式とステートメントはtypesは、設計時またはコンパイル時にのみ存在します。値は型を持っていますが型ですが、型そのものではありません。重要なのは、コンパイラーが値を予期し、可能であれば値として検出した式を解釈するコードの場所と、コンパイラーが型を予期し、可能であれば式を検出した式を解釈する他の場所があります。

typeof演算子は二重の寿命をもたらします。式typeof xは常にxが値であることを期待しますが、typeof x自体はコンテキストに応じて値またはタイプになります。

let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}

let TypeofBar = typeof bar;はJavaScriptに到達し、実行時に JavaScript typeof演算子 を使用して文字列を生成します。しかし、type TypeofBar = typeof bar;消去され、 TypeScript type query operator を使用して、TypeScriptがbarという名前の値に割り当てた静的型を調べています。

あなたのコードでは、

let myfruit = "pear";
if (typeof myfruit === "Fruit") { // "string" === "Fruit" ?!
    console.log("My fruit is of type 'Fruit'");
}

typeof myfruitは値であり、タイプではありません。したがって、TypeScript型のクエリ演算子ではなく、JavaScript typeof演算子です。常に値"string"を返します。 Fruitまたは"Fruit"になることはありません。型システムは実行時に消去されるため、実行時にTypeScript型クエリ演算子の結果を取得することはできません。 typeof演算子をあきらめる必要があります。


あなたがcanすることは、3つの既知のmyfruit文字列リテラルに対してFruitの値をチェックすることです... 、 この:

let myfruit = "pear";
if (myfruit === "Apple" || myfruit === "banana" || myfruit === "grape") {
  console.log("My fruit is of type 'Fruit'");
}

完璧ですねさて、多分冗長なコードのように思えるかもしれません。冗長性の少ない方法を次に示します。まず、既存のリテラル値の配列に関してFruit型を定義します... TypeScriptは値から型を推測できますが、型から値を生成することはできません。

const stringLitArray = <L extends string>(arr: L[]) => arr;
const fruit = stringLitArray(["Apple", "banana", "grape"]);
export type Fruit = (typeof fruit)[number];

Fruitが自分で手動で定義したものと同じタイプであることを確認できます。次に、タイプテストでは、次のように ser-defined type guard を使用できます。

const isFruit = (x: any): x is Fruit => fruit.includes(x);

isFruit()は、その引数がfruit配列で見つかったかどうかを確認する関数であり、見つかった場合、その引数の型をFruitに絞り込みます。動作を見てみましょう:

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

また、そのタイプガードにより、コンパイラはifステートメントの「then」節内で、myfruitFruitであることを認識できます。 Fruitのみを受け入れる関数と、Fruitである場合とそうでない場合がある値があるとします。

declare function acceptFruit(f: Fruit): void;
const myfruit = Math.random() < 0.5 ? "pear" : "banana";

関数を直接呼び出すことはできません。

acceptFruit(myfruit); // error, myfruit might be "pear"

ただし、canは、チェック後に "then"句内で呼び出すことができます。

if (isFruit(myfruit)) {
  acceptFruit(myfruit); // okay, myfruit is known to be "banana"
}

そもそも、カスタムタイプをチェックしたいのはこのためでしょう。だからそれを行うことができます。


要約すると、typeofは使用できません。文字列と比較できます。型推論と型ガードを実行して、重複したコードを削除し、コンパイラーから制御フロー型分析を取得できます。

それが役に立てば幸いです。幸運を。

41
jcalz