関連

stringは TypeScript で文字列だということを表現する型です。以下は変数の宣言時に一緒に使っている例ですがstringを使うと、その変数は変なことをしなければstringが入ってるということにできます。

const str: string = 'text';
// Type '123' is not assignable to type 'string'.
const numStr: string = 123;

TypeScript は賢いので型を指定せず入れてもstringとしてくれます。

let str = 'foo';
// ok
str = 'bar'

また String Literal Types と言われる、文字列だけどある特定の文字列を型として扱うことができます。この場合はある特定の文字列しか入れることができません。
これは型定義時にある特定の文字列stringの代わりに使うだけです。

const foo: 'foo' = 'foo';
// Type '"foo"' is not assignable to type '"bar"'.
const bar: 'bar' = 'foo';
// typescript@^3.4.0 から以下でも同じ意味
const foo = 'foo' as const;

Union と一緒に使うことで「Aという文字列」か「Bという文字列」というように扱える値を増やすことができます。以下の'foo' | 'bar'は、'foo''bar'だけ扱える型という意味になります。

// ok
let foo: 'foo' | 'bar' = 'foo';
// ok
foo = 'bar';
// Type '"baz"' is not assignable to type '"foo" | "bar"'.
foo = 'baz';

この String Literal Types はstringを継承している型ということなので、stringで宣言されている部分でも代入したり渡したりできます。

const fooOrBar: 'foo' | 'bar' = 'foo';
let value: string = fooOrBar;

文字列に絞り込む

ある Union 型などで「この値はstringか?」を調べるにはtypeof value === 'string'が使えます。

const a: string | number = 'foo';

if (typeof a === 'string') {
  a; // a: string
} 
関連

number型は、 TypeScript で数値だと表現する為の型です。他の言語のように小数点のものは別名だったりはしません。 TypeScript ではすべての数値がnumberになります。

numberを指定すると、代入を数値だけに絞れます。

const num: number = 123;
// Type '"123"' is not assignable to type 'number'.
const strNum: number = '123';

TypeScript は賢いので以下のように指定せずに代入しても、そこから推論してnumber型としてくれます。

const num = 123;
// ok
num = 456

数値に絞り込む

ある Union 型などで「この値はnumberか?」を調べるにはtypeof value === 'number'が使えます。

const a: string | number = 123;

if (typeof a === 'number') {
  a; // a: number
} 
関連

booleanは、 TypeScript で真偽値だと表現する為の型です。

以下のようにbooleanを指定すると、代入を真偽値だけに絞れます。

const bool: boolean = true;
// Type '"true"' is not assignable to type 'boolean'.
const boolStr: boolean = 'true';

TypeScript は賢いので以下のように型を省略しても推論してbooleanとして扱ってくれます。

let bool = true;
// ok
bool = false
関連

オブジェクトな値なら何でも入れることができる型です。
型にはobjectを使います。

const obj: object = {};

構造化したオブジェクトを Interface で定義する

ある程度構造化したobjectが使いたいなら、interfaceで型を作ります。以下はfoobarというプロパティを持つValue型の例です。

interface Value {
  foo: number;
  bar: number;
}

const value: Value = {
  foo: 0,
  bar: 1
};

ちなみに、以下はbazは持っていないプロパティなのでエラーになります。結構厳密です。

const value: Value = {
  foo: 0,
  bar: 1,
  baz: 2,
};

bazが静的であればValue型にbazを追加することで直せます。また動的な場合はインデックスシグネチャを型に追加してあげます。

interface Value {
  [key:string]: number;
  foo: number;
  bar: number;
}

Interface で構造化したもののプロパティを変数からアクセスできるようにする

例えば'foo'はどこからからやってきた変数propに入っている値だとして、value[prop]で取れそうですがこれはエラーになります。これにはインデックス署名をその Interface に追加する必要があります。

value[prop]で値を取れるようにしたものが以下にあります。[index: string]: number | string部分がその問題の部分です。これはstringが来た時にfoobarの値どちらかが取得できるということを表しています。それはfoobarプロパティがある為で、値の型は取得できるうるすべてをUnion化したものにする必要があります。。

interface Value {
  [index: string]: number | string;
  foo: number;
  bar: string;
}

また[index: string]部分は[index: number]とも定義できます。これはvalue[number]のようなアクセスの為に必要です。

Interface のプロパティを Partial 化する

1部だけ

Partial Propertyとは、あってもなくてもどちらでも構わないというようなプロパティのことです。これはプロパティ名の後:の代わりに?:とするだけです。

試しにbarを Partial 化するとこうなります。無くてもエラーになりません。

interface Value {
  foo: number;
  bar?: number;
}

const value: Value = {
  foo: 'foo'
}

すべてのプロパティを Partial 化

すべてを Partial 化したいなら全てに一々?を付ける必要はありません。Partialという TypeScript 自身が提供している Generics 化を使うことですべてを Partial 化できます。

interface Value {
  foo: number;
  bar: number;
}

type PartialValue = Partial;

