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 でサービスを展開する方法 (グラフィック チュートリアル)

推薦する

Ubuntu 20.04にSogou入力方式をインストールする詳細な手順

1. Fcitx入力フレームワークをインストールする関連する依存ライブラリとフレームワークは自動的に...

Windows2008 64 ビット システムでの MySQL 5.7 グリーン バージョンのインストール チュートリアル

序文この記事では、MySQL 5.7 グリーン バージョンのインストール チュートリアルを紹介します...

MySQL InnoDB テーブルスペース暗号化の例の詳細な説明

序文MySQL 5.7.11 以降、MySQL は、別の表領域に格納された InnoDB テーブルの...

JavaScript 関数構文の説明

目次1. 通常の機能2. 矢印関数3. データパケットJSON 4. オブジェクト5. 約束6. 非...

Vueナンバープレート入力コンポーネントの使い方の詳しい説明

参考までに、シンプルなナンバープレート入力コンポーネント(vue)です。具体的な内容は次のとおりです...

ウェブサイトのパフォーマンス: 画像とCookieの最適化、モバイルアプリケーションの最適化

前のセクションでは、コンテンツ、サーバー、JavaScript、CSS など、Web サイトのパフォ...

Win10 の Linux サブシステムを有効にする方法を説明します (詳細な画像とテキスト付き)

今日は、Windows 10 で Linux サブシステムを有効にする方法を紹介します。早速、手順を...

Vue の計算プロパティとプロパティリスニングについての簡単な説明

目次1. 計算プロパティ構文: 1. 省略形:文法: 2. 文章を完成させる: 2. モニタリング(...

JavaScript ツールチェーンの不完全なガイド

目次概要静的型チェックコードスタイルチェック(Linter)パッケージマネージャーモジュールローダー...

77.9K の GitHub リポジトリを持つ Axios プロジェクト: 学ぶ価値のあることは何でしょうか?

目次序文1. Axiosの紹介2. HTTPインターセプターの設計と実装2.1 インターセプターの紹...

ウェブサイトに天気予報を挿入する方法

天気予報をウェブサイトに挿入すると、次のような効果が得られます。次のコードを挿入する必要があります:...

ウェブページ制作時のコードコメントの書き方

<br />私の仕事で使用しているアノテーションの書き方の基準をまとめました。技術的な内...

Centos7にnginxをインストールする方法

必要な環境をインストールする1. gccのインストールnginx をインストールするには、公式サイト...

3次元画像配置効果を実現する純粋なCSSのサンプルコード

1. 要素の幅/高さ/パディング/マージンのパーセンテージ基準要素の幅/高さ/パディング/マージンの...

HTML テーブル マークアップ チュートリアル (6): 暗い境界線の色属性 BORDERCOLORDARK

表では、右下の境界線の色を個別に定義したり、セルの左上の境界線の色を定義したりできます。これら 2 ...