目次
1. 概要
2. 想定読者
3. 型について
4. インターフェース
5. 関数
6. クラス
7. ジェネリック
【1. 概要】
TypeScriptをこれから触ってみる方向けに、どんな機能があるのかを簡単にまとめてみました。
【2. 想定読者】
・JavaScriptの経験がある方。
・TypeScriptってそもそも何ぞや、の基本は抑えておられる方。
【3. 型について】
プリミティブ型
boolean
const b: boolean = true;
number
const n: number = 0;
string
const s: string = "string";
enum
enum Employee {
FirstName,
LastName,
Age
}
const employeeFirstName: Employee = Employee.FirstName
console.log(employeeFirstName);
// output: 0
console.log(Employee[employeeFirstName]);
// output: FirstNam
any
let randomValue: any = 10;
randomValue = 'Mateo'; // OK
randomValue = true; // OK
型アサーション
(randomValue as string).toUpperCase();
のようにas
構文で「信頼してください、わかってやっています」ということをコンパイラに伝える。
型アサーション
let randomValue: unknown = 10;
randomValue = true;
randomValue = 'Mateo';
// このまま下記コードを実行すると、'randomValue' is of type 'unknown'.とエラーが出る。
// randomValue.toUpperCase();
if (typeof randomValue === "string") {
console.log((randomValue as string).toUpperCase()); //* Returns MATEO to the console.
} else {
console.log("Error - A string was expected here."); //* Returns an error message.
}
共用体型
パイプ (|
) を使用して、代入できる値の型を制限する。
共用体型
let multiType: number | boolean;
multiType = 20; // OK
multiType = true; // OK
multiType = "twenty"; // ERROR
型ガード
型ガードとは、ある値に対して特定の型かどうかをチェックし、その結果に応じて処理を分けることを指す。
型ガード
function add(x: number | string, y: number | string) {
if (typeof x === 'number' && typeof y === 'number') {
return x + y;
}
if (typeof x === 'string' && typeof y === 'string') {
return x.concat(y);
}
throw new Error('Parameters must be numbers or strings');
}
console.log(add('one', 'two')); //* Returns "onetwo"
console.log(add(1, 2)); //* Returns 3
console.log(add('one', 2)); //* Returns error
交差型
次の例では、Employee と Manager という 2 つのインターフェイスを定義した後、両方のインターフェイスのプロパティを併せ持つ ManagementEmployee という新しい交差型を作成する。
交差型
interface Employee {
employeeID: number;
age: number;
}
interface Manager {
stockPlan: boolean;
}
type ManagementEmployee = Employee & Manager;
let newManager: ManagementEmployee = {
employeeID: 12345,
age: 34,
stockPlan: true
};
リテラル型の定義
変数に代入できる値を制限する。
string
、number
、boolean
の3つで使用可能。
リテラル型の定義
type testResult = "pass" | "fail" | "incomplete";
let myResult: testResult;
myResult = "incomplete"; // OK
myResult = "pass"; // OK
myResult = "failure"; // ERROR
配列
下記の2通りの定義が可能。
配列
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
【4.インターフェース】
TypeScript のコーディングのガイドラインでは、インターフェイスを文字I
で始めないことを勧めています。
インターフェース
interface Employee {
firstName: string;
lastName: string;
age?: number;
fullName(): string;
}
// Employeeインターフェースの拡張
interface Manager extends Employee {
phoneNumber: number;
}
let employee: Manager = {
firstName : "Emil",
lastName: "Andersson",
phoneNumber: 12345678901,
fullName(): string {
return this.firstName + " " + this.lastName;
}
}
employee.firstName = 10; //* Error - Type 'number' is not assignable to type 'string'
プロパティの種類 | 例 |
---|---|
必須 | firstName: string; |
省略可能 | firstName?: string; |
読取専用 | readonly firstName: string; |
インターフェイスを使用してJavaScript APIを記述
const fetchURL = 'https://jsonplaceholder.typicode.com/posts'
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
async function fetchPosts(url: string) {
let response = await fetch(url);
let body = await response.json();
return body as Post[];
}
async function showPost() {
let posts = await fetchPosts(fetchURL);
let post = posts[0];
console.log('Post #' + post.id)
console.log('Author: ' + (post.userId === 1 ? "Administrator" : post.userId.toString()))
console.log('Title: ' + post.title)
console.log('Body: ' + post.body)
}
showPost();
【5. 関数】
名前付き関数
名前付き関数
// 戻り値あり
function addNumbers1 (x: number, y: number): number {
return x + y;
}
addNumbers(1, 2);
// 戻り値なし
function addNumbers2 (x: number, y: number): void {
console.log(x + y);
}
匿名関数
匿名関数
let addNumbers = function (x: number, y: number): number {
return x + y;
}
addNumbers(1, 2);
アロー関数
アロー関数
// 匿名関数
let addNumbers1 = function (x: number, y: number): number {
return x + y;
}
// アロー関数(単一行ver)
let addNumbers2 = (x: number, y: number): number => x + y;
// アロー関数(複数行ver)
let total2 = (input: number[]): number => {
let total: number = 0;
for(let i = 0; i < input.length; i++) {
if(isNaN(input[i])) {
continue;
}
total += Number(input[i]);
}
return total;
}
パラメーターの種類
省略可能パラメーター
function addNumbers (x: number, y?: number): number {
if (y === undefined) {
return x;
} else {
return x + y;
}
}
addNumbers(1, 2); // Returns 3
addNumbers(1); // Returns 1
規定値を指定したパラメーター
function addNumbers (x: number, y = 25): number {
return x + y;
}
addNumbers(1, 2); // Returns 3
addNumbers(1); // Returns 26
restパラメーター
// 複数のパラメーターを 1 つのグループとして扱う
let addAllNumbers = (firstNumber: number, ...restOfNumbers: number[]): number => {
let total: number = firstNumber;
for(let counter = 0; counter < restOfNumbers.length; counter++) {
if(isNaN(restOfNumbers[counter])){
continue;
}
total += Number(restOfNumbers[counter]);
}
return total;
}
addAllNumbers(1, 2, 3, 4, 5, 6, 7); // returns 28
addAllNumbers(2); // returns 2
addAllNumbers(2, 3, "three"); // flags error due to data type at design time, returns 5
分解オブジェクトパラメーター
interface Message {
text: string;
sender: string;
}
function displayMessage({text, sender}: Message) {
console.log(`Message from ${sender}: ${text}`);
}
displayMessage({sender: 'Christopher', text: 'hello, world'});
関数型
関数型
// 関数型を定義
type calculator = (x: number, y: number) => number;
// インターフェースの場合は、下記のように定義
interface Calculator {
(x: number, y: number): number;
}
// 関数型を使用し、関数を定義
let addNumbers: calculator = (x: number, y: number): number => x + y;
let subtractNumbers: calculator = (x: number, y: number): number => x - y;
// 戻り値に関数型を指定
let doCalculation = (operation: 'add' | 'subtract'): calculator => {
if (operation === 'add') {
return addNumbers;
} else {
return subtractNumbers;
}
}
let calculationFunction = doCalculation('add');
calculationFunction(1,2); // return 3
【6. クラス】
アクセス修飾子
アクセス修飾子 | 説明 |
---|---|
public | アクセス修飾子を指定しない場合、既定値はpublic です。public キーワードを使用して、メンバーを明示的にpublic に設定することもできます。 |
private |
private キーワードを使用してメンバーを変更すると、そのメンバーを含むクラスの外部からはアクセスできなくなります。 |
protected |
protected 修飾子はprivate 修飾子とほとんど同じように機能しますが、protected と宣言されたメンバーには、派生クラス内でもアクセスできる点が異なります。 |
readonly |
readonly 修飾子を使用して、プロパティを readonly にすることができます。readonly プロパティは、宣言時またはconstructor で初期化されるときにのみ設定できます。 |
クラス
// インターフェース
interface Vehicle {
// pubclicメンバーのみ宣言可能
doors: number;
accelerate(speed: number): void;
}
// Carクラス
class Car implements Vehicle {
// プロパティ
private static numberOfCars: number = 0; // staticプロパティ
private _doors: number;
// コンストラクタ
constructor(doors = 4) {
Car.numberOfCars++;
if ((doors % 2) === 0) {
this._doors = doors;
} else {
throw new Error('Doors must be an even number');
}
}
// アクセサー
get doors() {
return this._doors;
}
set doors(doors) {
if ((doors % 2) === 0) {
this._doors = doors;
} else {
throw new Error('Doors must be an even number');
}
}
// メソッド
static getNumberOfCars(): number {
return Car.numberOfCars;
}
accelerate(speed: number): void {
this.showDoor()
console.log('run accelerate.')
}
// サブクラスからもアクセスするため「protected」を使用
protected showDoor(): void {
console.log(`ドア数は${this.doors}です。`);
}
}
// Carクラスを継承したElectricCarクラス
class ElectricCar extends Car {
// Properties unique to ElectricCar
private _range: number;
// Constructor
constructor(range: number, doors = 2) {
super(doors); // スーパークラスのconstructorを実行
this._range = range;
}
// Accessors
get range() {
return this._range;
}
set range(range) {
this._range = range;
}
// Methods
charge() {
console.log(this.showDoor() + " is charging.")
}
// オーバーライド
accelerate(): void {
console.log('run electricCar accelerate.');
}
}
【7. ジェネリック】
ジェネリック
function getArray<T>(items : T[]) : T[] {
return new Array<T>().concat(items);
}
let numberArray: number[] = getArray<number>([5, 10, 15, 20]);
numberArray.push(25); // OK
numberArray.push('test'); // ERROR
// 複数個
function identity<T, U> (value: T, message: U) : T {
console.log(message);
return value
}
let returnNumber = identity<number, string>(100, 'Hello!');
ジェネリック制約
type ValidTypes = string | number;
function identity<T extends ValidTypes, U> (value: T, message: U) { // Return type is inferred
let result: ValidTypes = '';
let typeValue: string = typeof value;
if (typeof value === 'number') { // Is it a number?
result = value + value; // OK
} else if (typeof value === 'string') { // Is it a string?
result = value + value; // OK
}
console.log(`The message is ${message} and the function returns a ${typeValue} value of ${result}`);
return result
}
let numberValue = identity<number, string>(100, 'Hello');
let stringValue = identity<string, string>('100', 'Hello');
console.log(numberValue); // Returns 200
console.log(stringValue); // Returns 100100
keyof
function getPets<T, K extends keyof T>(pet: T, key: K) {
return pet[key];
}
let pets = { cats: 4, dogs: 3, parrots: 1, fish: 6 };
console.log(getPets(pets, "fish")); // Returns 6
console.log(getPets(pets, 1)); // ERROR
ジェネリック インターフェイス
ジェネリック インターフェイス
interface Identity<T, U> {
value: T;
message: U;
}
let returnNumber: Identity<number, string> = {
value: 25,
message: 'Hello!'
}
ジェネリックインターフェースを関数型として宣言
interface ProcessIdentity<T, U> {
(value: T, message: U): T;
}
function processIdentity<T, U> (value: T, message: U) : T {
console.log(message);
return value
}
let processor: ProcessIdentity<number, string> = processIdentity;
let returnNumber1 = processor(100, 'Hello!'); // OK
let returnString1 = processor('Hello!', 100); // Type check error
ジェネリックインターフェースをクラス型として宣言
interface ProcessIdentity<T, U> {
value: T;
message: U;
process(): T;
}
class processIdentity<X, Y> implements ProcessIdentity<X, Y> {
value: X;
message: Y;
constructor(val: X, msg: Y) {
this.value = val;
this.message = msg;
}
process() : X {
console.log(this.message);
return this.value
}
}
let processor = new processIdentity<number, string>(100, 'Hello');
processor.process(); // Displays 'Hello'
processor.value = '100'; // Type check error
ジェネリッククラス
class processIdentity<T, U> {
private _value: T;
private _message: U;
constructor(value: T, message: U) {
this._value = value;
this._message = message;
}
getIdentity() : T {
console.log(this._message);
return this._value
}
}
let processor = new processIdentity<number, string>(100, 'Hello');
processor.getIdentity(); // Displays 'Hello'
カスタムの型とクラスでジェネリックを実装
class Car {
make: string = 'Generic Car';
doors: number = 4;
}
class ElectricCar extends Car {
make = 'Electric Car';
doors = 4
}
class Truck extends Car {
make = 'Truck';
doors = 2
}
function accelerate<T extends Car> (car: T): T {
console.log(`All ${car.doors} doors are closed.`);
console.log(`The ${car.make} is now accelerating!`)
return car
}
let myElectricCar = new ElectricCar;
accelerate<ElectricCar>(myElectricCar);
let myTruck = new Truck;
accelerate<Truck>(myTruck);
// 出力
// "All 4 doors are closed."
// "The Electric Car is now accelerating!"
// "All 2 doors are closed."
// "The Truck is now accelerating!"