Published on

TypeScript基礎

Authors
  • avatar
    Name
    Kikusan
    Twitter

公式: https://www.typescriptlang.org/docs/

install

npm install -g typescript
  • コンパイルするには
tsc hello.ts
tsc --outFile myApp.js module.ts module2.ts

webpackでのTypeScriptの使用

npm init -y
npm i -D webpack webpack-cli
npm i -D @babel/core @babel/preset-env babel-loader
npm i -D typescript @babel/preset-typescript
tsc --init
  • babel.config.js
module.exports = {
  presets: [
    '@babel/preset-env',
    '@babel/preset-typescript'
  ],
}
  • webpack.config.js
const path = require('path');

module.exports = {
  mode: process.env.NODE_ENV || 'development',
  entry: './src/app.ts', // 読み込みエントリファイル
  output: {
    filename: 'main.js', // 出力ファイル
    path: path.resolve(__dirname, 'dist'), // 自フォルダ
  },
  resolve: {
    extensions: ['.js', '.ts']
  },
  module: {
    rules: [
      {
        // js/tsを対象
        test: /\.(j|t)s$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader', // babel使用でビルド
        }
      }
    ]
  },
  cache: true
};
  • package.json
{
  "name": "typescript",
  "scripts": {
    "build": "webpack",
    "dev": "webpack --watch", 
    "lint:ts": "tsc --noEmit"
  },
  "devDependencies": {
    "@babel/core": "^7.17.2",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-typescript": "^7.16.7",
    "babel-loader": "^8.2.3",
    "typescript": "^4.5.5",
    "webpack": "^5.68.0",
    "webpack-cli": "^4.9.2"
  }
}

./src/app.tsを記載して以下でビルド

npm run lint:ts # コンパイルエラー検知
npm run dev # ライブコンパイル
npm run build # コンパイル

実行

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <script src="dist/main.js"></script>
</body>
</html>

構文

let int : number = 0;
// 数値リテラル 0xFF : 16進数, 0o66: 8進数, 0b1011: 2進数
let str : string = '';
console.log(typeof str); // number
let bool : boolean = true;
let sym : symbol = Symbol();
// 基本的にanyは使わないこと
let a : any;
a = 1; 
console.log(typeof a); // number
a = 'str'; // any型では型チェックが停止する
console.log(typeof a); // string
let b; // 型/初期値を省略してもany型になる(undefined,nullが初期値でも)
b = 1;
b = 'str'; 

// tsconfigのnoImplicitAnyオプションが有効だと引数などでanyは使えない
function greeting(name: string) { 
  return `Hello ${name}!`;
}
console.log(greeting("Taro"));

リテラル型

// タプルや共有型にtype で型エイリアスをつけられる
type MyType = [ string, number, boolean];

let data: MyType = ['abc', 1, false];

// 入れられるものの制限みたいなのもできる
type Season = 'spring' | 'summer' | 'autumn' | 'winter';
function getSeason(s: Season) {
  return s.toUpperCase();
}
console.log(getSeason('spring')); // SPRING
// 型注釈はオブジェクト型リテラルも使える
let a2: {
  kind: string;
  weight: number;
};
let a3: {
  kind: string,
  weight: number,
};

テンプレートリテラル型

// 文字列を型として設定できる。ここではyyyy-MM-dd以外受け付けなくなる
type DateFormat = `${number}-${number}-${number}`;
const date: DateFormat = '2022-08-30';

共有型と型ガード

// 共有型
let data: string | boolean; // 共有型: |でどれかを指定できる

function guardFunc(value: string | number) {
  // プリミティブ型の判定はtypeof オブジェクトはinstanceof
  if (typeof value === 'string') {
    return value.toUpperCase(); // 型ガード: typeofで型指定することでコンパイルエラーが起きない
  } else {
    return value.toFixed(1);
  }
}

// 引数 is 型名 とすることで型ガードを関数で行える。
function isContains(inf: string | number): inf is string {
  return (inf as string).includes("a"); // bool値を返す
}

if (isContains("aaa")) {
  console.log("is contain");
} else {
  console.log("is not contain");
}
// unknown型はなにでも入れられるが型ガードがないと操作ができない
let data: unknown = 'Hoge'; 

if (typeof str === 'string') {
  console.log(str.trim());
}

演算子

// ?. はjavaでいうoptional.orElse(undefined) nullとundefinedでヒット
let result = hoge?.trim();

// ?? はjavaでいうoption.orElse(設定値)
result = hoge ?? 'x';

配列・連想配列・列挙体・タプル

// 配列
let ary: string[] = ['a', 'b', 'c'];
ary.push('d');
ary.forEach((data) => {
  console.log(data); // a // b // c
});
// readonlyキーワードでは読み取りしかできなくなる
const ary2: readonly number[][] = [[1,2]];
// ary2.push([3,4]); readonlyなのでできない
ary2[0].push(3); // 中身に関してはできる。
console.log(ary2[0][2]); // 3
// 連想配列
let obj: {[index: string]: number;} = {
  'a': 1,
  'b': 2,
}
console.log(obj.a); // 1
console.log(obj['b']); // 2

列挙型

// 列挙
enum Nums {
  ZERO,
  ONE = 'one', // string か numberを割り当て可能
  TWO = 'two'
}

