blog

TypeScriptクイックスタート

自分の型システムを理解するこの強い型と弱い型の区別は、ある権威によって定義されたものでは全くないので、一般的な説明としては、強い型にはより強い型制約があり、弱い型にはほとんど制約がありません。 初期の...

Mar 28, 2020 · 12 min. read
シェア

TypeScriptクイックスタート、JavaScript独自の型システムとその問題点を理解する ( この記事のコンテンツ出力元: "プルフック教育大前提ブートキャンプ")

型システムの理解

JavaScript独自の型システムを理解する問題。次の型システムを理解することから始めましょう。

  • 強い型と弱い型

強い型付き:関数の実パラメータの型は、形式パラメータの型と同じでなければならないという言語レベルの制約。

class Main{
	static void foo(int num){
		System.out.println(num);
	}
	//Main.foo(100)// 
	//Main.foo("100")// 
}

弱い型付け:言語レベルでは実パラメータの型に制限はありません。

function foo(num){
	console.log(num)
}
//構文はエラーを報告しないので、任意の型を渡すことができる。
foo(100);//ok
foo("100");//ok

この強い型と弱い型の区別は、ある権威によって全く定義されていないため、強い型には強い型制約があり、弱い型には制約がほとんどないと一般的に説明されています。

強い型付け言語では任意の暗黙的な型変換は許されませんが、弱い型付け言語ではデータの任意の暗黙的な型変換が許されます。

強い型と弱い型の違いではなく、いつでも変更できる変数型の特徴

  • 静的型と動的型

静的型:宣言時に変数の型が明示され、宣言後はその型を変更することはできません。

動的型:変数の型は実行時にのみ指定でき、変数の型はいつでも変更できます。

  • JavaScript独自の型システムの問題点

JavaScriptは弱く型付けされ、動的に型付けされる言語であり、型システムの気まぐれな不在は信頼できません。

初期のJavaScriptアプリケーションはシンプルで、JavaScriptはスクリプト言語であり、JavaScriptの柔軟性と汎用性の利点の大規模なアプリケーションではコンパイルリンクがない、短いボードになりました。

弱いタイプの問題:

小規模なプロジェクトであれば慣例で設定できますが、大規模なプロジェクトではこの慣例が大きな落とし穴になることがあります。

//JavaScript 弱い型の問題
const obj = {}
//これは隠れた問題で、強く型付けされた言語は構文レベルで直接エラーを投げる。
setTimeout(() => {
 obj.foo();
}, 1000);
//異なる型のパラメータを渡すと、関数の効果はまったく異なるものになるし、強く型付けされた言語を使えば、パラメータの型を制限することができる。
function sum(a,b){
 return a+b;
}
sum();//200
sum(100,"100");//
//オブジェクトのプロパティ名は自動的に文字列に変換される。
const obj = {};
obj[true] = 100;
console.log(obj['true']);//オブジェクトインデクサの誤用

強いタイプのアドバンテージ:

  • バグは先に公開されています
  • よりスマートなコード、より正確なコード
  • リファクタリングはより安全です
  • 不必要なタイプ判定の削減

Flow

フロー静的型検査プログラム

yarn add flow-bin --dev フローの設置

コードにマークアップを追加:// @flow

yarn flow init フローの初期化

yarn flow flowコマンドを実行してコード内の型の問題を検出します。

/* 
@flow
*/
function s(num:number):void{
 console.log(num);
}
s(123);

JavaScriptはコンパイルフェーズを持たず、ただ実行するだけです。Flowは型エラーをチェックするためにJavaScripにコンパイルフェーズを追加するだけです。

  • flow アノテーションを削除するコンパイル

一般的に、アノテーションタイプは開発段階でのみ使用され、オンライン環境では必要ないため、オンライン環境ではアノテーションを削除する必要があります。

モジュールを取り付けます:yarn add flow-remove-types --dev

コマンドを実行します:yarn flow-remove-types src -d dist

これによって型アノテーションが削除され、以下のコードが生成されます。

