通常のJavaScript(ES6)と異なる点に重きをおいた特徴
問題点(2021年1月時点:バージョンVersion 4.1.3)
一部のコードにcommonjsに準拠した書き方、es2015に準拠した書き方があり混乱をまねく。 外部クラスファイルを扱う際、importやrequereを使うようだがcommonjsやらes2015やらで書き方が異なるうえ、 exportsが使えない、requireが使えないなどエラーが頻発する。 webpackなどでファイルを一つにまとめる方法が現在主流か?手順
前提条件としてnpmコマンドが使えるようになっていること。$ npm install -g typescript
$ tsc -V Version 4.1.3
cd ~/git/hello_project
$ tsc --init
test.ts
class HelloWorld
{
m_big_buta: string = '大きなブタ2'; // メンバ
/** コンストラクタ
*/
constructor(str1: string){
console.log(str1);
}
// publicメソッド
public bark()
{
let value1: number = 333; // 数値型の宣言
let str1: string = 'こんにちは世界!'; // 文字列型の宣言
console.log(value1);
console.log(str1);
this._working(); // privateメソッドの呼び出し
console.log(this.m_big_buta); // メンバを呼び出してみる
}
// privateメソッド
private _working(){
console.log('プライベートで猫が歩く');
}
}
var helloWorld = new HelloWorld("Hello World!");
helloWorld.bark();
$tsc test.ts
<html>
<head>
<script src="test.js"></script>
</head>
<body>
出力はコンソールにて
</body>
</html>
事前条件
TypeScriptの開発環境 (webpackでコンパイルする環境)
$cd ~/git/node_demo/demo2
npm initいくつか質問が聞かれるので適当に入力。
$ npm install webpack
$ npm install webpack-cli
$ npm install typescript
$ npm install ts-loader
mkdir src
mkdir dist
webpack.config.js
module.exports = {
// 本番環境ならるproduction(最適化される)、開発環境ならdevelopment(デバッグしやすい)。
mode: 'development',
// エントリーポイント:スタートファイル
entry: './src/index.ts',
module: {
rules: [
{
test: /\.ts$/, // コンパイル対象ファイルの拡張子
use: 'ts-loader', // コンパイルパッケージを指定
},
],
},
// ここで指定した拡張子は、import文で拡張子を省略できる。
resolve: {
extensions: [
'.ts', '.js',
],
},
};
tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"target": "es5",
"module": "es2015"
}
}
$ npx webpackwebpackでsrcに存在するtsファイル群を一つにまとめ、distフォルダにmain.jsとして出力する。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>TypeScriptとwebpackのDemo2</title>
<script src="dist/main.js"></script>
</head>
<body>
<h3>TypeScriptとwebpackのDemo2</h3>
コンソールに出力
</body>
</html>
表示画面
おまけ
「yarn build」コマンドでコンパイルできるよう、package.jsonに設定を追加{ "name": "typescript_demo2", "version": "1.0.0", "description": "TypeScriptの開発環境構築その2", "main": "index.js", "scripts": { "build": "npx webpack", "test": "echo \"Hello World!\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/amaraimusi/node_demo.git" }, "author": "kenji uehara", "license": "ISC", "bugs": { "url": "https://github.com/amaraimusi/node_demo/issues" }, "homepage": "https://github.com/amaraimusi/node_demo#readme", "dependencies": { "ts-loader": "^8.0.14", "typescript": "^4.1.3", "webpack": "^5.14.0", "webpack-cli": "^4.3.1" } }
実行例
見本クラス
/react_demo2/dev/resources/ts/test/Person.ts
// Person.ts
export class Person {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
}
}
※exportはjsxのお決まりの書き方のようで、importと対をなす考えのようである。見本クラスのインスタンス化
import React from 'react';
import ReactDOM from 'react-dom';
import { Person } from './test/Person';
const App = () => {
const john = new Person('John', 25);
const msg = john.greet();
return (
<div>${msg}</div>
);
}
ReactDOM.render(<App />, document.getElementById('react_app'));
let isDone: boolean = false;
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let color: string = "blue";
color = 'red'; // シングルクォートも使用可能
また、テンプレート文字列も利用できます。
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;
let uniqueSymbol: symbol = Symbol();
この型は一意の値を取り扱うときに利用します。重複が合ってはならないキーなどに。
function warnUser(): void {
console.log("This is a warning message");
}
function add(x: number, y: number): number {
return x + y;
}
上記のTypeScript版では、引数xとyの型をnumberに、返り値の型をnumberに指定しています。const add = function(x, y) { return x + y; };TypeScriptの関数式
const add: (x: number, y: number) => number = function(x, y) {
return x + y;
};
function greet(name?: string): void {
if (name) {
console.log(`Hello, ${name}!`);
} else {
console.log('Hello!');
}
}
function greet(name: string = "User"): void {
console.log(`Hello, ${name}!`);
}
function getSum(...numbers: number[]): number {
return numbers.reduce((prev, current) => prev + current, 0);
}
アクセス修飾子
TypeScriptには、public, private, protected の3つのアクセス修飾子があります。クラスのメソッドの基本
TypeScriptのクラスは、ES6のクラスの仕様をベースに、静的型チェックやアクセス修飾子などの追加機能を持っています。以下は、クラスとそのメソッドの基本的な例です。
class Animal {
constructor(private name: string) {}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters.`);
}
}
上記の例では、Animalというクラスにmoveというメソッドが定義されています。また、コンストラクタ内でnameというプライベートプロパティを定義しています。
public (デフォルト):
メンバーに何も指定しない場合、そのメンバーはデフォルトでpublicとなります。つまり、クラスの外からでもアクセス可能です。
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
}
private:
privateが指定されたメンバーは、そのクラスの中からのみアクセスできます。
class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
}
protected:
protectedが指定されたメンバーは、そのクラスおよびサブクラスの中からアクセスできます。
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
bark(): void {
console.log(`Woof! Woof! My name is ${this.name}`);
}
}
public (デフォルト)
publicアクセス修飾子は、メンバーが外部からアクセス可能であることを示します。TypeScriptでは、アクセス修飾子を指定しない場合、メンバーはデフォルトでpublicとなります。
class Person {
public name: string; // 明示的にpublicを指定している
constructor(name: string) {
this.name = name;
}
public greet(): void { // 明示的にpublicを指定している
console.log(`Hello, my name is ${this.name}.`);
}
}
private
privateアクセス修飾子は、メンバーがそのクラス内からのみアクセス可能であることを示します。
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
public greet(): void {
console.log(`Hello, my name is ${this.getName()}.`);
}
private getName(): string { // privateメソッド
return this.name;
}
}
上記の例では、nameプロパティとgetNameメソッドはprivateであり、クラスの外部からは直接アクセスできません。しかし、greetメソッドはpublicなので、外部から呼び出すことができ、その中でgetNameメソッドを使っているのが分かります。
また、TypeScript 3.8以降では、「#」をプロパティ名の前に付けることで、そのプロパティをプライベートフィールドとして定義することもできます。
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
public greet(): void {
console.log(`Hello, my name is ${this.#name}.`);
}
}
このプライベートフィールドは、通常のprivateメンバよりも厳格にプライベートであり、サブクラスからもアクセスすることができません。 オブジェクトとしての連想配列
JavaScriptのオブジェクトリテラルを使用して、簡単に連想配列を作成することができます。
let obj: { [key: string]: string } = {
"key1": "value1",
"key2": "value2"
};
ここでは、オブジェクトobjは文字列のキーに対して文字列の値を持つことが示されています。インデックスシグネチャ
より柔軟に連想配列の型を表現するために、TypeScriptではインデックスシグネチャを使うことができます。
interface StringDictionary {
[key: string]: string;
}
let obj: StringDictionary = {
"key1": "value1",
"key2": "value2"
};
このStringDictionaryインターフェースは、任意の文字列のキーを持つことができ、それに対応する値も文字列であることを示しています。
数値のキーを持つ連想配列
interface NumberDictionary {
[key: number]: string;
}
let arr: NumberDictionary = {
1: "value1",
2: "value2"
};
クラスにインターフェースを実装
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
constructor(public name: string) {}
makeSound(): void {
console.log("Woof! Woof!");
}
}
const myDog = new Dog("Buddy");
myDog.makeSound(); // "Woof! Woof!"
上記の例では、DogクラスはAnimalインターフェースを実装しています。このように、インターフェースはクラスが特定の形状や契約を満たしていることを保証するためのものです。
しかし、TypeScriptのインターフェースはコンパイル時のみの存在であり、コンパイルされたJavaScriptコードには現れません。そのため、実行時にはインターフェースに関する情報は利用できない点を理解しておくことが重要です。インデックスシグネチャとしてのインターフェースの使い方
// インターフェースの定義
interface Person {
firstName: string;
lastName: string;
age?: number; // オプショナルなプロパティ
}
// インターフェースを実装した関数
function greet(person: Person): void {
console.log(`Hello, ${person.firstName} ${person.lastName}`);
}
// インターフェースを実装したオブジェクト
const john: Person = {
firstName: "John",
lastName: "Doe"
};
greet(john);