はじめに
私がまとめたTypeScriptに関する記事一覧になります。もし興味がありましたらご覧になってください。
今回は、TypeScriptの基本的な文法・型についての記事になります。
TypeScriptにおける型の階層構造
A の型の値が B の型に代入可能なとき、「A は B のサブタイプ」で「B は A のスーパータイプ」という関係にある。
TypeScript の型のスーパータイプ / サブタイプの関係は以下のようになっている。
unknown はすべての型のスーパータイプであり、never はすべての型のサブタイプである。
なおこういった場合、unknown のような存在をトップ型(top type)と呼び、never のような存在をボトム型(bottom type)と呼ぶ。
型の階層構造が上記のようになっているということは以下のように代入可能ということ。
index.ts
const a: 'hello' = 'hello'; // String Enum type ('hello' type)
const b: string = a; // String type
const c: any = b; // Any type
const d: unknown = c; // Unknown type
基本的な型
any
- any型はどんな型でも代入を許す
- any型は型チェックされない
- 型推論できない変数は暗黙的にany型になる
- サバイバルTypeScript any型
let value: any;
value = 1; // OK
value = "string"; // OK
value = { name: "オブジェクト" }; // OK
// Parameter 'name' implicitly has an 'any' type.
function hello(name) {
console.log(`Hello, ${name.toUpperCase()}`);
}
hello(1); // name.toUpperCase is not a function
unknown
- unknown型は型安全なany型
- unknown型の値は具体的な型へ代入不可
- unknownと型の絞り込み(型ガード、型ガード関数)
- サバイバルTypeScript unknown型
let value: unknown;
value = 1; // OK
value = "string"; // OK
value = { name: "オブジェクト" }; // OK
const value: unknown = 10;
const int: number = value; // NG.
// 型ガード
const value: unknown = "";
if (typeof value === "string") {
// ここブロックではvalueはstring型として扱える
console.log(value.toUpperCase());
}
// 型ガード関数
function isObject(value: unknown): value is object {
return typeof value === 'object' && value !== null;
}
const value: unknown = { a: 1, b: 2 };
// 型ガード
if (isObject(value)) {
// ここでは、valueはobject型として扱える
console.log(Object.keys(value));
}
boolean
- 論理型(boolean type)は、trueとfalseの論理値からなる型
- サバイバルTypeScript boolean型
const isOk: boolean = true;
const isNg: boolean = false;
string
- ダブルクォートでもシングルクォートでもまったく同じ文字列型
- テンプレートリテラル(バッククォート`で囲んだ文字列)
- サバイバルTypeScript string型
const message: string = "Hello";
const count = 10;
console.log(`現在、${count}名が見ています。`);
number
- 整数と少数の区別がない
- 2進数、8進数、16進数
- 数値の区切り文字(numeric separators)
- 特殊な数値(NaNとInfinity)
- サバイバルTypeScript number型
const integer: number = 123;
const decimal: number = 1.23;
0b1010 // 2進数
0o755 // 8進数
0xfff // 16進数
100_000_000 // 1億
const price = parseInt("百円"); //NaN
const infinity = 1 / 0; // Infinity
bigint
- 数値型(2の53乗)よりも大きな整数を扱えるプリミティブ型
- bigint型のリテラルは整数値の末尾にn
- 計算速度はbigintよりも通常の数値(number)の方が高速
- サバイバルTypeScript bigint型
const x: bigint = 100n;
literal
- リテラル型はstring、number、booleanの3種類の型で作成が可能
- リテラル型はマジックナンバーやステートの表現に用いられられる。その際、ユニオン型と組み合わせることが多い
- サバイバルTypeScript リテラル型
const isTrue: true = true;
const num: 123 = 123;
const str: "foo" = "foo";
let num: 1 | 2 | 3 = 1;
object
- 基本的に{}を使って、型アノテーションを行う。
- サバイバルTypeScript オブジェクト型
let profile: {name: string} = { name : "Hiro" };
profile = {birthday: 1912} // エラーが発生。
array
- 配列にはT[]構文、Array構文の2つの型定義が存在
- サバイバルTypeScript 配列型
let array: number[];
array = [1, 2, 3];
let array: Array<number>;
array = [1, 2, 3];
tuple
- 要素数と型が固定された配列
- サバイバルTypeScript タプル型
function tuple() {
//...
return [1, "ok", true];
}
const list: [number, string, boolean] = tuple();
null
- nullは値がないことを示す値
- nullに対してtypeofを用いると"object"が返るので注意
- サバイバルTypeScript null型
const x: null = null;
console.log(typeof null); // object
undefined
- 未定義を表すプリミティブな値
- 変数に値がセットされていないとき、戻り値が無い関数、オブジェクトに存在しないプロパティにアクセスしたとき、配列に存在しないインデックスでアクセスしたときにundefinedが返る
- サバイバルTypeScript undefined型
let name;
console.log(name); //undefined
function func() {}
console.log(func()); //undefined
const obj = {};
console.log(obj.name); //undefined
const arr = [];
console.log(arr[1]); //undefined
void
- 戻り値がない関数の戻り値を型注釈するにはvoid型(console.log()など)
- サバイバルTypeScript void型
function print(message: string): void {
console.log(message);
}
never
- 「値を持たない」を意味するTypeScriptの特別な型
- 値を持たないとは、『例外が必ず発生する関数の戻り値』や『終了しない関数(無限ループ)の戻り値』
- never型には何も代入できない
- never型はどんな型にも代入できる
- サバイバルTypeScript never型
function throwError(): never {
throw new Error();
}
function forever(): never {
while (true) {} // 無限ループ
}
const foo: never = 1; // NG
const nev = 1 as never;
const a: string = nev; // 代入OK
const b: string[] = nev; // 代入OK
type alias(型の別名)
- 名前のついた型のこと。
- typeキーワードを使う。
- サバイバルTypeScript type alias
// プリミティブ型
type Str = string;
// リテラル型
type OK = 200;
// 配列型
type Numbers = number[];
// オブジェクト型
type UserObject = { id: number; name: string };
// ユニオン型
type NumberOrNull = number | null;
// 関数型
type CallbackFunction = (value: string) => boolean;
intersection(交差)
- T & U のように書き、『T型でもありながらU型でもある』を意味する型
- 『オブジェクト型を活用した新しい型を作る』という用途で使われる
- サバイバルTypeScript intersection型
// 既存の型を組み合わせる
type RedColor = {
red: number;
};
type BlueColor = {
blue: number;
};
// [RedColor]と[BlueColor]を組み合わせる
type RedColorAndBlueColor = RedColor & BlueColor;
const PurpleColor: RedColorAndBlueColor = {
red: 50,
blue: 50,
};
union(合併)
- T | U のように書き、『T型またはU型』を意味する型
- サバイバルTypeScript union型
// 複数の型を許容する
let value: number | string = 1;
value = "foo";
value = 2;
symbol
- プリミティブ型の一種で、その値が一意になる値
- シンボルはシンボル名が同じであっても、初期化した場所が違うとfalse
- サバイバルTypeScript symbol型
const s: symbol = Symbol();
const s1 = Symbol("foo");
const s2 = Symbol("foo");
console.log(s1 === s1); // true
console.log(s1 === s2); // false
enum(列挙型)
- 定数のセットに意味を持たせたコード表現
- サバイバルTypeScript 列挙型
enum Position {
Top = 1, // 1
Right, // 2
Bottom, // 3
Left, // 4
}
型アサーション
-
式 as 型
という構文で、その式の型を強制的に変えるという意味 - 『値の変換』ではなく、TypeScriptコンパイラが認識する『型』だけが変化することに注意
- 多用するとバグの温床になるため極力使用しない
- サバイバルTypeScript 型アサーション
function getFirstFiveLetters(strOrNum: string | number) {
const str = strOrNum as string;
return str.slice(0, 5);
}
// Helloが出力
console.log(getFirstFiveLetters('Hello! World!'));
// ランタイムエラーが発生
console.log(getFirstFiveLetters(123));
as const
- 型アサーションと構文が似ているが、型を変換する危険な機能ではない。
-
式 as const
の構文で記述。 - 基本的には
as const
が付けられた式に登場する各種リテラルを『変更できない』ものとして扱う。 - サバイバルTypeScript as const
// 1. 配列リテラルの型推論結果を配列型ではなくreadonlyのタプル型にする
// const names: readonly ["Hiro", "Hana", "Masa"]
const names = ['Hiro', 'Hana', 'Masa'] as const;
/* 2. オブジェクトリテラルから推論されるオブジェクトのプロパティが全てreadonlyになる。
const person: {
readonly name: "Hiro";
readonly age: 20;
}
*/
const person = { name: 'Hiro', age: 20 } as const;
/* 3. 文字列・数値・Bigint・真偽値リテラルに対して
付けられるリテラル型が広がらないリテラル型に固定される。
*/
// let Hiro1: stringと推論される。
const wideningHiro = 'Hiro';
let Hiro1 = wideningHiro;
// as const で定義したらlet Hiro2: "Hiro"と推論される。
const notWideningHiro = 'Hiro' as const;
let Hiro2 = notWideningHiro
参考