function sum(a:number,b:number){
	return a + b;
}
//flow コンパイル後のコード 型アノテーションを削除する
function sum(a , b ) {
 return a + b;
}
sum();
sum('100','100');
  • babel

yarn add @babel/core @babel/cli @babel/preset-flow --dev removeアノテーション・タイプのコンパイルを有効にするために、babelモジュールをインストールします。

.babelrcファイルの設定

{
 "presets": ["@babel/preset-flow"]
}

次に、次のように実行します: yarn babel src -d dist srcのソースコードから型アノテーションを削除し、それをdistディレクトリに置きます。

  • flow 開発ツールのプラグイン
    /* 
     生の型
     @flow
    */
    const a:string = 'foobar';
    const b:number = 100;
    const b1:number = NaN;
    const b2:number = Infinity;
    const c:boolean = true;
    const d:null = null;
    const e:void = undefined;
    const f:symbol = Symbol();
    function s():void{
     
    }
    function ss():number{
     return 100;
    }
    
    • 生の型
    /* 
    配列の型
    @flow
    */
    const arr:Array<number> = [1,2,3];//<T>  
    const arr2:number[] = [1,2,3,4];
    //固定長配列
    const foo:[string,number] = ['foo',123];//最初の要素は文字列、2番目の要素は数値でなければならない。
    
    • 配列の型
    /* 
    オブジェクトの型
    @flow
    */
    //オブジェクトのメンバ型を次のように定義する
    const obj:{foo:string,bar:number} = {
     foo:'string',bar : 123
    }
    //メンバ型の上記の定義は、メンバが定義されている必要があり、そうでない場合は、エラーを報告することができる?:メンバを定義する必要はない
    const obj1:{foo?:string,bar:number} = {
     bar:123
    }
    //オブジェクト属性のキーと値に型制限を設定する。
    const obj2:{[string]:string}={}
    obj2.key='value';
    obj2.age= '14';
    
    • オブジェクトの型
    /* 
    関数の型
    @flow
    */
    //関数のパラメータ型と戻り値の型を設定する
    function s(num:number):void{
     console.log(num);
    }
    s(123);
    function ss():number{
     return 100;
    }
    //コールバック関数(文字列)の型制限,number)=>void パラメータ型と戻り値型
    function foo(callback:(string,number)=>void){
     callback('zce',123);
    }
    foo(function(name,num){
     console.log(name,num);
    });
    
    • 関数の型
    /* 
    特殊な型
    @flow
    */
    //a型のリテラルはfooと等しくなる
    const a:'foo'='foo';
    //type  'success' | 'warning' | 'danger'
    const type:'success' | 'warning' | 'danger' = 'success';
    //複数の型を使うこともできる
    type StringOrNumber = string | number;
    const b : StringOrNumber = '12';//100
    const gender:?number = 100;//? maybe型は未定義のnullを受け入れることができる
    
    • 特殊な型
    /* 
    任意の型
    @flow
    */
    //mixed 強い型はどんな型でも渡すことができる。
    function passMixed(value:mixed){
     //型判定を追加する必要がある
     if (typeof value === 'number') {
     value * value;
     }
     if (typeof value === 'string') {
     value.substr(1);
     }
     
    }
    passMixed(200);
    passMixed('100');
    //any 弱い型はまた、任意の型を表すことができる一般的に、任意の型を使用することは推奨されない
    function passAny(value:any){
     //anyどのようなメソッドの値でも、実際にはまだ弱い型である。
     value * value;
     value.substr(1);
    }
    passAny(200);
    passAny('100');
    
    • 任意の型
    /* 
    ランタイム環境API型
    https://.//////.js
    @flow
    */
    //DOM BOM Nodelodashの組み込みAPIにはいくつかの型制限がある。
    const element:HTMLElement | null = document.getElementById("id");
    

    上記はFlowが提供する静的な型チェックの仕組みですが、実はTypeScriptも似たような書き方をしています。

    TypeScript

    TypeScriptはJavaScriptの型システムの問題を解決し、コードの信頼性を大幅に向上させます。

    TypeScriptはどのJavaScript実行環境でもサポートされます。

    デメリットその1:言語自体の概念が多い

    デメリット2:プロジェクト開始時に追加費用が発生すること

    TypeScript はプログレッシブに属します。

    TypeScriptは最終的にJavaScriptにコンパイルされ、ES3標準をサポートする予定です。

    yarn add typescript --dev typescriptモジュールをプロジェクトに追加

    yarn tsc 01-getting-start.ts tsファイルのコンパイル tscコマンドはtsファイルをコンパイルします。

    /* 
     完全にjavascriptのコーディングに従うことができる
    */
    const hello = (name:string) => {
     console.log(`hello ${name}`);
    }
    hello('zce');
    

    コンパイルされたファイルは以下の通り:

    "use strict";
    /*
     完全にjavascriptのコーディングに従うことができる
    */
    var hello = function (name) {
     console.log("hello " + name);
    };
    hello('zce');
    //# sourceMappingURL=01-getting-start.js.map
    
    • ランタイム環境API型

    プロジェクト全体をコンパイルし、設定ファイルを作成します。

    設定ファイル:TS 他にも様々な設定がありますので、公式サイトで確認してみてください。 よく使われる設定は以下の通りです。

    "target": "es5",//標準的なjsコードをコンパイルする
    "module": "commonjs",// 
    "outDir": "dist",//コンパイルされたディレクトリ
    "rootDir": "src",//ソースコードディレクトリ
    "sourceMap": true,//ソースコードのマッピング
    "strict": true,//すべての厳密なチェックオプションを有効にする
    "lib": ["ES2015","DOM"],//標準ライブラリの設定 新しいES2015標準を使用する場合は、DOM、BOMなどを含む対応する標準ライブラリを導入する必要がある。
    

    コマンド :yarn tsc はプロジェクト全体をコンパイルし、コンパイルされたjsファイルはdistディレクトリに保存されます。

    • TypeScript 設定ファイル
    /* TSのプリミティブ型の応用*/
    const a:string = 'foo';
    const b:number = 100;//NaN Infinity
    const c:boolean = true;//false
    // const d:string = null;//厳密モードでは、nullは許されない。
    const e:void = undefined;//厳密モードでは、nullは許されない。
    const f:null = null;
    const g:undefined = undefined;
    //ES5標準ライブラリを使用した後、ES6標準ライブラリを使用すると、新しい型がエラーになる。
    //解決策1:ES2015標準ライブラリに変更する;
    //解決策2:"lib": ["ES2015","DOM"]
    const h:symbol = Symbol();
    Array;
    Promise;
    //DOM APIDOMはlibに導入する必要がある。
    console.log();
    //標準ライブラリは、組み込みオブジェクトの宣言である。
    
    • オブジェクトタイプ
    export{}
    const foo:object = function(){}//[] {} は、オブジェクト、配列、関数を受け入れることができる。
    //オブジェクトのメンバ型の制限
    const obj:{foo:number,bar:string} = {foo:123,bar:"string"};
    
    • Object
    const arr1:Array<number> = [1,2,3,];
    const arr2:number[] = [1,2,3,];
    //--------- 
    function sum(...args:number[]){
     //パラメータがNumber型かどうかを判断するためにjsを使用する場合。
     //ts型注釈の数を追加することができる
     return args.reduce((prev,current)=>prev + current,0);
    }
    // sum(1,2,3,4,'foo');
    
    • 配列の型

    タプルは要素の数と型を指定する型です。 要素の型は同一である必要はありません。

    const tuple:[number,string] = [18,''];//型が一致しなかったり、要素数を超えたりするとエラーが報告される。
    const [age,name] = tuple;
    //タプル型は現在非常に一般的になっている
    //例えば、entriesはキーと値の配列を取得し、キーと値の配列はタプルになる。[string,number]
    //なお、entriesはES2017の標準ライブラリである。
    Object.entries({
     foo:123,
     bar:456
    });
    
    • タプル型

    一般的な定義型はこう書きます:

    const PostStatus={
     Draft:0,
     Unpublished:1,
     Published:2
    }
    

    列挙型の記述

    //デフォルト値は:0など、累積の値を指定することはできない
    //列挙は文字列にできるが、文字列は数値のように大きくならないので、列挙ごとに値を割り当てる必要がある。
    //定数列挙と基本列挙はコンパイルが異なる。
    const enum PostStatus{
     Draft,//0
     Unpublished,//1
     Published//2
    }
    const post={
     title:"Hello TypeScript",
     content:"",
     status:PostStatus.Draft//0 1
    }
    // PostStatus[0]// => Draft
    
    • 列挙型
    function stringify(value:any){
     return JSON.stringify(value);
    }
    stringify('100');
    stringify(100);
    //any 動的型はtsによって型チェックされない。
    let foo:any = 'string';
    foo = 100;
    foo.bar();
    
    • 任意の型
    //オプションやデフォルトのパラメータは、パラメータリストの最後になければならない。
    function func1(a: number, b?: number,...rest:number[]): string {
     return "func1";
    }
    func1();
    func1(100);
    // func1();
    

    関数式、コールバック関数の制約はTSでこのように定義できます:(a:number,b:number) => string

    //関数式
    //コールバック関数の制約
    const func2 : (a:number,b:number) => string = function(a:number,b:number):string{
     return 'func2';
    }
    
    • 関数の型

    TypeScriptは自動的に型を推薦することができ、一度決定された型を変更することはできません。

    let age = 18;//数に対して推論される
    // age = 'number';// 
    let foo;//どの型にも代入されない。
    //値の構文の任意の型を与えることができるエラーを報告しない
    foo = 100;
    foo = 'string';
    //各変数にはより直感的な型を追加することが推奨される
    
    • 暗黙の型推論
    const nums = [,];
    const res = nums.find(i=>i>1000);//resが見つからないことを推測する
    // const square = res * res;
    const num1 = res as number;//コンパイラに、私は番号でなければならないことを伝える、心配しないで。
    const num2 = <number>res;
    
    • 型宣言

    インターフェイスはオブジェクトの構造について合意することができ、どのようなメンバーを持つかについて合意することができます。

    interface Post {
     title: string;
     content: string;
     //オプションのメンバ
     subtitle?: string;
     //読み取り専用メンバー
     readonly summary:string;
    }
    function printPost(post:Post){
     console.log(post.title);
     console.log(post.content);
    }
    //subtitleオプション属性は渡すことができない
    printPost({title:'hello',content:'a javascript',summary:'12'});
    const hello: Post = {
     title: "hello",
     content: "a javascript",
     summary:'A JavaScript'
    };
    hello.summary = "123";//エラーsummary属性は読み取り専用属性であるため、属性の値を変更することはできない。
    

    ダイナミック・メンバのキー型と値型の定義に一貫性がないと、エラーが発生します。

    interface Cache{
     [prop:string]:string //[prop:string] -> keyの型は:string -> 値の型
    }
    const cache:Cache = {}
    cache.foo = "value";
    // cache.bar = 123;// 
    
    • インターフェイス

    クラス 物のクラスの抽象的な特徴を記述 ES6以前は関数+プロトタイプでモデル化されたクラス

    class この機能はES6で追加されたもので、TypeScriptではES6の上にclassへのアクセス修飾子が追加され、class属性は最初に宣言され、初期値を持たなければなりません。

    class Person{
     //クラス属性は初期値を持つか、コンストラクタで値を代入しなければならない。
     public name:string;//デフォルトのパブリックプロパティ
     private age:number;//private プライベート属性はクラス内部からのみアクセスできる。
     protected readonly gender:boolean;//protected サブクラスのみがアクセスできる
     //readonly による読み取り専用属性=あるいはコンストラクタは初期化され、変更が許されない。
     constructor(name:string ,age:number){
     //this "を直接使う。.name はエラーを報告する TS requires explicit declaration of attributes 宣言された属性は初期値を持たなければならない。=またはコンストラクタで値を代入する。
     this.name = name;
     this.age = age;
     this.gender = true;
     }
     sayHi(msg:string){
     // this.gender = false;
     console.log(`I am ${this.name} is age:${this.age}`);
     }
     logSay(){
     this.sayHi(' ');
     }
    }
    class Student extends Person{
     //コンストラクタがprivate宣言されていると、外部はそのクラスを好きなだけインスタンス化することができない。
     private constructor(name:string,age:number){
     super(name,age);
     console.log(this.gender);
     //
     // this.gender = false;
     }
     //内部の newインスタンスを介して外部呼び出しに返すことができる
     static create(name:string,age:number){
     return new Student(name,age);
     }
    }
    const tom = new Person('tom',18);
    console.log(tom.name);
    // console.log(tom.gender);
    // console.log(tom.age);
    // const jake = new Student('jake',18);
    const jake = Student.create('jake',18);
    

    クラスがインターフェイスを実装している場合、コードは次のようになります:

    interface Eat{
     eat(foo:string):void
    }
    interface Run{
     run(distance:number):void
    }
    class Person implements Eat,Run{
     eat(foo:string):void{
     console.log(`優雅に食べる${foo}`);
     }
     run(distance:number):void{
     console.log(`直立歩行${distance}`);
     }
    }
    class Animal implements Eat,Run{
     eat(foo:string):void{
     console.log(`GulpとEat${foo}`);
     }
     run(distance:number):void{
     console.log(` ${distance}`);
     }
    }
    

    抽象クラスはメソッドの具体的な実装を含むことができます。

    abstract class Anima{
     eat(foo:string):void{
     console.log(` ${foo}`);
     
     }
     //抽象メソッド
     abstract run(d:number):void;
    }
    class Dog extends Anima{
     //親クラスの抽象メソッドを実装する
     run(d: number): void {
     console.log(` :${d}`);
     }
    }
    
    • クラス

    TypeScriptはジェネリックスの概念も導入しています。

    function crateNumberArray(len:number,value:number):number[]{
     //<number> 数値型のデータを格納する
     const arr = new Array<number>(len).fill(value);
     return arr;
    }
    const res = crateNumberArray(3,100);
    //res => [,100]
    //文字列を作る
    function crateStringArray(len:number,value:string):string[]{
     //<number> 数値型のデータを格納する
     const arr = new Array<string>(len).fill(value);
     return arr;
    }
    //上記のコードの冗長性は、ジェネリック型を使うことで解決される。
    function createArray<T>(len:number,value:T):T[]{
     const arr = new Array<T>(len).fill(value);
     return arr;
    }
    createArray<number>(1,100);
    createArray<string>(10,'10');
    

    TSとJSの混合開発は、古いコードや他のロジックを含む、ここではこのようなサードパーティ製のモジュールの一般的な使用は、モジュールの型宣言を使用する必要があるこの時間のtsの実装を使用していないような型宣言を使用する必要があります。

    例えば、lodashのサードパーティライブラリは型宣言ファイルを提供します。 npm install @types/lodash

    import {camelCase} from 'lodash';
    //query-string すでに型宣言を含むモジュール
    import qs from 'query-string';
    // qs.parse();
    //関数の型を宣言する 関数の型を明示的に宣言する
    // declare function camelCase (input:string) : string;
    //npm install @types/lodash または型宣言モジュールをインストールする
    const r = camelCase('hello worle');
    
    Read next

    Javaの並行処理:notifyと気持ちの使用に関する条件

    よりシンプルに考えるために、プログラムには10回のサイクルを設定せず、2回のサイクル、つまり2つのプラス1スレッドと2つのマイナス1スレッドの作成のみを設定することを前提としています。それぞれ便宜上、プログラムを分析するために:スレッド3、スレッド4という名前の2つのマイナススレッド。 12は、プログラムが今から終了することはできません、スレッド2とスレッド4は、それ以来ブロックされている.... 分析すると、なぜプログラム...

    Mar 28, 2020 · 5 min read