ぐっちょむの開発にっき

プログラミングの話をするブログ

TypeScript入門者がハマりがちな記法(文法)の呼び方と簡易解説まとめ

この記事は、かつてTypeScriptで見かける記法が何と呼ぶのかすら分からず困っていた頃の自分へ向けて送るものですが、現在TypeScriptへ入門中の方々にも役立つかなと思い公開する次第です。正確な内容に関してはぜひ公式をご覧ください。

内容の修正や項目の追加などは随時承ります。

Type Annotation

こんな奴

let foo: number

foo = 777 // OK
foo = 'hoge' // コンパイルエラー

日本語での呼び名

  • 型注釈
  • 型アノテーション

ふわっと紹介

値を初めて定義する際によく使われます。 値に初期値が存在する場合は型推論が働き、let foo = 777のような宣言でもfooの型がnumber型であることをコンパイラが推定してくれます。初期値の無い宣言で使われることがほとんどですが、代入時の推論には無理がある場合などでもちょこちょこ使います。

参考文献


Type Assertion

こんな奴

const foo = <Bar> bar // <Type> value

const foo = bar as Bar // value as Type
  • <Type> valueもしくはvalue as Typeという、式として等価な2通りの記法が存在します。
  • 可能ならばvalue as Typeでの記述が推奨されます
  • <Type> Valueでの記述はJSX構文と衝突するためTSXファイルで使用できません

日本語での呼び名

  • 型アサーション

ふわっと紹介

型アサーションは値の持つ型情報を実行時の値とは別に上書きします実際の値の型とアサーションの型が確実に一致するとき以外は使用すべきでありません。

コード付き解説

const element: HTMLElement

// 代入される型を**無視して**`bar`の型が`number型`_となります。
const foo = inputElement as HTMLInputElement
const foo = bar.baz() as string // 2.式の型情報を上書き
const foo = (bar as string).toLowerCase() // 3.括弧によるアサーションの評価順の変更

上記例では、bar#baz()メソッドが返す値とは別にfoostring型と見做されます。

また、型アサーションは式であるため、括弧によって評価順を変更することも可能です。 例では、barの型情報をstring型に強制しているため、Stringオブジェクトの持つ#toLowerCase()メソッドが使用可能です。

ここから少し深入りします。

const num = 777
num.toUpperCase() // コンパイルエラー。

#toUpperCase()メソッドはnumber型に存在しない、Stringオブジェクトのメソッドであるためコンパイルエラーとなります。というわけで型アサーションを書き加えますが、

const num = 777 as string // コンパイルエラー
num.toUpperCase()

このような、明らかに間違った型によるアサーションもコンパイルで弾かれます。

const num = 777 as any
num.toUpperCase() // コンパイルは成功するものの実行時にエラーが投げられる

まずas anyによりnumの型はany型へと上書きされnumber型ではなくなります。

any型は任意の型として扱えるので Stringオブジェクトのメソッドである#toUpperCase()を呼び出すコードがコンパイルでは通りますがしかし、実行時にはエラーを吐きます。any型の扱いには気をつけましょう。


Generics

こんな奴

  • Foo<T>
  • new Foo<Bar>()
  • foo<Bar>()

日本語での呼び名

  • ジェネリック
  • ジェネリクス
  • (特にを指して)ジェネリック型

ふわっと紹介

例えばArray<T>という型は「任意で特定の型としてTを内包するArray」という意味になります。

Array型はそれ自体の定義時にどのような型を内包するのかは不明ですが、実際に使用される際にはインスタンスの内包する型が定まっています。例えばArray<number>という表記はnumber型のみで構成されたArrayであるというを意味します。

つまり、クラスやインターフェイスの宣言時には特定できない、ある時点で判明する型を別途指定するための仕組みがジェネリクスです。

※先述のType AssertionとGenericsとは似て非なる別物です。ご注意ください。

コード付き解説

Array<T>React.Component<P, S>などという型として用いられている場合が最初に遭遇するケースとして多いかと思います。

functionでの利用例

// この時点でTは任意の型となり得る。
function toArray<T>(...args: T[]): T[] { return args }

// "words"の型は引数の型から推論され、string[](== Array<string>)となる。
const words = toArray('foo', 'bar', 'baz')
words.push('hoge') // OK
words.push(1) // コンパイルエラー

クラスでの利用例

class MyObject<T> {

  members: T[]

  constructor(...members: T[]) {
    this.members = members
  }

  pick(index: number): T {
    return this.members[index]
  }

  append(member: T): void {
    this.members.push(member)
  }

}

// コンストラクタからの型推論によりpick()メソッドの戻り値はstring型となるため、以下のようなチェーンも可能。
const withString = new MyObject('foo', 'bar', 'baz')
const str = withString.pick(1).toUpperCase() // 'BAR'

// 以下の記法は"generic parameter"とも呼ばれ、コンストラクタでは型が指定できない場合などに用いる。
const withNumber = new MyObject<number>()
withNumber.append('foo') // コンパイルエラー

React Component定義での利用例

import React from 'react'

export interface MyProps {
    hoge: string
}

export interface MyState {
    fuga: string
}

export default class MyComponent extends React.Component<MyProps, MyState> {
  /* 任意の実装 */
}

Reactが提供するComponentクラスの場合は継承時にPropsとStateの型が定まるため、実際に使用する際には以下のようなコードになります。

参考文献


以上、2項目でした。

項目は思いつきor要望があり次第、随時追加予定です。


  1. C#の主要開発者であるMicrosoft社はTypeScriptの生みの親