このpartialValueは、次のように書くのと同じ意味になります。

interface PartialValue {
  foo?: number;
  bar?: number;
}

Record 型で一気に定義する

すべてのプロパティが同じ型の時便利なのがRecord<P extends string, V>型です。Pは、UnionstringVはそれぞれプロパティとなる値を設定します。つまり、P = 'foo' | 'bar'V = numberの場合、それは{foo: number, bar: number}となります。
つまり、次の2つはまったく同じ型になります。

type Value = Record<'foo' | 'bar', number>;
interface Value {
  foo: number;
  bar: number;
}

Interface の extends を使ってある構造を継承した新たな構造を作る

以下はValueのプロパティにbarを増やしたものになります。extendsで継承しているのでfoobarの記述はいりません。

interface ValueWithBaz extends Value {
  baz: number;
}
// 同じ
interface ValueWithBaz {
  foo: number;
  bar: number;
  baz: number;
}

Interface の単純分割

同じ名前でインターフェースを定義すると勝手にtype A = Foo & {children: unknown[]}のようにインターセプトしたタイプのように設定することができます。

interface A {
  foo: string
}

interface A {
  children: unknown[];
}

const a: A = {
  foo: 'foo',
  children: []
};

厳密なオブジェクトにする

例えば以下の時、

const a = {
  foo: 'foo',
  bar: 123,
  baz: true
};

これはこのような型が自動で付きます。

{
  foo: string;
  bar: number;
  baz: boolean
}

これは無いよりはマシですが、定数や型の特定に使えるような値がほしい時これではタイプガード系の関数を定義したり、キャストが必要になってしまいます。

そんな場合はオブジェクト宣言の後ろにas constと付けてあげます。(定数であればObject.freezeでJavaScriptから見た時でも凍結されているようにするとより良いと思います)

const a = {
  foo: 'foo',
  bar: 123,
  baz: true
} as const;

すると型は、

{
  readonly foo: 'foo';
  readonly bar: 123;
  readonly baz: true
}

とすることができます。これにより上書き防止(無いよりマシ)や、ifで型の絞り込みができるようになります。

const action: {type: 'foo'; foo: unknown} | {type: 'bar'; bar: unknown} = {};

if (action.type === 'foo') {
  // action = {type: 'foo'; foo: unknown}
}
関連

配列の型の定義はArray<T>型を使う方法と型の後ろにT[]を付ける方法の2通りあります。Array<T>T[]の違いはありません。

const foo: Array = [
  'foo',
  'bar',
  'baz',
];

const foo2: string[] = foo;

タプル

もし個数や順番、または型がバラバラな型の集まりが決まっているのであれば[A, B, C, D, E][]の間に型を羅列した型を定義します。

const foo: [string, number, boolean] = [
  'foo',
  123,
  true,
];

もしタプルを定数化したいなら、指定する型も厳密にしてあげるだけです。

const a: readonly ['foo', 123, true] = [
  'foo',
  123,
  true,
];

ですがこれであれば後ろに値宣言後にas constを付けることで同じ意味にできます。(できればObject.freezeで凍結化もしておくと良いと思います)

const a = [
  'foo',
  123,
  true,
] as const;
// a: readonly ["foo", 123, true]

例えば引数に上で定義した配列の中のどれかを渡してほしいような関数を定義したいなら、

typeof a[number];
// 'foo' | 123 | true

でユニオン化することができるので、あとは型として使うだけです。

配列の絞り込み

Union 型などを配列に絞り込みたい場合は、Array.isArrayメソッドを使います。

const foo: string | string[] = [];

if (Array.isArray(foo)) {
  foo; // foo: string[]
}
関連

TypeScript 独自のシンタックスです。オブジェクト型からその型が持つプロパティ名からなる文字列リテラル型の Union が取得できます。

interface Value {
  foo: string;
  bar: string;
  baz: string;
}

type ValueKey = keyof Value;
// type ValueKey = "foo" | "bar" | "baz"
関連

何も当てはまらない・無いを表現する型です。

void 型を関数の戻り値で使う

関数の戻り値に使うときは、returnも使わないような本当に何も返さない時に使います。基本省略することがほとんどかなと思います。

const fn: (text: string) => void = text => {
  console.log(text);
}

void 型を関数の引数に使う

引数に使うとその引数は無いものとすることができます。 Pertial (?)を付けなくても渡さなくていい引数扱いになります。

const fn = (arg: void) => {/* ... */};
fn(); // ok

const fn2 = (arg: undefined) => {/* ... */};
fn2(); // Expected 1 arguments, but got 0.
fn2(undefined);

関数の引数に使うのはどういう時か

恐らくこれは自分でそういう関数型をわざわざ定義することは99%無いです。
これが便利なのは恐らく、自作の Generic なタイプから引数型に変換するような時に Pertial 化(?)を付けなくてもしなくても引数の数を(型チェック的には)制御できることだと思います。

interface Arguments {
  foo: string;
  bar: void;
}

const foo = (val: Arguments['foo']) => /* ... */;
const bar = (val: Arguments['bar']) => /* ... */;

