2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【TS】TypeScriptの個人メモ

Last updated at Posted at 2024-12-05

any型

どんな値でも受け入れる型。
any型に代入したオブジェクトのプロパティ、メソッドは使用できる。

const anyStr: any = "apple";

// 5
console.log(anyStr.length);

any型のオブジェクトはどんな型にも代入できる。
any型はTypeScriptが型チェックを放棄した型だからできてしまう

const anyStr: any = "apple";

const bool: boolean = anyStr;

// apple string
console.log(bool, typeof bool);

unknown型

どんな値でも受け入れる型。any型と違うのは、unknown型に代入したオブジェクトのプロパティ、メソッドはどちらも使用できないこと。

const unknownStr: unknown = "apple";

// エラー:'unknownStr''は 'unknown' 型です。
console.log(unknownStr.length);

unknown型のオブジェクトは代入できない。
unknown型は型安全なany型と言われる

const unknownStr: unknown = "apple";

// エラー:型 'unknown' を型 'boolean' に割り当てることはできません。
const bool: boolean = unkonwnStr;

never型

never型は「値を持たない」型。
never型のオブジェクトには何も代入できない。any型でも代入できない。
ただしnever型を別の型に代入することはできる。

// ✕:string型をnever型オブジェクトに代入できない
// エラー:Type 'string' is not assignable to type 'never'.
const nev: never = "apple";


// 〇:never型をstring型に代入することはできる。
const nev = "apple" as never;
const foo: string = nev;

// string型として使える
console.log(foo.toUpperCase());

never型として生まれたら最後までneverのまま。

never型の用途

異常な関数の戻り値の型として使われる。

  • 例外が必ず発生する関数の戻り値の型
  • 無限ループの関数の戻り値の型
  • Switch文などの網羅性のチェック
例外が必ず発生する関数
function throwError(): never {
  throw new Error();
}
無限ループの関数
function forever(): never {
  while (true) {} // 無限ループ
}

文字リテラルのユニオン型オブジェクトに対して、型で処理分岐する場合、分岐する型の指定が不足していたり、後から型を追加したときにエラーとして拾えるようにするために使う。

まずはnever型を使わず、網羅性が不足しているのソースコードを記載

網羅性のチェックが不足

type Fruit = "apple" | "orange" | "banana";

const fruit: Fruit = "banana";
checkFruits(fruit);

