1. ホーム
  2. typescript

[解決済み] タイプスクリプト。カスタムタイプに対する typeof のチェック

2022-10-06 19:09:31

質問

私はカスタムタイプを持っています。

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

文字列がFruit型に含まれるかどうかを判断したいのですが、どうすればよいですか?どうすれば実現できますか?

以下はうまくいきません。

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

どんな考えでもありがたい

どのように解決するのですか?

簡単な答えです。

を使うことはできません。 typeof を実行時にチェックするために interface の型をチェックする必要があります。 その代わりに ユーザ定義型ガード関数 を書いて、そのような型をチェックすることができます。

const fruit = ["apple", "banana", "grape"] as const;
type Fruit = (typeof fruit)[number];
const isFruit = (x: any): x is Fruit => fruit.includes(x);

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

長い回答が続きます。


TypeScriptにおける値と型の違いについて、特に typeof 演算子に関するものです。 ご存知の通り、TypeScriptはJavaScriptに静的な型システムを追加しています。 がトランスパイルされると、その型システムは消去されます。 . TypeScriptの文法は、いくつかの式や文が を参照するものもあれば、実行時に存在する を参照する。 値 を持つ 型を持っていますが、それ自身は型ではありません。 重要なのは、コードの中には、コンパイラが値を期待し、可能であれば見つけた式を値として解釈する場所と、コンパイラが型を期待し、可能であれば見つけた式を型として解釈する場所があることです。

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 のタイプクエリ演算子 という名前の値にTypeScriptが割り当てた静的な型を調べている。 bar .

あなたのコードで

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

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


あなたは何を できること の値をチェックすることです。 myfruit の値を確認することです。 Fruit 文字列リテラル...例えばこのように。

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

完璧でしょう? さて、冗長なコードがたくさんあるように見えるかもしれません。 ここでは、より冗長でない方法を紹介します。 まず最初に Fruit の型を既存のリテラル値の配列の観点から定義します。TypeScriptは値から型を推論することはできますが、型から値を生成することはできません。

const fruit = ["apple", "banana", "grape"] as const;
export type Fruit = (typeof fruit)[number];

を確認することができます。 Fruit が手動で自分で定義したものと同じ型であることを確認します。 次に、型のテストに ユーザー定義のタイプガード をこのようにします。

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 文の中で、その myfruitFruit . を受け付けるだけの関数があったとしたらどうでしょう? Fruit を受け付ける関数があり、その値が Fruit :

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

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

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

しかし、あなたは できる をチェックした後に "then"節の内部で呼び出すことができます。

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

これが、そもそもカスタムタイプに対してチェックを行いたい理由だと思われます。 というわけで、これでできるようになりました。


要約すると typeof . 文字列との比較はできる。 型推論と型ガードで重複したコードを排除し、コンパイラからコントロールフローの型解析を受けることができます。

コードへのプレイグラウンドリンク