foo('foo'); // ok
foo(); // Expected 1 arguments, but got 0.

bar(); // ok
bar('bar'); // Argument of type '"bar"' is not assignable
            // to parameter of type 'void'.
関連

anyと似ていますがタイプセーフな型です。型を特定するまであらゆる操作が制限されるみたいな型です。unknownは存在しうるすべての型の Union のようなものかなと思います。(anyも)

const foo: unknown = 'foo';
// これは以下のようなもの(`IDO5`などは適当)
const foo: IDO5 | n0lM | 'sowe' | ... | string | number | boolean | undefined | null = 'foo'

上記では'foo'を入れていますが、これは Union にstringもしくは'foo'型が含まれる(イメージ)ので代入することができます。

ですがunknownの特徴として、unknownのままではプロパティやプロトタイプメソッドなどすべてのアクセスが制限されてしまいます。

const a: unknown = 'a';
a.foo
// Object is of type 'unknown'.

型を特定する

そのまま使えるのならそのままでいいかもしれないですが、unknownで何かしようと思ったら使う前にまず型変換してあげる必要があります。

例えばstring | undefinedという型valueの時、if (value !== undefined) {...}とすることで...ではvaluestring型だということに絞り込むことができます。

「存在しうるすべての型の Union のようなもの」と言ったようにunknown肯定的if文などで特定の型にキャストすることができます。また他の同じようにtypeofinstanceof,inなどを使った絞り込みも効いてくれます。

例えば以下のようにunknown型をstringに絞り込めます。

const a: unknown = 'a';
// const a: unknown
if (typeof a === 'string') {
  // const a: string
}

タイプガードの場合。これはFooBarに絞り込みます。

interface Foo {
  foo: string;
}
interface Bar {
  bar: string;
}

const isFoo = (value: unknown): value is Foo => {
  return typeof value === 'object' && typeof (value as Foo).foo === 'string';
}

const isBar = (value: unknown): value is Bar => {
  return typeof value === 'object' && typeof (value as Bar).bar === 'string';
}


const a: unknown = {};
if (isFoo(a)) {
  // const a: Foo
} else if (isBar(a)) {
  // const a: Bar
}

使い所

  1. スコープの中で最初からあるわけではないデータ

  2. API をinterfaceで定義していく時に後回しにしたいプロパティの型

  3. 関数のオーバーロード定義時ただ引数の数を合わせたいだけの時の型

  4. 型の種類が多い(そして数が不明かつ API ドキュメントの無いカオスプロジェクト)

みたいな所とかは一旦unknownで置いておいて、使う時になってから実際の型に置き換えたり、キャストしたりするといいかなと思います。

例えばhttp.createServerbody-parserなどの組み合わせで API を実装する時にbodyParser.json()(req, res, next)が終わるまではreq.bodyへのアクセスはさせたくありません。
そのような時に、最初{body: unknown}としておいてbodyParserのミドルウェア部分が終わった後{body: 実際の方}と設定してあげたりするといいかなと考えます。ミドルウェア部分が終わる前のreq.body

あと最近だと強制型変換などで一旦unknownに置いてから目当ての型に再度変換しないと駄目な ESLint ルールとかありしますね。

const a: Foo = {...};
(a as unknown) as Bar
関連

unknown型にとても良く似ています。違うのはあらゆるプロパティにアクセスできる点です。この型を使うと JavaScript のコードを書いてるように型を気にせず書くことができます。

anyは以下のように、そんなプロパティは無いので普通なら型エラーになる所ですが、型エラーにならずに記述することができます。もちろんこれは、実行すると「 Uncaught TypeError: Cannot read property 'b' of undefined (undefinedからbプロパティへアクセスはできせん)」エラーになります。

const num: any = 1;
num.a.b.c
// Uncaught TypeError: Cannot read property 'b' of undefined

使い所はディレクターに実装を急かされてるときです。

関連

neverは、 TypeScript で「あるはずない」を表現するための型です。

正常に return されない可能性のある関数で使う

これは例えば、

  1. エラーでthrowされる

  2. process.exitで途中で処理を途中で中断する

などの時neverが当てはまります。

const fn: () => never = () => {
  throw new Error('...');
  // process.exit(1)
}

ただDefinitelyTyped/DefinitelyTypedで型ファイルを追加しようとした時に、never型を使っていたら「neverいらないから削除して」と言われたのでvoidと同じで基本省略する形で統一すればいいかもしれません。

Conditional Types でありえない状態の戻り型に指定

Conditional Types で型を変換する時にstringnumberしか期待しないのにFooという良くわからないものが来てしまった時に、想定外なのでneverに設定するということが良くあると思います。

type StringOrNumber =
  T extends string
    ? string
    : T extends number
    ? number
    : never

JavaScript で飯食べたい歴約 8 年、 純( nju33 ) によるノートサイトです。

このサイトではドリンク代や奨学金返済の為、広告などを貼らせて頂いてますがご了承ください。

Change Log