TS 数値区切り文字とより厳密なクラス属性チェックの詳細な説明

TS 数値区切り文字とより厳密なクラス属性チェックの詳細な説明

概要

TypeScript 2.4 では、識別子のスペル修正が実装されています。変数名、プロパティ名、または関数名のスペルを少し間違えた場合でも、多くの場合、TypeScript は正しいスペルを提案してくれます。

TypeScript 2.7 は、ECMAScript の数値区切り文字の提案をサポートしています。 この機能を使用すると、数字の間にアンダースコア (_) を使用して数字をグループ化できます (数字をグループ化するためにカンマとピリオドを使用するのと同じように)。

定数worldPopulationIn2017 = 7_600_000_000;
定数 最小有効バイトマスク = 0b1111_1111;
定数パパイヤホイップカラーHexCode = 0xFF_EF_D5;

数字の区切り文字によって数値リテラルの値は変わりませんが、グループ化すると一目で数字が読みやすくなります。

これらの区切り文字は、2 進数や 16 進数にも役立ちます。

ビットを 0b0010_1010 とします。
ルーチンを 0xC0FFEE_F00D_BED とします。
マーティン = 0xF0_1E_ とする

直感に反するかもしれませんが、JavaScript では数字はクレジットカード番号や電話番号には適していません。文字列の方が適しています。

es2015 でコンパイルされた上記のコードをターゲットに設定すると、TypeScript によって次の js コードが生成されます。

定数worldPopulationIn2017 = 7600000000;
定数 最小有効バイトマスク = 255;
const papayawhipColorHexCode = 16773077;

演算子の改良と正確なinstanceof

TypeScript 2.7 では、型の改良に 2 つの変更が加えられ、「型ガード」を適用することでより詳細な型を決定できるようになりました。

まず、instanceof 演算子は、構造の互換性に依存せずに継承チェーンを利用するようになりました。これにより、実行時の instanceof 演算子の動作がより正確に反映されます。 これにより、instanceof を使用して構造的に類似しているが関連のない型を絞り込むときに発生するいくつかの複雑さを回避できます。

2 番目に、in 演算子は型ガードとして機能し、明示的に宣言されていないプロパティ名を絞り込むようになりました。

インターフェースA { a: 数値 };
インターフェース B { b: 文字列 };

関数foo(x:A | B) {
    if ("a" in x) {
        xa を返します。
    }
    xb を返します。
}

よりスマートなオブジェクトリテラル推論

JS には、ユーザーが一部のプロパティを省略し、後で使用するときにそれらのプロパティの値が未定義になるというパターンがあります。

foo = someTest とします? { 値: 42 } : {};

以前の TypeScript は、{ value: number } と {} の最適なスーパータイプである {} を探していました。 これは技術的には正しいですが、あまり役に立ちません。

バージョン 2.7 以降、TypeScript は各プロパティのオブジェクトリテラル型レコードを「正規化」し、未定義の型プロパティごとにオプションのプロパティを挿入して、それらを結合します。

上記の例では、foo の最終的な型は { value: number } | { value?: undefined } です。 TypeScript のきめ細かい型付けと組み合わせることで、TypeScript が理解できる、より表現力豊かなコードを記述できるようになります。 別の例を見てみましょう。

// 型がある
// | { a: ブール値、aData: 数値、b?: 未定義 }
// | { b: ブール値、bData: 文字列、a?: 未定義 }
bar = Math.random() < 0.5 とします。
    { a: true、aデータ: 100 } :
    { b: true、bData: "hello" };

もし(バー.b){
    // TypeScriptは'bar'が次の型であることを認識します
    //
    // '{ b: ブール値、 bData: 文字列、 a?: 未定義 }'
    //
    // 'bData' が利用可能であることがわかります。
    bar.bData.toLowerCase()
}

ここで、TypeScript は b プロパティを検査して bar のタイプを調整し、bData プロパティにアクセスできるようにします。

固有のシンボルタイプと定数名の属性

TypeScript 2.7 では ECMAScript シンボルの理解が深まり、シンボルの使用方法がより柔軟になりました。

最も要望の多いユースケースは、シンボルを使用して適切に型指定されたプロパティを宣言することです。 たとえば、次の例を考えてみましょう。

