TypeScriptとは
JavaScriptに静的言語の特性を付与した言語。
コンパイルすることでJavascriptになる。
オブジェクト思考の特性を活かし実装できることにより、ドキュメントとしての役割を果たせたり、テストの工数を減らすことができる。
基礎文法まとめ
型、変数宣言
型宣言は変数名: 型 = 値
の形で行う。
stringの値の代入はシングルクオート、ダブルクオート、バッククオートの三つから値の代入ができる。
let bool: boolean = true;
let num: number = 1;
let str1: string = 'a';
let str2: string = "b";
let str3: string = `c`;
型注釈、型推論
今まで通り変数名: 型 = 値
で記述するのが、型注釈
型宣言を省略して、代入された値から型を推測してくれるのが、型推論
基本的には型推論を利用することが推奨されます。
変数の初期値を設定しないで宣言した場合はany型となり、型推論ができないので注意が必要です。
let bool = true;
let num = 1;
let str1 = 'a';
let str2 = "b";
let str3 = `c`;
let h;// any型
h = 'hello';
h = 1;
オブジェクト型
// 型注釈
const Person {
name: string,
age: number
} = {
name = 'Mike',
age = 20
}
// 型推論
const Person = {
name: 'Mike',
age: 20
}
配列
// 型注釈
const human1: string[] = ['Mike', 'Anna', "Jason"]
// 型推論
const human2 = ['Mike', 'Anna', "Jason"]
let h1 = human1[0]
Tuple型
必ず指定のデータ型で配列を宣言したい場合に記述する。
配列の中に型を指定することにより実装できる。
これにより別の型を代入することができなくなる。
const food: [string, number, boolean] = ['Hamburger', 500, true];
Enum
enumにすることにより、例ではDirection型で値をすとくすることができる。
これにより、direction型に存在する値以外で上書きできなくする。
enum Direction {
EAST = 1,
WEST = 2,
SOUTH = 3,
NORTH = 4
}
let direction = Direction.EAST
Union
「|」を利用して複数の型を許容する変数を作成することができる。
let unionType: string | number = 1;
unionType = 'hello'
let unionTypeArray: (string | number)[] = ['hello', 1]
リテラル型
指定した型の値しか代入できない記法。
const human: 'Mike' = 'Mike'
const direction2: 'east' | 'west' | 'north' | 'south' = 'east'
typeエイリアス
オブジェクトの型を変数のように記述する。
type direction = 'east' | 'west' | 'north' | 'south'
let direction3: direction = 'north'
関数
function checkWork(job: Job) {
console.log('work');
}
private,public
name: string;
private role: string;
クラス
class Person {
name: string;
constructor(readonly name: string, protected age: number) {
}
}
const p = new Person('Mike', 20)
インターフェース
オブジェクトのみのtypeエイリアス
そのためオブジェクトの利用に必ず使われることによりわかりやすく記述できるのがメリット。
interface Nameable {
name: string;
nickName: string;
}
const nameable: Nameable = {
name: 'Quill',
nickName: 'Quilla'
}
メソッドの指定方法
インターフェース内のメソッドは省略して記述ができる。
本来メソッドは関数名: (引数) => 戻り値;
の形で記入する必要があるが、インターフェースの場合関数名(引数) : 戻り値;
の形で宣言できる。
interface Human extends Nameable {
age: number;
greeting(message: string): void;
}
クラスにインターフェースを適用する
クラスにインターフェースを適用することにより、インターフェースの実装を持ったクラスを生成することができる。
extendsは単数のクラスの継承、implementsは複数のインターフェースを継承することができる。
abstractクラスとの違いは実装があるかないかであり、interfaceは型のみを定義している。
typeエイリアスでも代用できる。
interface Human extends Nameable {
age: number;
greeting(message: string): void;
}
class Developer implements Human { // 複数継承する場合は「,インターフェース名」の形で記述する
constructor(public age: number, public experience: number, public name: string) {
}
greeting(message: string) {
console.log(message);
}
}
構造的部分型
インターフェースをimplementsしたクラスのインスタンスを宣言する場合、インターフェースの型で定義することができること。
もしimplementsしたクラスの方が宣言されている変数や関数が多い場合も宣言が可能。
インターフェースの条件さえ満たしていれば問題ない。簡単に言うと多い分には問題ないよってことです。
インターフェースの形で宣言する場合は、インターフェースの型にあるものしかアクセスできないため、多いものはないものとして扱われます。
class Developer implements Human { // 複数継承する場合は「,インターフェース名」の形で記述する
constructor(public age: number, public experience: number, public name: string) {
}
greeting(message: string) {
console.log(message);
}
}
// Human型で宣言
const user: Human = new Developer(38, 3);
readonly影響範囲
インターフェースの変数をreadonlyにすることで読み込みだけの値を作ることができます。
ただし、インターフェースのimplements先では型の変更ができるため使い方には注意が必要です。
interface Human extends Nameable {
readonly name: string;
age: number;
greeting(message: string): void;
}
class Developer implements Human {
// implements先でnameがpublicに上書きできてしまっている
constructor(public age: number, public experience: number, public name: string) {
}
greeting(message: string) {
console.log(message);
}
}
インターフェースの継承
インターフェースの継承はクラスと異なり、複数の継承が可能です。
クラスの場合はメソッドや変数の名前が同じ場合、オーバーライドされる。
インターフェースも上書きすることができ、継承元の変数が代入できる型であれば変更することができる。
typeエイリアスで代用も可能
typeエイリアスの場合は&で継承することもでき、継承元の型に関係なく上書きできてしまう。
interface Nameable {
name: string;
}
interface Human extends Nameable {
name: string; // any型当も許容される
age: number;
greeting(message: string): void;
}
// &の場合(上記の継承と同義)
type Human = {
name: string; // number型当も許容される
age: number;
greeting(message: string): void;
} & Nameable
オプショナルプロパティ「?」
「?」を付与することで、変数、メソッドをあってもなくてもよいと言う宣言ができる。
interface Nameable {
name?: string;
nickName?: string;
say(message?: string) { // 引数にも適用できる
if(message) {
console.log(message);
}
}
}
const nameable: Nameable = {
// name,nickNameがなくても宣言できる
name: 'Quill',
nickName: 'Quilla'
}
その他の使い方
インターセクション型
二つのtypeエイリアスまたはインターフェースを継承して新しいインターフェースを作ること。ユニオン型とは異なり、継承したAかつBの特性を持つ必要がある。
記述方法は&
でつなぐか、extendsで継承するインターフェースを羅列する必要がある。
type MixJob = Engineer & Sales;
const mixJob: MixJob = {
name: "Jon",
role: 'backend',
customerNum: 3,
createCode(lang: string){
console.log(lang + "言語を書いています。");
}
}
// type MixJob = Engineer & Blogger;と同じ
interface MixJob extends Engineer, Sales { }
Type ground
条件文を使って型を絞り込んでいくこと。
typeof
を使うことで型を絞り込み、その型特有のライブラリを使うことができる。
オブジェクトの場合も同様に絞り込みをかけることでそのオブジェクトに紐づく変数、関数を利用することができる。
オブジェクトに変数が含まれているかはin
演算子を利用することで絞り込みが可能となる。
function toUpperCase(x: string | number) : string | number {
if(typeof x === 'string') {
return x.toUpperCase();
}
return x;
}
interface Engineer {
name: string;
role: string;
createCode(lang: string): void;
}
interface Sales {
name: string;
customerNum: number;
}
type Job = Engineer | Sales;
function checkWork(job: Job) {
if('role' in job) {
job.createCode('TypeScript');
}
}
タグ付きUnion
型の絞り込みを行うデザインパターン。
例では型にkindというリテラル型で一意にユニオン型の判別ができる値を持たせることで判定し、各関数の処理、変数を利用する。
class Dog {
kind: 'dog' = 'dog'
speak() {
console.log('bow-wow');
}
}
class Bird {
kind: 'bird' = 'bird';
speak() {
console.log('tweet-tweet');
}
fly() {
console.log('flutter');
}
}
type Pet = Dog | Bird;
function havePet(pet: Pet) {
pet.speak();
switch (pet.kind) {
case 'bird':
pet.fly();
}
if (pet instanceof Bird) {
pet.fly();
}
}
havePet(new Bird());