「型指定の方法」と「基本型の種類」

TypeScriptの「型指定の方法」と「基本型の種類」について解説します。型指定することで、どういった制約が掛けられるのか確認していきます。

目次

型指定の影響

JavaScriptTypeScript には以下の違いがあります。

  • JavaScript
    • 動的型付け
    • 弱い型付け(他の型の代入も許可する)
  • TypeScript
    • 静的型付け
    • 強い型付け(他の型の代入を許可しない)

具体的に確認します。以下のようなコードがあります。

let xxx = 'hello'
xxx = 33

console.log(xxx)

xxx には 文字列(string) を格納して、その後に 数値(number) を格納しています。

JavaScriptは弱い型付けのため、問題なく実行できます。そのため、意図しない型の値が格納されて不具合が生まれる可能性が高まります。

TypeScriptであれば、以下のようにコンパイルエラーとなります。

$ tsc test1.ts
test1.ts:2:1 - error TS2322: Type '33' is not assignable to type 'string'.

2 xxx = 33
  ~~~


Found 1 error.

TypeScriptは、強い型付けのため、xxxstring 以外の値を代入しようとしたらエラーとなります。厳格に型の管理をすることで、意図しない不具合を防ぐことができます。

型指定の方法

以下のように、関数の引数 関数の戻り値 変数: 型 の形式で明示的に型指定することができます。

const twice = (count: number): string => {
    return `2倍にすると ${count * 2} です。`
}

let myCount: number
myCount = 100

console.log(twice(myCount))

もし、型指定せずに宣言だけした場合 any型 (後述) になります。

基本型の種類

boolean

真偽値です。
true false

number

数値です。浮動小数も含みます。
100 12.3

string

文字列です。
'abcde'

any

any を指定すると、どの型でも格納できるようになります。

let xxx: any

xxx = 1
xxx = false
xxx = 'hello'

型指定が何も指定されていな場合もany型となります。多用するとTypeScriptを利用しているメリットが薄くなります。

unknown

any と同様で、型が不明なときに活用できます。

any を利用していた場合、実行時にエラーが発生していた箇所が、unknown を利用することで、コンパイル時にエラーを検知することができます。

anyを利用していたため、実行時でないと検知できなかったエラーが、unknownを利用することで事前に検知できるようになります。

コードを実行する前に、実装者が、型判定を行うなどの修正が必要であるという判断ができます。

const unknown1: unknown = 1234;

if (typeof unknown1 === 'string') {
  unknown1.charAt(1);
}

補足
TypeScript4.0から catch句 にて unknown を指定できるようになりました。

array

配列です。配列の要素もチェックします。

例えば、以下はエラーになります。

let xxx = [1, 2, 3]
xxx = ["aaa"]
$ tsc test1.ts
test1.ts:2:8 - error TS2322: Type 'string' is not assignable to type 'number'.

2 xxx = ["aaa"]
         ~~~~~


Found 1 error.

要素の型のチェックを緩めたいのであれば、以下のようにします。

let xxx: any[] = [1, 2, 3]
xxx = ["aaa"]

主な型指定方法です。

let array1: number[]
let array2: string[]
let array3: any[]
let array4: Array<number>
let array5: Array<string>
let array6: Array<any>

array in array

多次元配列の場合、以下のようになります。

let nestedArray: number[][] = [[11, 12, 13], [23, 24, 25]]

tuple

配列の各要素ごとに型指定できます。

以下ケースでエラーとなります。

  • 要素ごとに指定した型と異なる型である
  • 要素数が違う

enum

enumを利用すると、数値集合を名前付して管理しやすくできます。

enum Kanto {
    Gunma,          // 0
    Tochigi,        // 1
    Ibaraki = 20,   // 20
    Saitama,        // 20
    Tokyo,          // 22
    Chiba = 30,     // 30
    Kanagawa        // 31
}

let prefecture: Kanto = Kanto.Gunma

数値列挙の場合、値は0から自動で割り当てられます。

以下のように文字列を値にすることもできます。

enum Kanto {
  Gunma = 'Gunma',
  Tochigi = 'Tochigi',
  Ibaraki = 'Ibaraki',
  Saitama = 'Saitama',
  Tokyo = 'Tokyo',
  Chiba = 'Chiba',
  Kanagawa = 'Kanagawa',
}

void

voidは 関数の戻り値 で活用されます。
以下のように 戻り値がない ときにvoidを指定します。

const echo = (name: string): void => {
    console.log(`Hello, ${name}`)
}

never