const Foo = シンボル("Foo");
const Bar = シンボル("Bar");

x = {とします
    [フー]: 100,
    [バー]: 「こんにちは」
};

let a = x[Foo]; // 型は 'number' です
let b = x[Bar]; // 型は 'string' です

ご覧のとおり、Foo と Bar は定数として宣言されているため、TypeScript は x にシンボル Foo と Bar で宣言されたプロパティがあることを追跡できます。 TypeScript はこれを活用して、Foo と Bar に新しい型、つまり一意のシンボルを追加します。

一意のシンボルはシンボルのサブタイプであり、Symbol() または Symbol.for() を呼び出すか、明示的な型注釈によってのみ生成できます。 これらは定数宣言と読み取り専用の静的プロパティにのみ表示され、既存の一意のシンボル タイプを参照するには、typeof 演算子を使用する必要があります。 固有のシンボルへの各参照は、完全に固有の宣言された ID を意味します。

// 作品
const Foo を宣言: 一意のシンボル;

// エラー! 'Bar' は定数ではありません。
Bar: ユニークなシンボル = Symbol();

// 動作します - 一意のシンボルを参照しますが、その ID は 'Foo' に関連付けられています。
Baz: typeof Foo = Foo; とします。

// これも動作します。
クラスC {
    静的読み取り専用 StaticSymbol: 一意のシンボル = Symbol();
}

それぞれの固有シンボルは完全に独立した ID を持つため、2 つの固有シンボル タイプを割り当てたり比較したりすることはできません。

const Foo = シンボル();
const バー = シンボル();

// エラー: 2 つの一意のシンボルを比較できません。
Foo === Barの場合{
    // ...
}

もう 1 つの使用例としては、シンボルをジョイント マーカーとして使用することが挙げられます。

// ./ShapeKind.ts
エクスポート const Circle = Symbol("circle");
エクスポート const Square = Symbol("square");

// ./ShapeFun.ts
* を ShapeKind として "./ShapeKind" からインポートします。

インターフェースサークル{
    種類: ShapeKind.Circle の型;
    半径: 数値;
}

インターフェーススクエア{
    種類: typeof ShapeKind.Square;
    sideLength: 数値;
}

関数エリア(形状: 円 | 四角形) {
    (shape.kind === ShapeKind.Circle)の場合{
        // 'shape' は 'Circle' 型です
        Math.PI * shape.radius ** 2 を返します。
    }
    // 'shape' はタイプ 'Square' です
    shape.sideLength ** 2 を返します。
}

より厳密なクラス属性チェック

TypeScript 2.7 では、クラス内の厳密なプロパティ初期化チェックのための新しいコンパイラ オプションが導入されました。 --strictPropertyInitializationフラグが有効になっている場合、型チェッカーはクラス内で宣言された各インスタンスプロパティが

  • undefined を含む型はありますか?
  • 明示的な初期化子がある
  • コンストラクタで確実に割り当てられる

--strictPropertyInitialization オプションはコンパイラ オプション ファミリの一部であり、--strict フラグが設定されると自動的に有効になります。 他のすべての厳密なコンパイラ オプションと同様に、--strict を true に設定し、--strictPropertyInitialization を false に設定することで、厳密なプロパティ初期化チェックを選択的に無効にすることができます。

--strictPropertyInitialization を有効にするには、--strictNullCheck フラグを (直接または --strict を介して間接的に) 設定する必要があることに注意してください。

それでは、厳密なプロパティ初期化チェックを見てみましょう。 --strictpropertyinitialized フラグが有効になっていない場合、次のコードは型チェックは正常に行われますが、実行時に TypeError が生成されます。

クラスユーザー{
  ユーザー名: 文字列;
}

ユーザーを新規作成します。

// TypeError: 未定義のプロパティ 'toLowerCase' を読み取ることができません
定数 username = user.username.toLowerCase();

ランタイム エラーの原因は、プロパティに値が割り当てられていないため、ユーザー名プロパティの値が未定義であることです。したがって、toLowerCase() メソッドの呼び出しは失敗します。

--strictpropertyinitialize を有効にすると、型チェッカーはエラーを報告します。