// Fruit型の文字リテラル型ごとに処理を分岐。
// "banana"型の分岐が無く、"その他"の処理にいってしまうがエラーにならないので、
// 気づけない。
function checkFruits(fruit: Fruit) {
  switch (fruit) {
    case "apple":
      console.log("リンゴ");
      break;
    case "orange":
      console.log("オレンジ");
      break;
    default: {
      console.log("その他");
      break;
  }
}

そこで"banana"型の分岐が抜けていた場合にtypescriptがエラーを検知してくれるようにするため、defaultでnever型を使う。

never型を使った網羅性
type Fruit = "apple" | "orange" | "banana";

const fruit: Fruit = "banana";
checkFruits(fruit);;

function checkFruits(fruit: Fruit) {
  switch (fruit) {
    case "apple":
      console.log("リンゴ");
      break;
    case "orange":
      console.log("オレンジ");
      break;
    default: {
      // "banana"型の分岐が無いのでエラーになる
      // エラー:型 'string' を型 'never' に割り当てることはできません
      const exhaustivenessCheck: never = fruit;
      break;
    }
  }
}
完璧な状態("banana"型の分岐も追加)
type Fruit = "apple" | "orange" | "banana";

const fruit: Fruit = "banana";
checkFruits(fruit);;

function checkFruits(fruit: Fruit) {
  switch (fruit) {
    case "apple":
      console.log("リンゴ");
      break;
    case "orange":
      console.log("オレンジ");
      break;
    case "banana":
      console.log("バナナ");
      break;
    default: {
      // この時の"fruit"変数が"never型"になっている。
      const exhaustivenessCheck: never = fruit;
      break;
    }
  }
}

型ガード

if文/switch文とtypeof/instanceof/in演算子を使って、対象のオブジェクトの型を確認すること。

const val = "apple";

// ↓ここが型ガード
if (typeof val === 'string') {

  // string型で確定なので安心してtoUpperCaseが使える
  console.log(val.toUpperCase();
} else {
  console.log('想定外の型');
}

型ガード関数

unknown型の変数に対して型ガードを行った後、そのunkonwn型の変数の型の変更まで行う。
ポイントは型ガード(型の絞り込み)を行う対象の引数がunkown型であること。

型ガードだけだと、対象の引数がunkown型の場合、後続の処理が実行できない。

通常の型ガードのみ
const val: unknown = "apple";

if (simpleCheck(val)) {
  // 型ガードはクリア!ただし、valはunknown型のままなので、
  // toUpperCase関数が使えない(エラー:'val''は 'unknown' 型です。)
  console.log(val.toUpperCase());
}

// 対象のunkonwn型オブジェクトの値の型stringかどうかだけをチェック
function simpleCheck(x: unknown): boolean {
  return typeof x === "string";
}
型ガード関数
const val: unknown = "apple";

if (typeGuardCheck(val)) {
  // 型ガードにクリアし、valもstring型に変換されているので、
  // toUpperCase関数が使える
  console.log(val.toUpperCase());
}

// 戻り値の型注釈に"x is string"を指定
function typeGuardCheck(x: unknown): x is string {
  return typeof x === "string";
}

型ガード関数の戻り値の型注釈であるx is stringという記述は型述語という。
引数xはstringであるとTypeScriptに解釈させている。

型のチェックはreturn typeof x === "string";でやっている、ということを忘れないように。

typescriptバージョン5.5以降では型述語の注釈無しでも型ガード関数と同じ扱いになる。

5.5以降で可能

const val: unknown = "apple";

if (check(val)) {
  // 型ガードにクリアし、valもstring型に変換されているので、
  // toUpperCase関数が使える
  console.log(val.toUpperCase());
}

// 戻り値の型注釈が無い場合、型ガード関数と同じ扱い。
function check(x: unknown) {
  return typeof x === "string";
}

型定義ファイル

拡張子が**.d.ts**で型情報がつまったファイル。

declare

JavaScriptファイルに変数・関数・クラスを記述しただけでは、TypeScriptはその存在を知らず、Not Foundになる。

index.js
function hello(name) {
  return "Hello, " + name;
}

// エラー:Cannot find name 'hello'.
hello("taro");

TypeScriptに存在知らせるために使うのがdeclare(アンビエント宣言)
declareは**.d.ts**ファイルに記述する

index.d.ts
declare function hello(name: string): string;

コンストラクトシグネチャ

コンストラクタの型を示している

declare const foo: new (arg: number) => unknown;

タプル型

配列の各要素の型や要素数を指定できる。

type tupleType = [string, number, boolean];
const tup: tupleType = ["apple", 10, true];

// エラー:ソースには 4 個の要素がありますが、ターゲットで使用できるのは 3 個のみです。
const tup2: tupleType = ["apple", 10, true, 'banana'];

ただしpushで要素の追加はできる

type tupleType = [string, number, boolean];
const tup: tupleType = ["apple", 10, true];

tup.push("banana");

// [ 'apple', 10, true, 'banana' ]
console.log(tup);

// 4
console.log(tup.length);

// 数字リテラルで追加した要素番号を指定するとエラーになる
// エラー:長さ '3' のタプル型 'tupleType' にインデックス '3' の要素がありません。
console.log(tup[3]);


// ただ、lengthを使うとエラーにならない
// banana
console.log(tup[tup.length - 1]);

Classを定義すると、Classは型になる

class Person {
  name: string;

  constructor(initName: string) {
    this.name = initName;
  }

  // "Person"をtypeとして使える
  greeting(this: Pick<Person, "name">) {
    console.log(this.name);
  }
}

Classの省略記述

class Person {

  // コンストラクタの引数にpublic演算子を入れると
  // publicなnameプロパティの作成と初期化までやってくれる
  constructor(public readonly name: string, private age: number) {}

  showAge() {
    console.log(this.age);
  }

  incrementAge() {
    this.age += 1;
  }

  greeting(this: Pick<Person, "name">) {
    console.log(this.name);
  }
}

const ken = new Person("KEN", 20);

型推論を残して型のチェックをする

type Person = { name: string };

// {name: string}型
const ken = { name: "KEN" } satisfies Person;

// 型注釈
// Pserson型
const bob: Person = { name: "BOB" };

オブジェクトのキーに過不足が無いかチェックできる。しかも型推論で使える。

satisfiesを利用
type Color = "red" | "green" | "blue";
const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: "#0000ff",
} satisfies Record<Color, unknown>;

// redをnumber[]として使える
palette.red.forEach((x) => console.log(x));
型注釈を利用
const oldpalette: Record<Color, string|number[]> = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: "#0000ff",
};

// エラー:redがnumber[]として利用できない
oldpalette.red.forEach((x) => console.log(x));
2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?