🐙

TypeScriptを雰囲気で使っていたので勉強し直したら知らなかったこと

2021/06/23に公開

TypeScript Version 4.3.4で動作確認

object型

let user = { name: "hoge" };

let user: { name: string } = { name: "hoge" };

は同義である。JSでいうところのobjectを定義すると{ name: string }という型が暗黙的に定義されたことになる。

リテラル型

値を指定する型のこと。決まった値しか使わせたくない場合に使用する。

let x: "hoge" | "piyo";
x = "hoge"
x = "piyo"
x = "fuga" // エラー

unknown型

型が不定の型。何でも代入できるが使用するときや、他の変数に代入するときに型ガードが必要。

let x: unknown = "hoge";

// x.toUpperCase(); これはコンパイルでエラー

if (typeof x == "string") {
  x.toUpperCase();
}

anyはコンパイルではエラーにならないが、実行時にエラーになる

let x: any = 1;

x.toUpperCase();

never型

関数やメソッドが正常に終了しないことを明示的に指定するための型

これだと正常に終了してしまうのでコンパイルでエラー

const hoge = (): never => {
};

必ず例外を投げる関数などを定義するときに使用する

const hoge = (): never => {
  throw Error();
};

スプレット演算子はobjectにも使える

配列

const x = [1,2,3];

console.log(...x); // 1 2 3

object

const x = { x: 1 };
const y = { y: 2 };

console.log({ ...x, ...y }); // {x: 1, y: 2}

objectの分割代入で変数名を指定できる

const user = { id: "hoge" };

const { id } = user;
const { id: no } = user;

console.log(id, no); // hoge hoge

クラスのコンストラクタは引数にアクセス修飾子をつけることでプロパティ が定義される

class User {
  id: number;
  
  constructor(id: number) {
    this.id = id;
  }
}

class User {
  constructor(public id: number) {}
}

は同義である

関数型のインターフェース

interface Hoge {
  (x: string): string;
}

const hoge: Hoge = (x: string): string => x;

type Hoge = (x: string) => string;

const hoge: Hoge = (x: string): string => x;

は同義である

交差型

class、interface、object型を交差すると結合した型になる

type Hoge = {
  x: string;
};

type Fuga = {
  y: string;
};

let hogeAndFuga: Hoge & Fuga = { x: "x", y: "y" };

union型を交差すると共通部分の型になる

type Hoge = number | boolean;
type Fuga = number | string;

let x: Hoge & Fuga = 1;
// x = "1"; エラー
// x = true; エラー

型ガード

typeof、instanceof、inのいずれかを使う

typeofはプリミティブ型で使う

const x: number | string = 1;
if (typeof x == "string") {
  // string
} else {
  // number
}

ユーザー定義クラスではinstanceofまたはinを使う

instanceof

class Hoge {};

class Fuga {};

const x: Hoge | Fuga = new Hoge();

if (x instanceof Hoge) {
  // Hoge
} else {
  // Fuga
}

in

class Hoge {
  myfunc() {}
}

class Fuga {}

const x: Hoge | Fuga = new Hoge();

if ("myfunc" in x) {
  // Hoge
} else {
  // Fuga
}

また、インタフェースにはinstanceofが使えない。回避策としてリテラル型のプロパティを定義して使用することができる

interface Hoge {
  kind: "hoge";
}

interface Fuga {
  kind: "fuga";
}

let x: Hoge | Fuga = { kind: "hoge" };

if (x.kind == "hoge") {
  // Hoge
} else {
  // Fuga
}

型キャストの書き方

let y = x as Hoge;

let y = <Hoge>x;

は同義である

インデックス型

型は決まっているがキー名が決まってないプロパティを定義することができる

class Hoge {
  [key: string]: number;
}

const hoge: Hoge = {
  one: 1,
  two: 2,
  three: 3,
};

メソッドや関数をオーバーロードできる

class Hoge {
  fuga(x: string): string;
  
  fuga(x: number): number;
  
  fuga(x: number | string) {
    return x;
  }
}

ジェネリックに制約をつける

extendsを使う

const check = <T extends number | string>(x: T, y: T): boolean => {
  return x == y;
}

keyofでオブジェクトのプロパティに存在するという制約をつけることができる

const hoge = <T extends object, U extends keyof T>(obj: T, k: U) => {
  return obj[k];
};

hoge({ id: 1 }, "id")

Utility Types

Partial以外にも色々ある
https://www.typescriptlang.org/docs/handbook/utility-types.html

Discussion