クラスユーザー{
  // 型エラー: プロパティ 'username' に初期化子がありません
  // コンストラクタ内で確実に割り当てられるわけではない
  ユーザー名: 文字列;
}

次に、型エラーを排除するために User クラスを正しく型指定する 4 つの方法を見てみましょう。

解決策1: 定義を許可する

型エラーを排除する 1 つの方法は、ユーザー名プロパティに undefined を含む型を指定することです。

クラスユーザー{
  ユーザー名: 文字列 | 未定義;
}

ユーザーを新規作成します。

これで、ユーザー名プロパティが undefined の値を保持することは完全に有効になります。しかし、ユーザー名プロパティを文字列として使用したい場合、まずtypeofを使用して、実際に文字列が含まれていて未定義の値ではないことを確認する必要があります。

// わかりました
const username = typeof user.username === "文字列"
  ? user.username.toLowerCase()
  : "該当なし";

解決策2: 明示的なプロパティの初期化

型エラーを排除する別の方法は、ユーザー名プロパティに明示的な初期化子を追加することです。この方法では、プロパティはすぐに文字列値を保持し、 undefined にはなりません。

クラスユーザー{
  ユーザー名 = "n/a";
}

ユーザーを新規作成します。

// わかりました
定数 username = user.username.toLowerCase();

解決策3: コンストラクタ代入を使用する

おそらく最も便利な解決策は、コンストラクターにユーザー名パラメーターを追加し、それをユーザー名プロパティに割り当てることです。したがって、User クラスのインスタンスが構築されるたびに、呼び出し元はユーザー名を引数として提供する必要があります。

クラスユーザー{
  ユーザー名: 文字列;

  コンストラクター(ユーザー名: 文字列) {
    this.username = ユーザー名;
  }
}

const user = 新しい User("mariusschulz");

// わかりました
定数 username = user.username.toLowerCase();

次のように、クラス フィールドへの明示的な割り当てを削除し、ユーザー名コンストラクター パラメーターに public 修飾子を追加することで、User クラスを簡素化することもできます。

クラスユーザー{
  コンストラクター(パブリックユーザー名: 文字列) {}
}

const user = 新しい User("mariusschulz");

// わかりました
定数 username = user.username.toLowerCase();

厳密なプロパティ初期化では、コンストラクター内のすべての可能なコード パスですべてのプロパティを明示的に割り当てる必要があることに注意してください。 したがって、次のコードは、ユーザー名プロパティを初期化されていない状態に割り当てるケースがあるため、型が正しくありません。

クラスユーザー{
  // 型エラー: プロパティ 'username' に初期化子がありません
  // コンストラクター内で確実に割り当てられるわけではありません。
  ユーザー名: 文字列;

  コンストラクター(ユーザー名: 文字列) {
    もし(Math.random() < 0.5){
      this.username = ユーザー名;
    }
  }
}

解決策4: 明示的な割り当てアサーション

クラス プロパティが明示的に初期化されておらず、型も undefined でない場合、型チェッカーでは、プロパティがコンストラクター内で直接初期化されることを要求します。そうでない場合、厳密なプロパティ初期化チェックは失敗します。ヘルパー メソッドでプロパティを初期化する場合、または依存性注入フレームワークでプロパティを初期化する場合、これは問題になります。このような場合、プロパティの宣言に明示的な割り当てアサーション (!) を追加する必要があります。

クラスユーザー{
  ユーザー名!: 文字列;

  コンストラクター(ユーザー名: 文字列) {
    this.initialize(ユーザー名);
  }

  プライベート初期化(ユーザー名: 文字列) {
    this.username = ユーザー名;
  }
}

const user = 新しい User("mariusschulz");

// わかりました
定数 username = user.username.toLowerCase();

ユーザー名プロパティに明確な代入アサーションを追加することで、型チェッカーは、ユーザー名プロパティが初期化されることを予期していることを、たとえ独自に検出できない場合でも伝えます。コンストラクターが返された後にプロパティを明示的に割り当てることが私たちの責任となるため、注意が必要です。そうしないと、ユーザー名プロパティが明示的に未定義になったり、実行時に TypeError が発生したりする可能性があります。