let n: Nums = Nums.ONE;
console.log(Nums.ZERO); // 0
console.log(n); // one
console.log(Nums[0]); // ZERO

タプル

let tuple: [string, number, boolean] = ['aaa', 10.5, false];
tuple.forEach((data) => {
  console.log(data); // aaa // 10.5 // false
});

関数

function greeting(name: string): string { 
  return `Hello ${name}!`;
}

// 戻り値なし
function voi(): void {}
// never ではループや例外で必ず終端に到達しない
function nev(): never {
  while(true) {}
}

// 関数リテラルは型推論で決まる。
const func2 = (num1: number, num2: number) => {
  return num1 + num2;
}

console.log(func2(1, 2)); // 3
// greeting() // 宣言した引数は必須

// 引数名?で省略可になる 
function showTime(time?: Date): string {
  if (time === undefined) {
    return (new Date()).toLocaleString();
  } else {
    return time.toLocaleString();
  }
}

// 引数にデフォルト値を与えられる
function showTime2(time: Date = new Date()): string {
  return time.toLocaleString();
}
// 可変長引数 ...
function sum(...values: number[]) {
  let result = 0;
  for (let value of values) {
    result += value;
  }
  return result;
}

console.log(sum(1, 2, -3, 10)); // 10
// オーバーロード
// 宣言の直前にシグニチャを記載する(型チェックのため)
function overload(value: number): void;
function overload(value: boolean): void;
function overload(value: any): void {
  // anyで受け取り、シグニチャの分だけ実装する
  if (typeof value === 'number') {
    // numberの場合
  } else {
    // booleanの場合
  }
}

オブジェクト指向

class Animal {

  // 静的メンバー
  public static LEGS: number = 4;

  // public, protected, private がjavaと同じく使える。
  private _name!: string; // !は初期化チェック回避
  #_age!: number; // #はprivateと同義

  // コンストラクター
  constructor(name: string, age: number) {
    this._name = name;
    this.#_age = age;
  }

  // 静的メソッド
  public static getLegs(): number {
    return this.LEGS; // staticでもthisが必要
  } 

  // getアクセサ
  get age(): number {
    return this.#_age;
  }

  // setアクセサ
  set age(age: number) {
    this.#_age = age;
  }

  describe(): string {
    return `${this._name}${this.#_age}歳です。`;
  }

  // メソッドチェーンの実装
  plusAge(value: number): this {
    // thisを返すことでメソッドチェーンできるようになる
    this.#_age += value;
    return this;
  }
}

let p = new Animal("dog", 10);
p.age = 10;
console.log(p.age);
console.log(Animal.getLegs()) // 4 
// 継承
class Dog extends Animal {

  protected kind: string;

  // コンストラクターオーバーライド
  constructor(name: string, age: number, kind: string) {
    super(name, age);
    this.kind = kind;
  }

  // 独自メソッド
  bark() {
    console.log('bowwow!');
  }

  // メソッドオーバーライド
  describe(): string {
    return super.describe() + `${this.kind}という種類です。`;
  }
}
// コンストラクター省略 メンバー作成と初期化を同時にできる
class Animal {
  constructor(private name: string, private age: number) {}
}

抽象クラスとインターフェイスはjavaと一緒。
javaと異なる点は、構造的部分型: 同じメソッドやメンバーがあれば、型互換性がある

抽象クラス

// 抽象クラス
abstract class Animal {
  // 抽象メソッド
  abstract sleep(): string; 
}

class Dog extends Animal {
  // 抽象メソッドは実装を強制される
  sleep(): string {
    return 'Dog is sleeping';
  }
}

インターフェイス

// インターフェイス
interface Animal {
  kind: string;
  sleep(): string;
}

class Dog implements Animal {
  sleep(): string {
    return 'Dog is sleeping';
  }
}

// インターフェイスは型注釈にも使える
const cat: Animal = {
    kind: 'cat',
    sleep(): string {
        return 'Cat is sleeping';
    }
}

シグニチャ

interface Animal {
  readonly name: string; // readonlyは読み取り専用プロパティ
  age?: number; // ?は省略可能を示す
}

//コールシグニチャ
interface Calc {
  (x: number, y:number): number;
}
let add: Calc = function (a: number, b: number): number {
  return a + b;
}

// インデックスシグニチャ: 繰り返し利用するデータ型に有効
interface HashMap {
  [index: string]: number; 
}
let map: HashMap = {
  'str1': 1,
  'str2': 2,
};

// コンストラクタシグニチャ
interface Animal {
  new(name: string): Dog;
}
class Dog {
  constructor(private name: string){}
}

ジェネリクス

// <>でジェネリクスが使える。_を入れてるのはjsxだと認識されないため
const toArray = <T, _>(arg1: T, arg2: T): T[] => [arg1, arg2];
console.log(toArray(8, 3)); // [8,3]

モジュールシステム

ES Modulesに準拠。
ただし拡張子は記載しない

探索されるのは、.ts, .tsx, .d.ts, package.jsonのtypesで期待したファイル, /index.ts, /index.tsx, index.d.ts

外部のモジュールはJavaScriptで配布されているものがほとんど。
これをTypeScriptで読み込むための型情報が入っているのが、.d.tsファイル。