ryoのぼやき

技術についての学習内容のまとめです。

【TypeScript】基本型のまとめ

この記事の内容

TypeScriptは体系的に学んだことなかったので、
改めて公式ドキュメントを読んでいくことにしました。

その1回目として、Basic Typesの章を読んだので、そのまとめをまとめておこうと思います。

TypeScript: Handbook - Basic Types

Boolean / Number / String

JavaScriptと同じ。
真偽値、数値、文字列(順不同)。

Array

配列を表す型。
書き方は2通りある。

let list1: number[] = [1, 2, 3]; // 1. 型名[]
let list2: Array<number> = [1, 2, 3]; // Array<型名>

Tuple

Arrayのそれぞれの要素に対して、型指定して宣言ができる型。

let x: [string, number];
x = ["hello", 10];// OK
x = [10, "hello"];// Error

要素には、配列と同じようにアクセスできる。

console.log(x[0].substring(0)); // "hello"

Enum

数値をひとまとまりにして、名前付けして型宣言ができる。いわゆるEnum
値を指定しなければ0からインクリメントされた値となる。
指定すれば、そこからインクリメントされた値となる。
※以下の例参照

enum Color {
    Red = 1,
    Green,
    Blue = 18,
    Pink,
}
console.log(Color.Red)// 1
console.log(Color.Green)// 2
console.log(Color.Blue)// 18
console.log(Color.Pink)// 19

index指定してアクセスすることで、名前を取得できる。

console.log(Color[1])// Red
console.log(Color[2])// Green
console.log(Color[18])// Blue
console.log(Color[19])// Pink

Unknown (v3.0 ~)

未定の型としてunknownで定義できる。
ユーザーの入力値によって、動的に型が変わりうる場合などに使用する。

let notSure: unknown = 4;
notSure = "maybe a string instead";

// OK
notSure = false;

また型ガードetcによって型が定まるまで利用できない。

declare const maybe: unknown;
const aNumber: number = maybe; // Error: Type 'unknown' is not assignable to type 'number'

if (maybe === true) {
    const aBoolean: boolean = maybe;// OK, 型ガードによってmaybeはbooleanになっている
    
    const aString: string = maybe;// Error, booleanなので、string型の変数には代入できない
}

if (typeof maybe === "string") {
    const aString: string = maybe;//OK, 型ガードによってmaybeはstringになっている

    const aBoolean: boolean = maybe;// Error, stringなので、boolean型の変数には代入できない
}

Any

3rdパーティライブラリ使うときなどに、型チェックで苦戦することがある。
そういう時の回避策としてany型を使用できる。
any型を指定すると、コンパイル時の型チェックの対象から外れるためコンパイルエラーを回避できる。
しかし、コンパイル時の型チェックこそTypeScriptの恩恵が得られる部分なので、any型は極力使わないようにするべき。

let looselyTyped: any = 4;
looselyTyped.ifItExsists();// OK
looselyTYped.toFixed();// OK

また、any型はネストされた下位のプロパティにも伝播する。

let looselyTyped: any = {};
let d = looselyTyped.a.b.c.d; // aもbもcもdもany型になる

Void

any型がすべての型を含むのとは反対に、型を持たないことを表すのがvoid型。
戻り値を持たない関数の、戻り値の型として使用するのが一般的で、変数の型として使用することはあまりない。
※その場合、null("strictNullChecks"が無効の場合のみ)かundefinedしか割り当てられないため。

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

let unusable: void = undefined;
unusable = null; // OK, ただし '--strictNullChecks' ではない場合

Null / Undefined

undefined値とnull値に対応する型としてそれぞれundefined型、null型が使用できる。
void型と同じく、単体で使用する用途はない。
後述するように union typeによる使用が一般的。

// これらの値しか割り当てできない
let u: undefined = undefined;
let n: null = null;

undefined値とnull値は他のすべてのタイプのサブタイプとなる。
つまり、numberの変数に対し、undefined値やnull値を割り当てられる。

ただ、"—strictNullChecks"指定をしている場合は、null値はunknown型、undefined値はunknown型とvoid型にしか割り当てができなくなる。
例えば、stringかnullかundefinedが入る変数を定義したいときは、以下のようにunion typeを使って、型定義できる。

let nullableString: string | null | undefined = null;

Never

neverは何もないことを表す型。例えば、必ずエラーを投げる関数の戻り値として使用する。
変数は型ガードによって絶対に真とならないように狭められた場合にneverになる。
neverはすべての型のサブタイプとなり、すべての型に割り当てることができる
一方で、どの型もneverのサブタイプとならないので、neverに割り当てることはできない。
(never自身は除く)
any もneverに割り当てることはできない。

let a: string = "hoge";
let func1 = (): never => {
    throw new Error();
};
a = func1();// neverをstringに代入 ※実行時はerror
let an: any = {};
let n1: never = an;// これは不可
let n2: never = func1();// never自身はOK

Object

object型はプリミティブでない型を表す。つまり、number, string, boolean, symbol, null, undefined以外の型である。
object型を使うと、Object.create()のようなAPIをより適切に表せる。

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

//以下はOK
create({ prop: 0});
create(null);

//以下はNG
create(51);
create("string");
create(false);
create(undefined);

Type Assertions (型ガード)

開発者の方がTypescriptより値の型を知ってるケースがあり、そういう場合にType assertionsを使うことで、型をコンパイラに伝えれる。
ほかの言語でいうところのキャスト。
方法は2通りある。

  • asによる型ガード
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
  • <>による型ガード
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

どちらを使うかは好みによるが、TSXではasによるものしか使用できない。

まとめ / 感想

改めて学習すると、知らなかった仕様を色々知ることができました。
(strictNullChecksの有無による違いなど)
引き続き、次の章も進めていきたいと思います。