概要
知人、社内向けに書きます。
型ってなんだよレベルは脱出してる前提です。
参考
- 公式サイト - Typed JavaScript at Any Scale.
- 公式リファレンス - TypeScript Documentation
-
TypeScript Playground
- Javascriptコードへのコンパイル結果の確認や型チェックを行いながらTypeScriptのコードを書けるツール
型について
###明示的な型付け
TypeScriptでは明示的に型付けを行うことが可能。
let umaru : number;
umaru = 108;
umaru = '108'; //エラー
変数名に続けてコロン:と型をを書くことで変数の型を指定することが可能。
上記コードは変数umaruはnumber型と明示している。
number型である108は代入可能だが、string型である'108'は代入不可。
function double(n: number): number {
return 2 * n;
}
function errorFunc(n: number): number {
return n.toString(); //エラー
}
関数の引数は変数の型付けと同様の方法で型を指定することが可能。
関数の戻り値の型を明示する場合は、引数を取り囲むカッコの直後にコロン:と型を書けばOK。
###暗黙的な型付け
コードから型を推測し、変数や関数に型を付与してくれる。あざす。
let umaru = 108;
umaru = '108'; //エラー
この例だと変数umaruが108という数値、即ちnumber型で初期化されている。
この時点でumaruはnumber型に確定となり、続くstring型である'108'の代入は、型が異なることによるエラーとなる。
function double(n: number) {
return n * 2;
}
let umaru = double(1);
//→ umaru = 2
umaru = 'taihei'; //エラー
関数の戻り値の型を指定しないパターン。
この場合、関数doubleの戻り値は関数内のコードからnumber型と推測することが可能。
変数umaruについてもnumber型と確定され、number型以外の値の代入はエラーとなる。
このようにTypeScriptは型を推測しながらエラーがないかチェックしてくださる。
すべての型を我々開発者側で明示する必要は無い。
???「わぁいTypeScript あかりTypeScript大好き」
基礎的な型
標準で使える型は以下の通り。
型名 | 内容 |
---|---|
string | 文字列 |
number | 数値 |
boolean | 真偽値 |
any | 型チェックを行わない |
Array | 配列 |
Tuple | 組み合わせ |
Object | オブジェクト |
null | null |
undefined | undefined |
void | 他のどの型でもない |
-
string
ダブルクォーテーション「"」やシングルクォーテーション「'」で括った文字列はstring型となる。 -
number
JavaScriptで扱える数値を表す。
10進数だけじゃなくて、2,8,16進数も扱える。 -
boolean
trueもしくはfalseのどちらかを取る。 -
any
anyは型チェックを必要としないという意思表示となる。
function double(n: any) {
return 2 * n
}
var umaru_chan = double('42'); //①エラーとならない
①で関数doubleを引数'42'を指定して呼び出しているが、エラーにはならない。
・・・が実行時にもエラーにはならずNaNが帰ってくる。
anyはあまり使わないようにしよう。
-
ArrayとTuple
Arrayは配列のことで、長さが特に決まっていないものを指す。
また、配列の中身は1つの型しか指定出来ない。
Array型に続けて中身の型を<と>で囲んで指定するか(①)、中身の型に続けて[ ]を書くことで(②)配列の型を表すことが出来る。
var arrayUmaru1: Array<number> = [1,2,3]; //①
var arrayUmaru2: number[] = [1,2,3]; //②
それに対しTupleは長さを明示する必要があるが、中身に異なる型を指定することが出来る。
var tuple: [number, string] = [1, 'foo'];
-
Object
オブジェクトも型として表すことが出来る。
function area(rect: {length: number, width: number}) {
return rect.length * rect.width;
}
var umaru1 = area({length: 3, width: 4});
var umaru2 = area({length:3}); //エラー -= ∧_∧
var umaru3 = area({width: 4}); //エラー =と(`・ω・´)
var umaru4 = area({length: 3, width: 4,extra: 'hoge'}); //エラー -=/ と_ノ
関数areaの引数の型として**{length: number, width: number}を指定している。
つまり、number型であるlengthプロパティと、number型であるwidth**プロパティのみを持つオブジェクトを受け付けるという意味。
コードの最後の3行のように、必要なプロパティが足りなかったり、型定義に存在しないプロパティが存在している場合はエラーとなる。
-
nullとundefined
null型とundefined型は、それぞれJavaScriptにおけるnullとundefinedに対応。
TypeScript標準の動作では、これら2つの型はすべての方に対して代入可能。
var umaru1: number = null; //エラーにならない
var umaru2: number = undefined; //エラーにならない
・・・がこのままだと各変数がnullかundefinedではないことを確かめてからでないと使えない。
面倒なので、オプションであるstrictNullChecksをオンにすることを推奨。
オンにすることで、それぞれの型もしくはanyのみが代入可能となる。↑のコードはエラーとなる。
設定方法は割愛。
-
void
voidは他のどの型でも無いことを示す型。
function log(umaru: any){
console.log(umaru);
}
この関数の返り値は無し。コンパイルして型宣言を出力させると以下の通りとなる。
declare function log(umaru: any): void;
返り値の型としてvoidが付与されていることが確認できる。
新たな型を作成
前述の基礎的な型を使って新しい型を定義することができる。
-
interface
interface UmaruChan {
length: number;
width: number;
}
function area(umaruChan: UmaruChan){
return umaruChan.length * umaruChan.width;
}
var umaru1 = area({length: 3, width: 4});
var umaru2 = area({length:3}); //エラー ∧,,∧
var umaru3 = area({width: 4}); //エラー ( 'ω' )つ
var umaru4 = area({length: 3, width: 4,extra: 'hoge'}); //エラー (m9 \
number型であるlengthプロパティと、number型であるwidthプロパティを持つ、新しい型UmaruChanを定義。
また、定義したプロパティは過不足持ってなければならない。エラーとなる。
これだと使いづらいことがあるので、指定が任意であるプロパティを定義することができる。
interface NetworkOptions {
timeout?: number;
numofRetry?: number;
}
var options01: NetworkOptions = {
};
var options02: NetworkOptions = {
timeout: 3000
};
var options03: NetworkOptions = {
numofRetry: 3,
};
var options04: NetworkOptions = {
timeout: 3000,
numofRetry: 3,
};
var option05: NetworkOptions = {
timeout: 3000,
numofRetry: 3,
umaruChan: 1, //エラー!!!!!!!!!!!!!!!!!!!!!!!
}
例としてネットワーク通信の設定用オブジェクトを定義。
timeoutプロパティは通信がタイムアウトするまでの時間を、
numofRetryはリトライ回数を指定するためのプロパティという想定で定義。
これらが指定されていない場合、それぞれのデフォルト値を用いてネットワーク通信を行うものとする。
timeoutプロパティおよびnumofRetryプロパティそれぞれの末尾に「?」が書かれているが、
これを書くことで指定が任意であることを指示することができる。
NetworkOptions型を使用する際、timeoutプロパティとnumofRetryプロパティについては任意の指定が可能となるが、
型定義に存在しないプロパティの指定はエラーになる。
-
Type Aliasing
type Id = number
以上のように、型に別名をつけることも可能。ここではId型をnumber型の別名として定義している。
-
リテラル型
ある特定の数値や文字列も型として利用することが可能。
type One = 1;
type Umaru = 'umaru';
var one: One = 2; //エラー
var umaru: Umaru = 'taihei'; //エラー
One型は1型の別名、Umaru型は**'umaru'型の別名として定義。それぞれ1と'umaru'以外の値は代入不可。
これをリテラル型と呼ぶ。次のUnion Types**と併用すると便利になる型。
-
Union Types
Union Typesは和集合を定義することが可能。
type Action = {
type: 'increment';
} | {
type: 'add';
payload: number;
};
function increaser(state: number, action: Action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'add':
return state + action.payload;
}
}
ここでは、操作を表すActionを、typeプロパティが**'increment'型であるものと'add'型であるもののいずれかをとる型として定義。片方にのみpayloadプロパティを定義してみた。
関数increaser**の内部では、switchによって処理を分岐させている。
このとき、TypeScriptはUnion Typesを構成する型のうち、その文脈における型を判別してくれる
例を上げると、case 'increment'節ではpayloadプロパティが存在しないことを検知してくれるので、action.payloadが存在しないことをエラーで出力してくれる。
const action : Action = {
type: 'add',
};
console.log(increaser(1,action));
//出力エラー↓
Type '{ type: "add"; }' is not assignable to type 'Action'.
Property 'payload' is missing in type '{ type: "add"; }' but required in type '{ type: "add"; payload: number; }'.
-
Genetics
ジェネリックとジェネリクスのどっちが一般的なんでしょうね。
function double(n: number) {
return 2 * n;
}
function concat(str: string) {
return str + str;
}
// 関数 doubleCall を定義して以下のように使いたい
doubleCall(2,double);
doubleCall('abc',concat);
関数doubleはnumber型を受け取りnumber型を返し、関数concatはstring型を受け取りstring型を返す。
これらの関数を2回呼ぶ関数doubleCallを定義したい、こういった場合にGenericsの登場
interface SameTypeFunction<T> {
(src: T): T;
}
function doubleCall<T>(src: T, func: SameTypeFunction<T>) {
return func(func(src));
}
function double(n: number) {
return 2 * n;
}
function concat(str: string) {
return str + str;
}
function toStrUmaru(n : number) {
return 'UMARU';
}
doubleCall<number>(2,double);
// 型推論によって明示的なパラメータ指定が不要になっている。
doubleCall(2,double);
doubleCall('abc',concat);
doubleCall(2,toStrUmaru); //エラー
関数名の直後において <> で型パラメータ名を括って型指定をすべき箇所に型パラメータ名を指定する。
SameTypeFunction型はなんらかの型Tを受け取りTを返す関数を定義している。
doubleCall関数の第2引数の型としてSameTypeFunction型をしているが、この型パラメータもGenericsによって指定可能としている。
Genericsを用いて型を使用する際は通常、 <> にパラメータを指定する必要がある。
ただし、doubleCall関数では第1引数によってT型によって推論可能であるため、型パラメータの指定無しで呼び出すことも可能。
最後の例のようにnumber型をとりstring型を返すtoStrUmaru関数を用いたdoubleCall**関数の呼び出しはエラーとなる。
SameTypeFunctionによる制約に違反するため。制約と誓約
-
Optional Chaining
TypeScript3.7から利用可能となった
interface Options {
actions?: {
callback: () => void;
}
}
function area(props: Options){
if (props.actions) {
props.actions.callback()
}
}
Options型では、プロパティactions型が存在する場合と存在しない場合があるが、上のコードのようにactionsプロパティが存在するか確かめる必要がある。
Optional Chainingは、明示的なチェックを省いてプロパティの値を参照可能とするもの。下記のように書ける。
//「?.」の左側に指定されているプロパティが定義されていなければ何もしない
function area(props: Options) {
props.actions?.callback()
}