let name: type[= initial]
--------------------------
- name: 変数名
- type: 変数の型
- initial: 初期値
ex1)
let age: number = 20;
console.log(age);
型をチェックするので、number型の変数に文字を保存することはできない。
let age: number = 20;
age = 'ハタチ';
// エラー
// boolean型
let flag: boolean = false;
// number型
let age: number = 30; // 10進数
let binary: number = 0b11; // 2進数
let exp: number = 1.23e-5; // 指数表現
// string型
let name: string = '田中太郎';
// string型(テンプレート文字列)
let meibo = `名簿名: ${name}`;
console.log(meibo); => 田中太郎
// 配列型
let os: string[] = ['masOS', 'windows', 'linux'];
console.log(os[2]); => linux
// オブジェクト型(連想配列)
let duet: { [index: string]: string } =
{ first: '田中', second: '鈴木'};
console.log(duet['first']);
連想配列のインデックス/値型を指定できることをTypeScriptではインデックスシグニチャという。
{ [index: string]: string }
インデック型 値型
インデックスシグニチャというが、indexの部分は仮の名前なので、keyとかでもいい。
インデックスシグニチャの記述は、ある特定の機能を実現するために必要なメソッドのシグニチャ(名前や引数、戻り値の型)だけを定義し、処理そのもは持たないinterface命令として切り出すこことも可能。
interface MyMap {
[index: string]: string;
}
let duet: MyMap = { first: '田中', second: '鈴木' };
// タプル型
タプルとは、データ構造の一つでリストと同じように複数の値を持つことができます。
- イミュータブル(変更ができない)
- 実行速度が少し早い
let score: [number, string] = [100, 'great'];
console.log(score[0]); => 100
console.log(score[1]); => great
// enum型(列挙体)
enum型(列挙体)は、複数の変数に一連の整数値を付けると便利な場合に使用します。
enum Color { Red, Green, Blue };
let red: Color = Color.Red;
console.log(red); => 0
console.log(Color[red]); => Red
const name: type = value
--------------------------
- name: 定数名
- type: 定数の型
- value: 定数値
ex1)
const PI: number = 3.14;
PI = 3.141 // 再代入しようとするとエラー
型キャスト
。型変換。
<type> variable
--------------------------
- type: 変換する型
- variable: 型変換する変数
ex1)
function greet(name: string) {
return 'こんにちは、' + name;
}
// string型の引数nameに数値を渡す(any型にキャスト)
console.log(greet(<any>108));
// as構文でも置き換え可
// <...>の代わりにas構文もok
console.log(greet(108 as any));
function name(arg : type, ...) : r_type {
...body...
}
--------------------------
- name: 関数名
- arg: 仮引数
- type: 引数の型
- r_type: 戻り値の型
- body: 関数の本体
ex1)
// 引数・戻り値なし void型は値がないことを意味する型。
function greet(): void {
console.log('Hello');
}
greet(); => Hello
// 引数・戻り値あり
function add(x: number, y: number): number {
return x + y;
}
console.log(add(4, 5));
// 引数を省略可能にするには、仮引数の末尾に?をつける。
function showCost(price: number, discount?: number) {
if (discount) {
console.log(`価格価格: ${price - discount}`);
} else {
console.log(`価格価格:${price}`);
}
}
showCost(1000);
showCost(1000, 200)
// 仮引数の末尾に?をつけ忘れるとエラーになる
function showCost(price: number, discount: number) {
if (discount) {
console.log(`価格価格: ${price - discount}`);
} else {
console.log(`価格価格:${price}`);
}
}
showCost(1000); => Expected 2 arguments, but got 1.
showCost(1000, 200)
// 引数のデフォルト値
デフォルト値を指定した場合、引数は無条件に省略可能となるので、?は不要。指定したらエラー。
function showCost(price: number, discount: number = 0) {
if (discount) {
console.log(`価格価格: ${price - discount}`);
} else {
console.log(`価格価格:${price}`);
}
}
showCost(1000);
showCost(1000, 200)
// 可変長引数 「...」
function sum(...values: number[]): number {
let result: number = 0;
// 可変長引数引数valuesは配列として処理できるできる
for (let i: number = 0; i < values.length; i++) {
result += values[i];
}
return result;
}
console.log(sum(100, 200, 300));
(arg : type, ...) : r_type => {
...body...
}
--------------------------
- arg: 仮引数
- type: 引数の型
- r_type: 戻り値の型
- body: 関数の本体
ex1)
// 以下をアロー関数で書き直すと
function add(x: number, y: number): number {
return x + y;
}
let add = (x: number, y: number): number => {
return x + y;
};
引数が1つで、引数・戻り値の型宣言がない場合は、引数を囲むカッコも省略できるが、特段使う必要もなさそう。
アロー関数では宣言時にthisが固定されるという性質があるので、出来るだけfunction命令よりもアロー関数を優先して利用しよう。
function name(arg : type, ...) : r_type;
function name(arg : type, ...) : r_type;
...
function name(arg : type, ...) : r_type; {
...body...
}
--------------------------
- name: 関数名
- arg: 仮引数
- type: 引数の型
- r_type: 戻り値の型
- body: 関数の本体
関数のオーバーロード
とは、名前は同じで、引数の型、並びだけが異なる関数を複数定義すること。ただし、TypeScriptのオーバーロードは構文は、まず、実装(本体)を持たないシグニチャだけの関数を列挙し、最後に全てのオーバーロードに対応できる万能の関数を本体付きで記述する。
型判定をcase文で書いているみたい?
function search(str: string, start: number): string;
function search(str: string, start: string): string;
function search(str: string, start: any): string {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'いろはにほへとちりぬる';
console.log(search(msg, 3));
console.log(search(msg, 'と'));
// 変数variable型type1かtype2いずれかである
variable: type1 | type2
--------------------------
- variable: 変数
- type1、2: 型
共用型
とは、変数に対して、複数の型を紐付けされた型のこと。
ex1)
function search(str: string, start: number): string;
function search(str: string, start: string): string;
function search(str: string, start: any): string {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'いろはにほへとちりぬる';
console.log(search(msg, 3));
console.log(search(msg, 'と'));
上記を共用型を利用して書き換えると
function search(str: string, start: number | string): string {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'いろはにほへとちりぬる';
console.log(search(msg, 3));
console.log(search(msg, 'と'));
上記をさらにアロー関数を使って書き換えると
let search = (str: string, start: number | string): string => {
if (typeof start === 'number') {
return str.substring(start);
} else {
return str.substring(str.indexOf(start));
}
}
let msg = 'いろはにほへとちりぬる';
console.log(search(msg, 3));
console.log(search(msg, 'と'));
class name {
modifier variable: p_type...
modifier constructor(modifier arg: a_type,...) { ...c_body... }
modifier method(arg: a_type,...): r_type { ...m_body... }
}
--------------------------
- name: クラス名
- modifier: 修飾子
- variable: プロパティ名
- p_type: プロパティの型
- arg: 引数
- a_type: 引数の型
- c_body: コンストラクターの本体
- method: メソッド名
- r_type: 戻り値の型
- m_body: メソッドの本体
ex1)
class Dog {
// コンストラクター
constructor(private category: string, private weight: number) { }
// toStringメソッド
public toString(): string {
return this.category + ':' + this.weight + 'kg'
}
}
let mame = new Dog('豆柴', 8);
console.log(mame.toString());
静的メンバー(static member)とは、 特定のインスタンスにではなく、クラスに属するフィールドやメソッドのことです。
class FigureUtil {
// 静的プロパティPIを宣言
static PI = 3.14;
// 静的メソッドcircleを宣言
static circle(radius: number): number {
return radius * radius * this.PI;
}
}
console.log(FigureUtil.PI);
console.log(FigureUtil.circle(2));
get name(): type { ... }
set name(value: type) { ... }
--------------------------
- name: プロパティ名
- type: プロパティの型
- value: 設定時に渡された値
get/setアクセサーとは、クラスの外側からはプロパティのようにアクセスできる。 → 単なる変数ではなく内部的にはメソッドとして取得/設定時の挙動を実装する仕組みのこと。
get/setアクセサーを利用することで、例えば値を設定する際にその妥当性を検証したり、取得する際に値を変換したりと、プロパティ(メンバー変数)だけでは表現できない「値の出し入れに伴う任意の処理」を実装できます。
class Dog {
// weightプロパティの値を格納するためのプライベート変数
private _weight: number;
// weightプロパティを取得
get weight(): number {
return this._weight;
}
// weightプロパティを設定(0未満の場合は例外)
set weight(value: number) {
if (value < 0) {
throw new Error('weightプロパティは正数で指定してください。');
}
this._weight = value;
}
}
let poodle = new Dog();
poodle.weight = -13; // => weightプロパティは正数で指定してください。
class name extends parent {
...body...
}
--------------------------
- name: クラス名
- parent: 基底クラス名
- body: クラスの定義
extendsキーワード → 既存のクラスの継承
superキーワード → 派生クラス(サブクラス)から基底クラス(スーパークラス)のメソッドを呼び出す
class Dog {
constructor(protected category: string, protected weight: number) { }
toString(): string {
return this.category + ':' + this.weight + 'Kg'
}
}
// Dogクラスを継承したULtraDogクラス
class UltraDog extends Dog {
toString(): string {
// 基底クラスのtoStringメソッドを呼び出し
return 'スーパー' + super.toString();
}
}
let dober = new UltraDog('ドーベルマン', 35);
console.log(dober.toString()); => スーパードーベルマン:35Kg
親メソッドを呼び出すにはsuper.メソッド名(...)
。
コンストラクターをオーバーライドし、その中から親コンストラクターを呼び出すことも可能です。その場合は単にsupper(...)
とする。
interface name extends s_name {
...body...
}
--------------------------
- name: インターフェイス名
- s_name: 親インターフェイス名
- body: インターフェイスの本体
インターフェイス
とは、シグネチャだけで実装がないクラスのようなもので、クラスに対して、特定の機能(実装)を強制する目的で利用される。
インターフェイス
は契約のようなもの。インターフェイス
の実装はクラスの継承とは違い、クラスの継承とは違うそれ以外の機能を参照するための口を作ること。
ex)
電気契約者 → 電力会社 ← 火力発電or原子力発電or風力発電
契約者は電力がどのように作られているか(実装)を意識しなくても、形式的に電気(メソッド)を使うことができる。
インターフェイス
そのものの定義には、interface命令を利用する。クラスと同じく、extendsキーワードで別のインターフェイスを継承することも可能。
// Petインターフェイス
interface Pet {
name: string; // プロパティ定義
call(): void; // メソッド定義
}
// Pet実装クラスを宣言
class Dog implements Pet {
name: string;
call(): void {
console.log(this.name + 'さん');
}
}
let pochi = new Dog();
pochi.name = 'ポチ';
pochi.call(); => ポチさん
// Power Contractインターフェイス
interface PowerContract {
method: string;
call(): void;
}
// Power company実装クラスを宣言
class PowerCompany implements PowerContract {
method: string;
call(): void {
console.log(this.method + 'を使って電力を供給しています');
}
}
let tepco = new PowerCompany();
tepco.method = '原子力発電';
tepco.call(); => 原子力発電を使って電力を供給しています
インターフェイスを実装するにはimplementsキーワードを利用します。
複数のインターフェイスを実装するにはカンマ区切りで
implements Pet, Movable
とする。
インターフェイスを型注釈として利用する。
interface Member {
name: string; // プロパティシグニチャ
greet(): voild: //メソッドシグニチャ
}
// Member型の変数memを宣言
let mem: Member = {
name: '田中太郎',
greet() {
console.log('こんにちは、' + this.name);
}
};
mem.greet(): => こんにちは、田中太郎
// number型の引数引数a, bを受け取り、、number型の値を返す返すAddFunc型型
interface AddFunc {
(a: number, b: number): number
}
// AddFunc型の関数を定義
let add: AddFunc = function (x: number, y: number): number {
return x + y;
};
{ [index: string]: string }
インデック型 値型
class name<T> {
...body...
}
--------------------------
- name: クラス名
- T: 型引数
- body: クラスの本体
ジェネリック(Generics)
とは、汎用的なクラスやメソッドを特定の型に対応つけるための機能。
class GenericClass<T> {
data: T;
getData(): T {
return this.data;
}
}
let gen = new GenericClass<number>();
gen.data = 108;
console.log(gen.getData()); => 108
ジェネリック型を定義するには、クラス名の後方に
ジェネリック型をインスタンス化する際は、<…>の形式で紐付けるべき型を付与します。例では、型引数Tにnumber型が割り当てられたことになります。number型に紐づいたGenericClassオブジェクトが生成された。
小難しく書くとこんな感じだが、要はGenericClassオブジェクト生成時に型を決めてやれば由なしにTに型が紐づけられて便利だよねという話。
参考 Angularアプリケーションプログラミング
Angularの本ですが、TypeScriptの紹介もあっていい本でした。