TypeScriptを1.xしか使ったことがない人のための基礎知識

TypeScriptのリリースについて

TypeScriptは2012年10月1日にマイクロソフトによってリリースされた。

JavaScriptをベースに省略も可能な静的型付けとクラスベースオブジェクト指向を加えた厳密なスーパーセットとなっていて使い勝手が良いため、現在でも多くの開発現場で使用されている。

TypeScriptの2.0がリリースされたのは2016年9月22日でその後も3.0, 4.0がリリースされて2021年1月12日現在の最新版はバージョン4.1.3となっている。

TypeScriptは1.xしか知らないと戦力外

TypeScriptはバージョンがアップデートされるたびに新機能が導入されて、1.xの古いバージョンとは別物となっている。

この記事では1.xの静的型付けとクラスベースオブジェクト指向などの基本機能しか知らない人が必ず知っておくべきバージョン2以降の新機能をまとめた。

これらを習得していないのであればTypeScriptができると安易に言わないほうが賢明。

TypeScriptの開発環境はParcelwebpackなどをnpmでインストールすれば簡単に用意できる。

never型

TypeScript 2.0 から導入

any型やvoid型などは知っていてもnever型は知らない人は結構多い。

返り値が何もない場合は:voidだがthrow new Errorやループで返り値が発生しない場合はnever型を使う。

function error(message: string): never {
  throw new Error(message);
}

Numeric Literal Types

TypeScript 2.0 から導入

String Literal Typesはバージョン1.8から存在したが、NumericやBooleanなどString以降はバージョン2以降に遅れて導入された。

function dice(): 1 | 2 | 3 {
  return (Math.floor(Math.random() * 3) + 1) as 1 | 2 | 3
}
const result = dice()
console.log(result)
// 1 or 2 or 3

readonly

TypeScript 2.0 から導入

変数を読み込み専用にする。

オブジェクト、配列、classの値を変更不可にできる。

interface Point {
  readonly x: number;
  readonly y: number;
}

var p: Point = { x: 1, y: 2 };
p.x = 3; // Error, p.x is read-only

String Enum

TypeScript 2.4 から導入

CやJavaなどにあるString EnumがTypeScriptに導入された。

…が、TypeScriptがJavaScriptのスーパーセットであることや、型が安全でないなどの理由により使用されないことが多い。

enum Colors {
  Red = 'RED',
  Green = 'GREEN',
  Blue = 'BLUE'
}
console.log(Colors)
/*
{
  Red: "RED",
  Green: "GREEN",
  Blue: "BLUE"
}
*/

Numeric separators

TypeScript 2.7 から導入

アンダースコアを数値セパレーターをして使用できる。

数値の桁が多い場合は_で分けれおけば桁数の間違えを防止しやすくなる。

const million:number = 1_000_000
console.log(million)
// => 1000000

Improved control over mapped type modifiers

TypeScript 2.8 から導入

-readonlyのように修飾子に-を付けると解除できる。

type Foo<T> = { -readonly [P in keyof T]-?: T[P] }

readonly tuples

TypeScript 3.4 から導入

配列のタプルでreadonlyが使えるようになった。

function foo(arr: readonly string[]) {
  arr.slice(); // okay
  arr.push("hello!"); // error!
}

Stricter Generators

TypeScript 3.6 から導入

厳密なチェックがイテレータとジェネレータでもできるようになった。

function* count() {
  let i = 1
  while (true) {
    if (yield i++) {
      break
    }
  }
  return 4649
}

const iter = count()
let curr = iter.next()
while (!curr.done) {
  console.log(curr.value)
  curr = iter.next(curr.value === 3)
}
console.log(curr.value)
/* console結果
1
2
3
4639
*/
/**
 * - yields numbers
 * - returns strings
 * - can be passed in booleans
 */
function* count():Generator<number, string, boolean> {
  let i = 1
  while (true) {
    if (yield i++) {
      break
    }
  }
  return 4649 // stringではなくnumberなのでエラー
}

const iter = count()
let curr = iter.next()
while (!curr.done) {
  console.log(curr.value)
  curr = iter.next(curr.value === 3)
}
console.log(curr.value)

Nullish Coalescing

TypeScript 3.7 から導入

変数がnullかundefinedのときに後続の処理を行う仕組み。

const foo:any = undefined
const bar = ():void => console.log('Hello!')
let a = foo !== null && foo !== undefined ? foo : bar()
let b = foo ?? bar()

// a と b は同じ結果
console.log(a) // => 'Hello!'
console.log(b) // => 'Hello!'

Optional Chaining

TypeScript 3.7 から導入

変数がnullかundefinedのときに後続の処理を行わない仕組み。

例えばfoo.toUpperCase()はfooの変数がundefinedかnullだとエラーになってしまうが、foo?.toUpperCase()だとエラーにならなくなる。

const foo:any = undefined
const r1 = foo?.trim()
console.log(r1)
// => undefined

const bar:any = undefined
const r2 = bar.trim()
console.log(r2)
// => エラーになる

Uncalled Function Checks

TypeScript 3.7 から導入

呼ばれていないFunctionをチェックする。

interface User {
  isAdministrator(): boolean;
}

function foo(user: User) {
  if (user.isAdministrator) {
    // 処理
  }
}

ECMAScript Private Fields

TypeScript 3.8 から導入

privateを#で記述できるようになった。

#だとブラケット構文の場合はundefinedとなるため完全に読み取り不可となる。

// privateを使用する場合
class Person {
  private name: string

  constructor(name: string) {
    this.name = name
  }

  hello() {
    console.log(`Hello, ${this.name}!`)
  }
}

const ken = new Person('Ken')
ken.hello()
// => Hello, Ken!

console.log(ken['name'])
// => Ken

console.log(ken.name)
// Error!
// #を使用する場合
class Person {
  #name: string

  constructor(name: string) {
    this.#name = name
  }

  hello() {
    console.log(`Hello, ${this.#name}!`)
  }
}

const ken = new Person('Ken')
ken.hello()
// => Hello, Ken!

console.log(ken['#name'])
// undefined

console.log(ken.#name)
// Error!

export * as

TypeScript 3.8 から導入

ECMAScript 2020からexport * as が使えるようになった。

export * as utilities from "./utilities.js";

Short-Circuiting Assignment Operators

TypeScript 4.0 から導入

// これを
a = a && b;
a = a || b;
a = a ?? b;

// このように書ける
a &&= b;
a ||= b;
a ??= b;
let a = 0
const b = 2

// a = a || b と同じ
a ||= b

console.log(a)
// 2

補足

この記事に書かれているのはTypeScript バージョン2以降で導入された新機能のごく一部にすぎないので、さらに詳しく知りたい方は公式サイトをご参照下さい。

The TypeScript Handbook