CodeGridの「TypeScriptことはじめ」を参考にしました。
TypeScriptとは
- マイクロソフトによって開発されたプログラミング言語
- TypeScriptで書かれたコードはJavaScriptのコードに変換される
- コンパイルするためのソフトウェアをtscという
- 文字列型、数値型、boolead型などのデータの型を備えている。変数や関数の引数、戻り値などのあらゆる値についてプログラム実行前に型を決める静的型付け言語
- プログラム実行時まで型が決まらない言語を動的型付け言語(JavaScript)
- TypeScriptやCoffeeScriptなどのJavaScriptにコンパイル言語をAltJSと総称される
- AltJSやBabelを使う理由のひとつに同じJavaScriptでもブラウザごとに使える構文が揃っていないため
- すべてのJavaScriptはいっさい書き換えることなくTypeScriptのコードとして振る舞うことができる(スーパーセット)。よって段階的に静的型付けを導入することができる
- Googleの社内標準言語
静的型付け言語の利点
- 変数名のtypo、値の消し忘れ、記述のミスを自動的にコンパイルエラーとして落とす
- 上記のようなミスを自動テストによって防ぐ必要がなくなる
ことはじめ
ファイルの拡張子は.ts
型注釈は: string
function greeter(person: string) {
return "Hello, " + person;
}
var user = "Jane User";
document.body.innerHTML = greeter(user);
コンパイル実行
$ tsc greeter.ts
型注釈でエラーになった場合、terminalでエラー分が表示される
function greeter(person: string) {
return "Hello, " + person;
}
var user = [0, 1, 2];
document.body.innerHTML = greeter(user);
コンパイル実行
$ tsc greeter.ts
greeter.ts:5:35 - error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
5 document.body.innerHTML = greeter(user);
型注釈と型推論
型注釈の書き方
ある変数について、その変数がどういう型を受け入れるかを指定する
※型注釈の先頭を大文字にするのは禁止(String、Number、Boolean)
型注釈の書き方はこちらが参考になりました。
「TypeScript のオブジェクト型リテラルいろいろ」
変数
let str: string;
str = '文字列';
let num: number;
num = 123;
let bool: boolean;
bool = true;
引数/関数の戻り値
型注釈は変数だけでなく、引数やプロパティでも同様に書くことができる
function add(a: number, b: number): number {
return a + b;
}
-
a: number, b:number
は引数の型注釈 -
): number {
は関数の戻り値の型注釈 - 戻り値を返さない場合は
: void
を指定
配列
型注釈の書き方は配列の中身の型 + []
function doubleAll(arr: number[]): number[] {
return arr.map(n => n * 2);
}
doubleAll([1, 2, 3]);
オブジェクト
オブジェクトの型注釈の区切りは;
になる
function getLabel(user: {name: string; id: string}): string {
return `${user.name}@{user.id}`;
}
const user = {
name: '鈴木',
id: 'ichiro51',
}
getLabel(user);
objectのKeyの数が多い場合はinterface
でまとめる
interface User {
name: string;
id: string;
}
function getLabel(user: User): string {
return `${user.name}@${user.id}`;
}
const user = {
name: '鈴木',
id: 'ichiro51',
}
getLabel(user);
関数型
let add: (a: number, b: number) => number;
add = (a, b) => {
return a + b;
};
add(2, 3);
any型
1つの関数が複数の型を返す場合をany型
という
any型はコンパイルをすり抜けてエラー検出されないので基本的には使わないようにする
let v: any;
v = 1;
v = 'a';
v = true;
型推論とは
型推論を使えば型注釈を毎回すべてに書く必要がなくなる
let str = '文字列';
str = 1;
// error TS2322: Type '1' is not assignable to type 'string'.
変数宣言でstr
にstring型が格納されたので「変数strはstring型である」と推論される。
この機能が型推論。
その値にnumber型を格納しようとするとエラーになる
オブジェクト
存在しないプロバティはエラーになる
const user = {
name: '佐藤',
id: 'taro123',
}
user.address = 'aaa@bb.com';
greeter.ts:20:7 - error TS2339: Property 'address' does not exist on type '{ name: string; id: string; }'.
型推論の時点で型が確定するため違う型を格納しようとするとエラーになる
const user = {
name: '佐藤',
id: 'taro123',
}
user.name = 123;
greeter.ts:20:1 - error TS2322: Type '123' is not assignable to type 'string'.
配列
const array = [0, 1, 2];
array.map(v => v * 2);
型注釈を書いた方がいい理由
修正やリファクタリングをするとき型注釈を書いてるとミスを防げる
function calculatedTime(hour: number): string {
return hour + 'h';
}
calculatedTime(1);
新たにif分岐を追加して戻り値を設定したとき、型が違ってエラーが出力される
function calculatedTime(hour: number, expr: boolean): string {
if (expr) {
return hour;
}
return hour + 'h';
}
calculatedTime(1, true);
greeter.ts:56:5 - error TS2322: Type 'number' is not assignable to type 'string'.
クラス構文
- TypeScriptはJavaScriptのスーパーセットのためクラス構文を扱える
- クラスを型として扱ってコンパイルする
書き方
- クラス内の先頭にプロパティの型を事前に宣言しないとエラーになる
- クラスにどんなプロパティが定義されてるかを把握できる
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
引数プロパティ宣言
- 引数プロパティ宣言でプロパティ宣言を省略できる
- publicをアクセス修飾子という
class Person {
constructor(public name: string, public age: number) {
}
}
インスタンスメソッド
インスタンスメソッドの戻り値の型注釈
class Person {
constructor(public name: string, public age: number) {
}
say(): string {
return `Hello, I'm ${this.name}.`;
}
}
const person = new Person('Bob', 36);
console.log(person.say());
継承
- 継承元のクラスをスーパークラス。継承したクラスをサブクラスという
- 共通のプロパティは
super()
を呼び、スーパークラスのコンストラクタに値を渡さなければならない
class Person {
constructor(public name: string, public age: number) {
}
say(): string {
return `Hello, I'm ${this.name}.`;
}
}
class Employee extends Person {
constructor(public name: string, public age: number, public location: string) {
super(name, age)
}
}
継承サブクラスの扱い
getCardLabel()の引数person: Person
にはPerson型だけでなく、そのクラスを継承したサブクラスのインスタンスでも受け付ける
class Person {
constructor(public name: string, public age: number) {
}
say(): string {
return `Hello, I'm ${this.name}.`;
}
}
function getCardLabel(person: Person): string {
return `[${person.name} (${person.age})]`
}
class Employee extends Person {
constructor(public name: string, public age: number, public location: string) {
super(name, age)
}
}
const person = new Person('Bob', 36);
console.log(getCardLabel(person));
const employee = new Employee('Tom', 32, 'Japan');
console.log(getCardLabel(employee)); // [Tom (32)]