以下のように処理が終了しないので、決して何も返さない場合には、neverを指定します。

let error = (message: string): never => {
    throw new Error(message)
}

object

objectのプロパティも型の制約を持ちます。
以下のコードを例に確認します。

let xxx = {
    aaa: 111,
    bbb: 'hello'
}

xxx = {
    aaa: true,
    bbb: 222
}

xxx = {
    yyy: 111,
    zzz: 'hello'
}

コンパイルすると以下のエラーが検出されました。

$ tsc test1.ts
test1.ts:7:5 - error TS2322: Type 'true' is not assignable to type 'number'.

7     aaa: true,
      ~~~

  test1.ts:2:5
    2     aaa: 111,
          ~~~~~~~~
    The expected type comes from property 'aaa' which is declared here on type '{ aaa: number; bbb: string; }'

test1.ts:8:5 - error TS2322: Type 'number' is not assignable to type 'string'.

8     bbb: 222
      ~~~

  test1.ts:3:5
    3     bbb: 'hello'
          ~~~~~~~~~~~~
    The expected type comes from property 'bbb' which is declared here on type '{ aaa: number; bbb: string; }'

test1.ts:12:5 - error TS2322: Type '{ yyy: number; zzz: string; }' is not assignable to type '{ aaa: number; bbb: string; }'.
  Object literal may only specify known properties, and 'yyy' does not exist in type '{ aaa: number; bbb: string; }'.

12     yyy: 111,
       ~~~~~~~~


Found 3 errors.
  • 値が型と一致しない
  • 存在しないプロパティが定義されている

というエラーです。

なお、明示的に型指定を記述するには以下のようにします。

let xxx: { aaa: number, bbb: string } = {
    aaa: 111,
    bbb: 'hello'
}

objectのkeyに変数利用

以下のように、変数をkeyにすることもできます。

const key = 'ccc';

const xxx = {
  aaa: 111,
  bbb: 'hello',
  [key]: 'world',
};

console.log(xxx['aaa']); // 111
console.log(xxx['bbb']); // hello
console.log(xxx['ccc']); // world
console.log(xxx[key]); // world

object in array

Arrayの中にObjectが存在する場合、以下のようになります。

let objectInArray1: Array<{ [field: string]: any }>

objectInArray1 = [
  {aaa: 1, bbb: 2},
  {aaa: 20, ccc: 2},
  {aaa: 22, ddd: "abc"}
]

Objectの中が想定できるのであれば、上記指定より、以下のように interface を定義した方が良いです。

interface Xxx {
  aaa: number;
  bbb?: number;
  ccc?: number;
  ddd?: string;
}
let objectInArray2: Array<Xxx>

objectInArray2 = [
  {aaa: 1, bbb: 2},
  {aaa: 20, ccc: 2},
  {aaa: 22, ddd: "abc"}
]

null

null のみ格納できます。

以下のコードは tsconfig.json にて、"strictNullChecks": true となっている場合にエラーとなります。

let xxx: string
xxx = null

もし strictNullChecks を有効なままで null も許容するのであれば、以下のように実装する必要があります。

let xxx: string | null
xxx = null

undefined

undefined のみ格納できます。

以下のように、関数の引数をオプションにした場合、引数xstring | undefined と推測されます。

const fn = (x?: string): string => {
  if (x === undefined) {
    return ''
  } 
  return x.toUpperCase()
}

fn('aaa')
fn()

複数型の指定

intersection ( T1 & T2 )
すべて満たす

T1 & T2 のように & で複数の型を指定します。
以下例において、XxxInterfaceAInterfaceB のプロパティを備えている必要があります。

interface InterfaceA {
  aaa: string;
  bbb: string;
}

interface InterfaceB {
  ccc: string;
}

type Xxx = InterfaceA & InterfaceB
const fnXxx = (): Xxx => {
  return {
    aaa: 'aaa',
    bbb: 'bbb',
    ccc: 'ccc',
  }
}

union ( T1 | T2 )
いずれかの型を満たす

T1 | T2 のように | で複数の型を指定します。

unionは、特定条件のときに必須のプロパティを用意したい時などに活用できます。

interface InterfaceA {
  hasContent: true;
  name: string;
  body: string;
}

interface InterfaceB {
  hasContent: false;
}

const xxx = (yyy: InterfaceA | InterfaceB) => {
  if (yyy.hasContent) {
    // yyy.hasContent === trueなので、yyyはInterfaceAの型に絞り込まれました。
    const zzz = `${yyy.name}_${yyy.body}`;
  }
};

参考

よかったらシェアしてね!
目次