Typescript の 基本的な型について

Typescript の型定義ファイルを書くために 基本的な型から学び直しています。
ほとんど公式ドキュメントの直訳になってしまいました。

参考リンク:Basic Types ・Typescript

なお、このエントリーはマークダウン記法により記述されています。 Thanks はてなブログを高速でかけるチートシート

基本的な型について

boolean

もっともシンプルなデータ型で、値は true または false になリマス。

let isActive: boolean = false

Number

Typescript は Javascript 同様に全ての number は浮動小数点数です。
16進数、10進数、 8進数、2進数に対応しています。
浮動小数点数とは

「「123.45」を「12345×10^-2」と表現するように、数字を仮数、基数、指数の要素で表現すること。「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

let decimal: number = 6;  <- 10進数
let hex: number = 0xf00d;  <- 16進数
let binary: number = 0b1010;  <- 2進数
let octal: number = 0o744;  <- 8進数

10進数以外を扱うのは高校ぶりです。。。 10進数以外はこれを参照:Hexadecimal

String

テキストデータの型です。ダブルクォート ( " ) か、シングルクォート ( ' ) でデータを囲います。

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.`;

組み込み式は単に文字列を足していくよりもずっと見やすくなるので重宝してます。

Array

配列の型を記述する方法は2つあります。
1つは要素の型の後ろに [] をつける方法です。

let list: number[] = [1, 2, 3];

もう一つはジェネリクスタイプを使う方法です。

let list: Array<number> = [1, 2, 3];

こんな方法があるんですね・・・。知らなかった。

Tuple

タプル型とは、固定された数の要素型がわかっている配列を記述したいときに使います。要素型が同じである必要はありません。
例えば、 string number とのペアの値を扱いたいときこうなります。

// タプル型を宣言する。
let x: [string, number];
// 初期化
x = ["hello", 10]; // OK
// 初期化失敗
x = [10, "hello"]; // Error 上記にて、x[0] には文字列であるように型定義しているから弾かれるんですね!

じゃあ、型定義した配列の数を超えた要素にアクセスしようとした時はどうなるか?
答えは、ユニオンタイプが適用されるみたいです。

x[3] = "world"; // OK, 'string' は 'string | number' (ユニオン型)に割り当てることができる。

console.log(x[5].toString()); // OK, 'string' and 'number' は両方とも 'toString'メソッドを持つ。
x[6] = true; // Error, 'boolean' 型は 'string | number' 型に割り当てることはできない。

Enum

性質が似たものを列挙したいときに使う型です。 number と親和性が高いです。

enum 型では、初期値として列挙されているメンバーたちに 0 からはじまる値が格納されています。

enum Color {Red, Green, Blue} // => Red = 0, Green = 1, Blue = 2
let c: Color = Color.Green; //=> 1

この値は手動で変えられます。最初の値を1 に変更するとそれ以降、2,3,4....と代入されていきます。

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green; // => 2

順番をこだわる必要がないなら、列挙されているメンバーに自由に値を割り当てることができます。

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Blue; //=> 4

上記では値の名前から数値にアクセスしましたが、その逆もできます。つまり、数値から値の名前にアクセスできます。

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName); //=> Green

any

なんでもありの型です。Typescript 使いが白旗をあげるときにつかう型だと思っています・・・。

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // => おーるおっけー!!

any と似た型に Object があります。 違いは、any は値の割り当て、メソッドの呼び出し両方が可能なのに対して、Object では値の割り当てのみが可能で、メソッドの呼び出しができません(本当は存在するのにも関わらず)

let notSure: any = 4;
notSure.toFixed(); // okay ただしコンパイラは型チェックしない。
let prettySure: Object = 4;
notSure.toFixed(); // error プロパティ 'toFixed' は 'Object' 型に存在しません。

any は一部のデータ型がわかっているときに便利だそうです。例えば、型が混在した 配列とか。

let list: any[] = [1, true, "free"];
console.log(list,'割り当て前') // => [1, 100, "free"] え・・・、**list[1]**は**true**じゃないの・・・ 
list[1] = 100;
console.log(list,'割り当て後') // => [1, 100, "free"] 

Void

any 型のおよそ対極にいます。なんの型も持たない型です。

function warnUser(): void {
    console.log("This is my warning message");
}

void型をもつ変数は undefinednullしか代入できません。

let unusable: void = undefined;

Null and Undefined

Typescript では、Null と、undefined はそれぞれ、Null 、undefined 型を持ちます。 デフォルトでは、nullundefined は全ての型の子の型なため、例えば number などにも割り当てできます。
それが気持ち悪いのであれば、tsconfig-strictNullChecksを書き足してください。void にしか割り当てできなくなります。

Never

never型は存在しない値の型を示します。例えば、関数の戻り値の型に never型を定義した場合、期待される戻り値は 例外を投げる、もしくは戻り値がない表現です。

、、、と直訳してみましたがさっぱり意味がわからなかったので先人の知識を参照にしました。 Typescript 型入門

union型を触り始めるとたまに出てくるのがnever型です。never型は「属する値が存在しない型」であり、部分型関係の一番下にある(任意の型の部分型となっている)型です。どんな値もnever型の変数に入れることはできません。

// エラー: Type '0' is not assignable to type 'never'.
const n: never = 0;

一方で、never型は全ての型の子の型なので never型の値はどんな型にも割り当てることができるんですね。

// never型の値を作る方法が無いのでdeclareで宣言だけする
declare const n: never;

const foo: string = n;

上記の記事の中で、neverが使われる具体例がわかりやすかったですね。switch casethrow err文ですね。

interface Some<T> {
  type: 'Some';
  value: T;
}
interface None {
  type: 'None';
}
type Option<T> = Some<T> | None;

function map<T, U>(obj: Option<T>, f: (obj: T)=> U): Option<U> {
  switch (obj.type) {
    case 'Some':
      return {
        type: 'Some',
        value: f(obj.value),
      };
    case 'None':
      return {
        type: 'None',
      };
    default:
      // ここでobjはnever型になっている
      return obj;
  }
}

実はこの中でobjの型はneverになっています。なぜなら、それまでのcase文によってobjの可能性が全て調べ尽くされてしまったからです。これが意味することは、実際にはdefault節が実行される可能性は無く、この中ではobjの値の候補が全く無いということです。そのような状況を、objにnever型を与えることで表現しています。

function func(): never {
  throw new Error('Hi');
}

const result: never = func();

関数の返り値の型がnever型となるのは、関数が値を返す可能性が無いときです。これは返り値が無いことを表すvoid型とは異なり、そもそも関数が正常に終了して値が返ってくるということがあり得ない場合を表します。上の例では、関数funcは必ずthrowします。ということは、関数の実行は中断され、値を返すことなく関数を脱出します。特に、上の例でfuncの返り値を変数resultに代入していますが、実際にはresultに何かが代入されることはあり得ません。ゆえに、resultにはnever型を付けることができるのです。

わかりやすい説明。。。圧倒的感謝!

Object

objectはプリミティブ型 (number string boolean symbol null)以外の型を表現したいときに使います。

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK null はサブタイプだからおっけー?

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

アサーション

現在の型をより明確に定義したいときに使うやつです。他言語では型キャストと呼ばれているみたいですね。型アサーションのやり方は2つあって1つが三角括弧を使う方法。

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK null はサブタイプだからおっけー?

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

もう一つが as構文を使う方法。

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

お好みでお選びくださいとのことです。