明示的な代入アサーション

私たちは型システムをできるだけ表現力豊かにしようとしていますが、TypeScript よりもユーザーの方が型を理解しやすい場合があることも認識しています。

前述のように、明示的な割り当てアサーションは、プロパティに値が明示的に割り当てられることを TypeScript に伝えるために使用する新しい構文です。 しかし、クラス プロパティで使用することに加えて、TypeScript 2.7 では変数宣言でも使用できるようになりました。

x!: 数値[]とします。
初期化します。
プッシュ(4)

関数初期化() {
    0, 1, 2, 3 の整数部分。
}

x の後に感嘆符を付けていなかった場合、TypeScript は x が初期化されていないと報告します。 遅延初期化または再初期化が必要なシナリオで使用すると便利です。

以上がTSの数値区切りとより厳密なクラス属性チェックの詳細な説明です。TSの詳細については、123WORDPRESS.COMの他の関連記事にも注目してください。

以下もご興味があるかもしれません:
  • TypeScript ジェネリックパラメータのデフォルト型と新しい厳密なコンパイルオプション
  • JavaScript のプライベート クラス フィールドと TypeScript のプライベート修飾子の詳細な説明
  • TypeScript における型保護の詳細な説明
  • TypeScriptの型保護メカニズムについての簡単な説明
  • TypeScript 型宣言の書き方の詳細な説明
  • TypeScript 基本データ型
  • TypeScript 強制型変換の学習
  • TypeScript 型推論

<<:  MySQLデータベースの共通操作スキルのまとめ

>>:  Windows Server 2016 でサービスを展開する方法 (グラフィック チュートリアル)

推薦する

Apple M1チップにnginxをインストールし、vueプロジェクトをデプロイする詳細な手順

nginx をインストールApple Mac ではインストールに brew を使用します。brew ...

Vue印刷機能を実装する2つの方法の概要

方法1: npm経由でプラグインをインストールする1. npm install vue-print-...

Centos8でdockerがインストールできない問題の解決方法

問題 [root@zh ~]# [root@zh ~]# [root@zh ~]# yum -y d...

MySQL 5.7.13 ソースコードのコンパイル、インストール、および構成方法のグラフィックチュートリアル

インストール環境: CentOS7 64ビットMINI版公式ソースコードのコンパイルおよびインストー...

MySQL でトランザクションのコミットとロールバックを実装する方法の詳細な例

最近、データベース データのスケジュールされた移行を実行する必要があります。実行プロセス中に何らかの...

jsonファイルの書き方の詳細説明

目次JSONとはなぜこの技術なのでしょうか? JSONの使い方- データ形式- メモ- JSには2つ...

nginx + fastcgi を使用して画像認識サーバーを実装する

背景ディープラーニング モデルの推論には、特定のデバイスが使用されます。マシンは、モデルの読み込み、...

Linuxシステムはポート3306、8080などを外部に開放します。ファイアウォール設定の詳しい説明

多くの場合、Linux システムに Web サービス アプリケーション (Tomcat、Apache...

MySQL におけるデータタイムとタイムスタンプの違い

MySQL には 3 つの日付型があります。日付(年-月-日)テーブル test(hiredate ...

Vue のクロスドメイン問題の処理と解決策の概要

ネットワークリクエストを送信すると、次の保存情報が表示されます。おめでとうございます。ドメインを越え...

入力のid属性とname属性の違いの例

長い間ウェブサイトを作ってきましたが、入力時のnameとidの違いがまだわかりません。最近jQuer...

HTML割引価格計算の実装原理とスクリプトコード

コードをコピーコードは次のとおりです。 <!DOCTYPE HTML PUBLIC "...

Tomcat サーバーの応答が遅い場合の解決策

1. 分析的思考1. 機械自身の理由を排除する2. サーバーパフォーマンス分析3. プロジェクト自体...

NodeJS は画像テキスト分割を実現します

この記事では、画像テキストセグメンテーションを実装するためのNodeJSの具体的なコードを参考までに...

mysql5.7.19 winx64 解凍版のインストールと設定のチュートリアル

mysql 5.7.19 winx64解凍版のインストールチュートリアルを収録しました。具体的な内容...