白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

TypeScriptのtype(型エイリアス)がややこしいけど面白い

こんにちは。
本が読みたい気持ちと首が痛い気持ちが拮抗しています。
さて、前回がinterfaceの比較だけで終わってしまったので、typeについて見ていきます。

アノテーション(Type Annotation)

とりあえず型を使ってみます。こんな感じで定数とか変数とかにヒントのような形で付ける事ができます。
これを型アノテーション(Type Annotation)といいます。普通にstringとかnumberも型アノテーションに使えます。
item2には型アノテーションはついていないですが、型推論がされるので結果的には型は決まります。

// nameのstringとかpriceのnumberも型アノテーション
const item1 : { name: string; price: number; } = {
    name: "りんご",
    price: 150
};
const item2  = {
    name: "りんご",
    price: 150
};

エイリアス(Type Aliases)

次にこいつに名前がついていないと使い回せなくて面倒なので、名前をつけてあげます。これが型エイリアス(Type Aliases)です。
つまり型ってなんなんじゃいって話になるわけですが、オブジェクト指向言語を使っているとclassやinterfaceと型の違いが曖昧になってきます。いったんWikipedia型システムとか読んでみると良さそうです。(長いけど)

type Item = {
    name: string;
    price: number;
};
const item3 : Item = {
    name: "りんご",
    price: 150
};

アサーション(Type Assertion)

「as」を使うと型を強制することができます。これを型アサーション(Type Assertion)といいます。

const item4 = {
    name: "りんご",
    price: 150
} as Item;

型系の用語がたくさんで混乱しそうです。

typeに利用できる型

要はエイリアスなのでどちらかというとどんな型が存在しているかって話で、見出しが適切か怪しいですがとりあえず…。

// プリミティブ型(こんな宣言をしてはいけませんよ)
type int = number;
// リテラル型(なんかこれもう定数な気がするけどいつ使うんだろう)
type Greet = "こんにちは";
// オブジェクト型
type Item = { name: string; price: number; };
// 配列型
type StringArray = string[];
// タプル型
type ItemTuple = [ string, number ];
// 関数型
type Action<T> = (obj: T) => void;

void型とかany型も型のような気はしますが、エイリアスつけたりはしなそうですよね。

ユニオン型(Union Type)

なんと型は合体ができます。orの合体がユニオン型です。stringまたはnumberの型ってどんなときに使うんだ…。
nullは関数の戻り値で値がなかったらnullを返すみたいなときに普通に使う気がします。

type StringOrNumber = string | number;
type DateOrNull = Date | null;

orなのでユニオンした全部にあるメンバーは参照できそう。

type Desk = { width: number; depth: number; };
type Curtain = { width: number; height: number; };
type DeskOrCurtain = Desk | Curtain;

const s = { width: 100, depth: 50 } as DeskOrCurtain;
s.width;    // 見える
s.depth;    // 見えない

リテラルも使えるので列挙型のような使い方をすることも多いですね。

type Season = "春" | "夏" | "秋" | "冬";

インターセクション型(Intersection Type)

で、もう一個合体があります。andの合体がインターセクション型です。
こちらはインタフェースを複数実装するときみたいに使えそうですね。

type DeskAndCurtain = Desk & Curtain; // 机とカーテンが合体したなにかすごそうで多分役に立たない謎の物体

const s = { width: 100, depth: 50, height: 200 } as DeskAndCurtain;
s.width;    // 見える
s.depth;    // 見える
s.height;    // 見える

プリミティブ型にも使えますが、実際にはそんな型は作れないのでnever型という特殊な型になるらしいです。
never型には何も入れられませんが、never型は何にでも入れられます。つまり…どういうこと?
なんか決して起こり得ない値に対する型とかに使うのが良いのかもしれません。

type StringAndNumber = string & number; // never型になる

型難しいぞ

改めてちゃんと型について考えると、普段割と適当に使ってるんだなということに気づきます。
プリミティブも確かに型だなと思ったり、リテラルを型として扱うのかと思ったり、合体してみたり。
でもこのあたりをちゃんと知っておくと表現の幅が広がりそうです。
次回はinterfaceとtypeの比較をします。