TypeScriptの型について
こちらのドキュメントをGoogle翻訳よりもわかりやすく翻訳してみました。
本家にはない説明も加えてありますのでよく読み、用法・用量お守りください。
基本的な型
- はじめに
- Boolean
- Number
- Bigint
- String
- Array
- Symbol
- Tuple
- Enum
- Any
- Unknown
- Void
- Null and Undefined
- Never
- Object
- 型注釈
- letに関するメモ
はじめに
使いやすいプログラムの為に、私たちは「数値」「文字列」「構造」「ブール値」などのもっとも単純なデータ単位を操作できる必要があります。
TypeScriptでは、JavaScriptで期待されるものとほぼ同じ型をサポートしています。
便利な列挙型を利用して、操作を手助けします。
以下、サンプルコードが記述されていますが、TypeScriptの型指定ではJavaScriptとは違い基本的に小文字で始まることに注意してください。
大文字で始まる型宣言は、JavaScriptコードで適切に使用されることはほとんどない非プリミティブのオブジェクトとして参照されてしまうので使用しないでください。
Boolean
最も基本的なデータ型は、単純なtrue/false
値で、JavaScriptおよびTypeScriptでboolean
を使用します。
let isDone: boolean = false;
Number
JavaScriptと同様に、TypeScriptの数値はすべて浮動小数点数です。これらの浮動小数点数はnumber
を使用します。
16進数(hexadecimal)と10進数(decimal)に加えて、TypeScriptではECMAScript 2015で導入された2進数(binary)と8進数(octal)もサポートしています。
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
Bigint
bigint
型は、ECMAScriptの新しい規格として定義されています。(upcoming proposal)
number
型が扱える正確な数値として52bit(2^53-1
bit)ですが、それ以上の値を扱う際に使用する型です。
number
型では最大値を超えると表示上 Number.MAX_SAFE_INTEGER = 9007199254740991
になりますが、
bigint
型を使用するとJavaScriptで扱える最大値 Number.MAX_VALUE = 1.7976931348623157e+308
まで表示することができます。しかし、正確性は保障されないことに変わりはありません。
bigint
型は、型安全保障(type guard)も他の型と同様に利用できます。
number
型とbigint
型は互換性がありません。
2つの型を使って演算を実行するには、型変換(cast)をすることで実行可能になります。参考: mrsekutの備忘録
let foo: bigint = BigInt(100); // BitInt関数により初期化
let bar: bigint = 100n; // BitIntリテラルによる割り当て
let integer: number = 10; // numberリテラルによる割り当て
console.log(foo + bar); // OK
console.log(foo + integer); // Error, Type 'bigint' is not assignable to type 'number'.
console.log(foo + BigInt(integer)); // OK
if (typeof foo === "bigint") { // タイプガードも利用可能
console.log("'foo' is a bigint.");
} else {
console.log("'foo' is a floating-point number.");
}
String
Webページやサーバー用のJavaScriptでプログラムを作成する時に、boolean
、number
に加えてもう一つの型はテキストデータです。
他の言語と同様に、テキストデータを参照するためにstring
を使用します。
また、JavaScriptと同様に、TypeScriptも "二重引用符(double quotes)" または '一重引用符(single quotes)' を使用して文字列データを囲みます。
JavaScriptとTypeScriptではCやC#、Javaとは違いchar
型が存在しないため、一重引用符を使用することができます。
let color: string = "blue";
color = 'red';
テンプレート文字列も使用することができ、複数行にまたがり式を埋め込むことができます。
これらの文字列は `backtick/backquote` で囲まれ、埋め込み式は ${ expr } です。
複数行にまたがって代入するときは、コード内のインデントも反映されてしまうので注意してください。
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.`;
上記の変数sentence
は、次のような宣言と同等です。
let sentence: string =
"Hello, my name is " + fullName + ".\n\n" +
"I'll be " + (age + 1) + " years old next month.";
Array
TypeScriptではJavaScriptと同様に値の配列操作ができます。
配列型は2つの方法のいずれかで記述できます。最初の方法では、要素の型に続いて[]
を使用してその要素型の配列を宣言します。
let list: number[] = [1, 2, 3];
2つ目は、ジェネリクスであるArray<elemType>
を使用する方法です。
let list: Array<number> = [1, 2, 3];
どちらの方法であってもJavaScriptにトランスパイルすると同じ配列になります。
Symbol
symbol
は、ECMAScript 2015から導入されたnumber
やstring
のようなプリミティブなデータタイプです。
symbol
の値は、Symbol
のコンストラクタによって生成されます。
let sym1 = Symbol();
let sym2 = Symbol("key"); // stringキーのオプション
Symbol
は、不変(immutable)であり、一意(unique)です。
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3 // Symbolは一意のため、結果はfalseです
string
型のようにSymbol
はオブジェクトプロパティのユニークキーとして使用できます。
const sym = Symbol();
let obj = {
[sym]: "value"
};
console.log(obj[sym]); // "value"
Symbol
は、オブジェクトプロパティとクラスメンバーのような算出プロパティの宣言としても使用できます。
const getClassNameSymbol = Symbol();
class C {
[getClassNameSymbol](){
return "C";
}
}
let c = new C();
let className = c[getClassNameSymbol](); // "C"
以下、Symbol
が持つ拡張機能です。詳細はハンドブックをご覧ください。
- Symbol.hasInstance
- Symbol.isConcatSpreadable
- Symbol.iterator
- Symbol.match
- Symbol.replace
- Symbol.search
- Symbol.species
- Symbol.split
- Symbol.toPrimitive
- Symbol.toStringTag
- Symbol.unscopables
Tuple
タプル型を使用すると、型はわかっているが同じである必要のない固定数の要素で配列を表現できます。
例えば、文字列と数値のペアとして値を表すことができます。
// タプル型の宣言
let x: [string, number];
// 正しい初期化
x = ["hello", 10]; // OK
// 正しくない初期化
x = [10, "hello"]; // Error
要素が存在するインデックスで要素を取得すると、正しい型が取得できます。
console.log(x[0].substring(1)); // OK
console.log(x[1].substring(1)); // Error, number は substring を持っていません
要素が存在しないインデックスで要素を取得しようとすると、エラーが発生します。
let x: [string, number];
x = ["hello", 10];
x[3] = "world"; // Error, プロパティ '3' は 型 '[string, number]' に存在しません
console.log(x[5].toString()); // Error, プロパティ '4' は 型 '[string, number]' に存在しません
Enum
JavaScriptでは存在していない列挙型ですが、TypeScriptでは列挙型としてenum
があります。
C# のような言語と同様にenum
は、数値のセットによってわかりやすい名前を付ける方法です。
ちなみにJavaScriptでも、連想配列(辞書型)として列挙型のように振舞うことは可能です。
enum Color {
Red,
Green,
Blue
}
let c: Color = Color.Green;
デフォルトでは、列挙型は 0 から採番を始めます。手動で採番をするにはメンバーのいずれかに値を与えると変更することが可能です。
下記の例では、 0 ではなく 1 から採番しています。
enum Color {
Red = 1,
Green,
Blue
}
let c: Color = Color.Green;
また、全て手動で設定することも可能です。
enum Color {
Red = 1,
Green = 2,
Blue = 4
}
let c: Color = Color.Green;
列挙型の便利なところは、その値から列挙型した名前を取得できるところです。
例えば、下記の例では値が 2 であるのにもかかわらず、Color
という列挙型で何にマップされているかわからない場合、対応する名前を検索できます。
enum Color {
Red = 1,
Green,
Blue
}
let colorName: string = Color[2];
console.log(colorName); // value値 2 に対応する 'Green' が表示されます
例では数値をvalueとして設定していますが、文字列も設定することが可能です。
その場合、自動採番はされないので全ての列挙に対して値を設定してください。
enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
let c: Color = Color.Green;
Any
プログラマはアプリケーションの作成時に、型が不明な変数を宣言する場合があります。動的なコンテンツやユーザーまたはサードパーティライブラリ、Native JavaScriptから型が不明な値が返ってくることがあるからです。
プログラマはそれらの不明な型変数をオプトアウトして、コンパイルを通す必要があります。
その時は、any
型で宣言します。
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // 問題なく、booleanを代入できます。
any
型は既存のJavaScriptを操作する協力な方法であり、コンパイル中にタイプチェックを徐々にオプトイン/アウトできます。
let notSure: any = 4;
notSure.ifItExists(); // OK, 実行時には ifItExists はおそらく存在している
notSure.toFixed(); // OK, toFixed は存在している (コンパイラはチェックしない)
let prettySure: Object = 4;
prettySure.toFixed(); // Error, 'Object'型に 'toFixed' は存在しません
Object
は非プリミティブ型です。そのため、使用は推奨していません。Do's and Don'ts
any
型は、使用する型の一部を知っている場合にも便利ですが、それがすべてではありません。
例えば、any
型の配列は様々な型が混在する場合があります。
let list: any[] = [1, true, "free"];
list[1] = 100;
Unknown
unknown
型は、型安全なany
型です。全ての変数宣言でunknown
型を使うことができますが、
unknown
型はそれ自体と型注釈、制御フローベースの型絞り込み機能を持ちません。
つまり、実際に使用する際には型を明確に指定する必要があります。
TypeScript 3.0 unknown - サンプルコードと解説
Null and Undefined
TypeScriptでは、undefined
とnull
の両方が明示的な型としてそれぞれ存在しています。
void
型のように単独ではあまり役に立ちません。
// これらの型指定された変数に対して他の値を割り当てることはできません
let u: undefined = undefined;
let n: null = null;
デフォルトでは、null
とundefined
は他の全ての型の派生型です。
つまりnumber
のようなものにnull
とundefined
を割り当てることができます。
しかし、--strictNullChecks
オプションを使うとnull
とundefined
は、any
型にのみ割り当てが可能です。(例外としてundefined
はvoid
型に割り当て可能です。)
これにより、たくさんの一般的なエラーの助けになります。
一つの変数に対してstring
, null
, undefined
をそれぞれ割り当てたい時は、string | null | undefined
の様な複合型(ユニオン型)を使うことができます。
--strictNullChecks
オプションは使用することを推奨していますが、このハンドブックではオフにしています。
Never
never
型は何も発生しない型として表現されます。
使用例として、常に例外をスローする関数式やアロー関数式、または決してreturn
されないアロー式の戻り値型などがあります。
また、変数は決して真にならない場合、型推論ではnever
型を推論します。
never
型は、全ての型の派生型なので、全ての変数に指定できます。
さらに、型無し(no type)はnever
型の派生型であり、never
型を割り当て可能です。(never
型自身を除いて)
ただし、any
型にnever
型を割り当てることはできません。
never
型を返す関数のサンプルをいくつか紹介します。
// この関数は途中でthrowされているため、完了することはありません
function error(message: string): never {
throw new Error(message);
}
// 上記のerror関数の戻り値がnever型なので、戻り値の型推論結果はnever型です
function fail() {
return error("Something failed");
}
// この関数は無限ループにより、完了することはありません
function infiniteLoop(): never {
while (true) {
}
}
Object
object
型は非プリミティブな型を表します。
object
とは違い、number
, string
, boolean
, bigint
, symbol
, null
, undefined
はプリミティブです。
object
型を使うと、Object.create
のようなAPIを適切に表現することができます。
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
型注釈
TypeScriptプログラマは、TypeScriptコンパイラよりもその変数の型について把握している状況があります。
エンティティについて現在の型よりも正確で具体的な型を理解しているときに起こります。
型注釈は、コンパイラに "trust me, I know what I’m doing." (信じて、私は把握しているよ!)と伝える方法です。
TypeScriptでは、他言語のキャストにあるような、型チェックや変数の再構築はしません。
型注釈は実行に影響はありません。コンパイル時に影響する機能です。TypeScriptは、プログラマが型チェックをしている前提で動作します。
型注釈を設定する方法は2種類あります。
"<>"(angle-bracket)記法と as
記法です。
let someValue: any = "this is a string";
let angleBracket: number = (<string>someValue).length; // <>記法
let asBracket: number = (someValue as string).length; // as記法
2つのサンプルは同等です。どちらを使用するかは好みです。
ただし、JSX
でTypeScriptを使用する場合は as
記法のみ許可されます。
letに関するメモ
既にお気づきかと思いますが、ここまでのサンプルではJavaScriptのvar
の代わりにlet
を使用してきました。
let
はES2015でJavaScriptに導入され、var
よりも安全であるため、現在では標準とみなされています。
JavaScriptの多くの問題はlet
を使用して軽減されるため、可能な限りvar
の代わりにlet
を使用する必要があります。
(詳しくは、「let var scope」で検索してみてください。)