ES6に続いて、altJSの中でもひときわ勢いを感じるTypeScriptについて調べてみました。
概要
TypeScript = altJSの一種
-
オープンソース / マイクロソフト発
-
構文はJavaScript(ES5-6)と上位互換性がある
- ES6の構文はすべて使える
-
静的型付けによる型チェックができる点が特徴
- その他、interface/enum/genericなど独自の機能が追加されている
-
ES6と同様、開発時にjs(ES5)にトランスパイルすることで、ブラウザ上で実行できるようになる
- トランスパイルはnpmパッケージ(
typescript
)を使う
- トランスパイルはnpmパッケージ(
導入
試す(Playground)
ブラウザ上でTypeScriptを書いて動かせるPlaygroundが公開されている。
http://www.typescriptlang.org/play/
単独利用する(CLI)
TypeScriptファイルを手動でトランスパイルしたい場合は、npmパッケージをグローバルインストールした上でコマンド操作して使う。
# インストールする
$ yarn global add typescript
# コマンドラインでコンパイルする
# path/to/source.js が作られる
$ tsc path/to/source.ts
バンドルツールに組み込む(webpack)
Webpackによるバンドル作成時に自動的にトランスパイルさせたい場合は、
TypeScript本体に加えてローダーts-loader
をインストール/設定する。
# webpack環境を構築済みのローカルにインストールする
$ cd path/to/proj
$ yarn add --dev typescript ts-loader
# デフォルトではプロジェクト直下にtsconfigファイルが必要になるので、空のファイルを作っておく
# あるいはtsc --init でテンプレートを配置しておく
$ touch ./tsconfig.json
module.exports = {
// ...
module: {
rules: [
// 追加: TypeScript用のローダ追加
{
test: /\.ts$/,
use: [{
loader: 'ts-loader',
}],
},
// ...
],
},
// ...
}
TypeScript独自の構文/機能
型
ES5のプリミティブを拡張する形で、基本型が追加されている。
型 | 型指定時の記法 | リテラル | 備考 |
---|---|---|---|
any | any |
- | プリミティブを含む任意の型を許容 |
void | void |
- | 戻り値なしを明示する場合など(null/undefinedは許容) |
null | null |
null |
nullのみ許容 |
undefined | undefined |
undefined |
undefinedのみ許容 |
never | never |
- | 到達しえない戻り値の型として形式的に設定する型 |
真偽値 | boolean |
true, false |
- |
数値 | number |
123, 12.3, 0xFFFF, 0b1101 |
- |
文字列 | string |
'string' |
- |
配列 |
type[] , Array<type>
|
[0, 1, 2] |
- |
タプル | [type1, type2...] |
[0, 'value'] |
- |
enum | SomeEnumType |
SomeEnumType.some |
- |
あらゆるクラスはnull(undefined)のサブタイプとして扱われる。
そのため、あらゆる型の変数は__null代入を許容する__。
型指定(:type)
TypeScriptでは、変数や関数の引数、戻り値などに型を指定できる。
コンパイル時に静的な型チェックが行われ、問題がある場合はコンパイルエラーとして出力される。
ただし、コンパイルエラーになっても.js
ファイルは作成__される__(でも動く保証はない)。
let some: string ='foo'
some = 123
//=> error TS2322: Type '123' is not assignable to type 'string'.
// 引数を文字列型に指定
const echo = (message: string) => {
console.log(message)
}
// OK
echo('test')
// コンパイル時にエラーになる
echo(123)
//=> error TS2345: Argument of type '123' is not assignable to parameter of type 'string'.
型アサーション(as)
他の言語のキャストに近似する。
anyに変換することによってあらゆる代入時エラーを抑制(無視)させることもできる。
// 無理やり違う型の値をつっこむ
let some: number = 123
some = ('string' as any)
型エイリアス(type)
型に別名をつけることができる。
class Foo{
}
// 別名をつける
type Bar = Foo
new Foo() // OK
new Bar() // NG: 型の参照以外の方法では利用できない
// OK: 引数や戻り値の型として利用するのは問題ない
function getBar(): Bar{
return new Foo()
}
共同型(|)
引数の型を復数指定できる。
// 引数のとりえる型を復数指定する
function foo(param: string | number){
if(typeof param === 'string'){
}else if (typeof param === 'number'){
}
}
インタフェース(interface)
Javaなどの他言語と同じインタフェースが定義できる。
またインタフェース内においても型の指定が可能。
interface Param{
alpha: string,
bravo: string,
}
const foo = (param: Param) => {
console.log(param.alpha)
console.log(param.bravo)
}
// OK
foo({
alpha: 'test',
})
// NG
foo({
charlie: 'test',
})
//=> error TS2345: Argument of type '{ charlie: string; }' is not assignable to parameter of type 'Param'.
//=> Object literal may only specify known properties, and 'charlie' does not exist in type 'Param'.
オプションと読み専用指定(?/readonly)
インタフェースで定義したプロパティはデフォルトで必須となるが、?
によって任意扱いにできる。
また、readonly
によって変更を禁止できる。
interface Param{
readonly alpha: string,
bravo? : string,
}
let param: Param = { alpha: 'string' }
param.alpha = 'mod' // error
関数のインタフェース定義
関数のシグネチャもインタフェースとして定義できる。
interface SomeFunc{
(a: string, b: string): boolean
}
const someFunc: SomeFunc = (a, b, c) => { // error(c)
return 'bad return' // error
}
インタフェースの実装
クラスに適用することもできる。
interface SomeBehavior{
// ...
}
class SomeClass implements SomeBehavior{
// ...
}
クラス
アクセス修飾子(public/private/protected)
プロパティのアクセス範囲を指定できる。
TypeScriptにおけるデフォルトは__public__。
class Foo{
someDefault(){
}
public somePublic(){
}
private somePrivate(){
}
protected someProtected(){
}
}
class Bar extends Foo{
some(){
super.someDefault() // OK
super.somePublic() // OK
super.somePrivate() // NG
super.someProtected() // OK
}
}
const foo = new Foo()
foo.someDefault() // OK
foo.somePublic() // OK
foo.somePrivate() // NG
foo.someProteted() // NG
読み専用指定(readonly)
interfaceと同様に、プロパティを読み込み専用指定できる。
class Foo{
readonly bar: string = 'BAR'
baz: string = 'BAZ'
}
const foo = new Foo()
foo.bar // OK
foo.baz // OK
foo.bar = 'MOD' // NG
foo.baz = 'MOD' // OK
アクセサ(get/set)
外部からのプロパティの取得/設定をハンドルできる。
class Foo{
private _param: string = null
get param(): string{
// ... プロパティの更新に加えて任意の処理を追加できる
return this._param
}
set param(param: string){
// ...
this._param = param
}
}
const foo = new Foo()
foo.param = 'foobar'
console.log(foo.param)
抽象クラス(abstract)
抽象クラスを定義できる。
おおむね他言語と同じで、
- 抽象クラスをインスタンス化することは不可
- 抽象クラス内に具象メソッドを実装することもできる
abstract class Foo{ // 抽象クラスの定義
abstract absMethod(): void // 抽象メソッドの宣言
nonAbsMethod(){
}
}
class Bar extends Foo{
absMethod(){
}
}
const foo = new Foo() // NG
const bar = new Bar() // OK
bar.absMethod()
bar.nonAbsMethod()
関数/メソッド
シグネチャのチェック
型と同様に、引数の個数についてもコンパイル時にチェックされる。
const echo = () => {
console.log(message)
}
echo('some message')
// => error TS2346: Supplied parameters do not match any signature of call target.
オプション引数(?)
必須でない引数はオプション指定することで、省略時のエラーを回避できる。
function required(some: string){
return some
}
function optional(some?: string){
return some
}
required('foo') // OK
required() // NG
optional('foo') // OK
optional() // OK
オーバーロード
引数の型ごとに、異なる関数を定義できる。
ひとつの引数に対して復数のシグネチャを設定できる。
function foo(param: string)
function foo(param: number)
function foo(param: any){
switch(typeof param){
case 'string':
case 'number':
// ...
}
}
foo('string')
foo(123)
ジェネリック型(Generics)
ジェネリック(テンプレート)型の関数を定義できる。
function echo<T>(param: T): T{
return param
}
echo('test')
echo(123)
クラスに対しても指定できる。
class SomeClass<T>{
someParam: T
someMethod(param: T): T{
// ...
}
}
const strInstance = new SomeClass<string>()
const numInstance = new SomeClass<number>()
列挙型(Enum)
列挙型を定義できる。
enum ABC {
A = 1, // 値の指定が可能
B = 1<<4, // 式により値を指定することも可能
C = 1 + 3,
D, // 未指定の場合は前の値+1
}
名前空間(namespace)
モジュールの概念に近似する(internal module
と呼ばれていたとのこと)。
1モジュール内に名前空間を定義し、スコープを制限したり、実装を隠蔽できる。
namespace Foo{
// exportしないと外部からアクセスできない
export const bar = 'BAR'
const baz = 'BAZ'
// ネストすることも可
export namespace Hoge{
export const piyo = 'PIYO'
}
}
Foo.bar // OK
bar // NG: 空間が異なる
Foo.baz // NG: exportされていない
// 別名をつける
Foo.Hoge.piyo // OK
import Hoge = Foo.Hoge
Hoge.piyo // OK
宣言のマージ
namespace, interface, class, enumを重複して定義すると、その内容が自動的にマージされる。
namespace Foo{
export const bar = 'BAR'
}
// 重複する名前空間を定義
namespace Foo{
export const baz = 'BAZ'
}
// お互いが上書きされることなく、両方の機能が生きる
Foo.bar
Foo.baz
JSX構文
Reactで利用されるJSX構文をサポートしている。
利用するためには以下を満たす必要がある:
- ファイル名を
*.tsx
とすること - コンパイル時に
jsx
オプションを有効にすること
設定ファイル(tsconfig.json)
TypeScriptのコンパイルに関する設定をファイル化できる。
設定ファイルはtsconfig.json
というファイル名で、プロジェクトのルートに配置する。
設定ファイルの作成
CLIのinit
オプションを使うとテンプレートファイルが作成されるので、これをベースに編集する。
# カレントにtsconfig.jsonのテンプレートが配置される
$ tsc --init
compilerOptions
のオプション名は、tscコマンドのオプションと共通する。
オプションの一覧は公式を参照のこと。
設定ファイルにおける設定項目は__すべて任意__なので、デフォルトを使う場合は省略してもいい。
{
// コンパイラオプションを指定
"compilerOptions": {
"target": "ES5", // ES5に変換する
"module": "ES6", // ES6方式のモジュールシステム(import/export)を使う
"removeComments": true, // 変換時にコメントを消す
"outFile": "path/to/dest.js", // 指定した出力先にファイルをバンドルする
"sourceMap": true // デバッグ用のmapファイルを生成
"typeRoots": [ ... ], // 型定義ファイルを明示
},
// コンパイル対象のファイルを指定
"files": [
"path/to/src/moduleA.ts",
"path/to/src/moduleB.ts",
"path/to/src/moduleC.ts",
"path/to/src/entry.ts",
]
// あるいは、対象ファイル/除外ファイルの形式で指定することも可
"include": [
"path/to/src/**/*" // 対象フォルダ以下をまとめて追加
],
"exclude": [
"path/to/src/**/*.spec.ts" // テストファイルは除外
]
}
設定ファイルの利用
CLIの場合、設定ファイルはproject
オプションで指定できる。
# 設定ファイルの内容でコンパイル実行
$ tsc --project ./tsconfig.json
ネイティブjsとの連携(型定義ファイル, .d.ts)
型定義ファイル = ネイティブJavaScriptで書かれたコードをTypeScriptが解釈するための型情報の定義ファイル。
CLIオプションあるいは設定ファイルで型定義ファイルを読み込むことで、TypeScript側からjsコードを利用できるようになる。
自作する他、有名なライブラリは公開された既存のファイルを使える。
たとえばnpmパッケージの場合は@types/*
という名前で型定義ファイルが配信されている。
$ npm search @types
#> @types/node | TypeScript… | =types | 2017-06-12 | |
#> @types/lodash | TypeScript… | =types | 2017-06-12 | |
#> @types/jquery | TypeScript… | =types | 2017-06-02 | |
#> @types/react | TypeScript… | =types | 2017-06-12 | |
#> ...
型定義ファイルの作成については今回未調査です