【TypeScript】初歩的だけど忘れがちな構文
TypeScriptの基本的な所は知っているけど、「あれの呼び方何って言うんだっけ?」とか「interfaceとtypeって何が違うんだっけ?」となりがちなので、その辺りをまとめてみました。
1. 基本
覚えておきたいTSの基本的な構文や型
インデックスシグネチャ
[key: T]: U
という構文をインデックスシグネチャを呼びます。インデックスシグネチャのキーの型(T)は、numberかstringのどちらかでなければいけません。
interface NumberList {
[key: number]: string
}
const a: NumberList = { 1: 'one', 2: 'tow' }
a[3] = 'three'
console.log(a) // { "1": "one", "2": "tow", "3": "three"}
タプル型
タプル型は配列の派生型です。固定長の配列を型付けするための型です。
const a: [number] = [1]
const b: [number, string] = [1, 'foo']
const c: [number, string] = [1, 'foo', 'bar'] // Error
タプル型を可変長にしたい場合は、スプレッド構文を使います。
const a: [number, ...string[]] = [1, 'a', 'b']
列挙型
キーが固定されているオブジェクトのようなものです。列挙型はenumとも言います。しかし、列挙型の利用は、非推奨と言われています。その理由は「3. Tips」で後述します。
const enum Color {
Red = '赤',
Bule = '青',
Green = '緑'
}
console.log(Color.Green) // 緑
合併型、共用型、共用体型、union型(|)
AとBという2つがある場合、A, B, ABを指します。
type A = string | number
交差型(&)
AとBという2つがある場合、ABを指します。合併型、交差型ともにベン図を使うと理解しやすいです。
type A = string & number
オプションパラメーター(?)
パラメーターを省略可能にします。
type A = {
a?: number
}
const a: A = {}
console.log(a) // {}
非nullアサーション演算子(!)
非nullアサーション演算子は、その値が非null且つ、非undefinedであるとTSに伝えます。
interface User { name : string }
const user = { name: 'yamada' }
const getUser = (user?: User) => {
console.log(user!.name) // "yamada"
}
getUser(user)
非nullアサーション演算子は、強制的にnullまたはundefinedでないことに出来てしまうため危険です。そのため、if文で存在をチェックしてあげると良いです。
nterface User { name : string }
const user = { name: 'yamada' }
const getUser = (user?: User) => {
if (user && user.name) {
console.log(user.name) // "yamada"
}
}
getUser(user)
オプショナルチェイニング
オブジェクトのプロパティで、nullやundefinedのチェックに「?」を使うことで、プロパティがnullかundefinedの場合は、次のプロパティにはアクセスせず、undefinedを返します。
対応前
type Foo = {
bar?: {
baz?: string
}
}
const foo: Foo = {
bar: {
baz: "hoge"
}
}
const getHoge = (foo: Foo) => {
if (foo.bar && foo.bar.baz) {
console.log(foo.bar.baz)
}
}
getHoge(foo)
対応後
const getHoge = (foo: Foo) => {
if (foo.bar?.baz) {
console.log(foo.bar.baz)
}
}
2. 比較
概念が似たもの同士の比較
null, nudefined, viod, neverの違い
(1)null ... 値が欠如していることを意味します。
(2)nudefined ... あるものがまだ定義されていないことを意味します。
(3)viod ... 明示的に何も返さない関数の型です。
const a = () => {
const b = 1 + 1
}
(4)never ... 決して戻ることのない関数の型です。例外とスローしたり、whileで永久的に処理を実行するとnever型になります。
const a = () => {
throw new Error('Error')
}
const b = () => {
while(true) {
doSomething()
}
}
object型とObject型の違い
もしオブジェクト型を使う場合は、Object型はなるべく避けてください。Object型は、string型やnumber型を許容します。
const a: Object = 'a'
const b: object = 'a' // Type 'string' is not assignable to type 'object'.(2322)
const a: Object = 1
const b: object = 1 // Type 'number' is not assignable to type 'object'.(2322)
interfaceとtypeの違い
interfaceとtypeの挙動は似ていますが、用途はinterfaceはクラスやオブジェクトの規格を定義し、typeは型や型の組み合わせに別名を付ける時に使います。
違いは以下のようなものがあります。ちなみにtypeは、型エイリアスと呼ばれます。
(1)型エイリアスは型演算子(&や|)を使うことが出来ますが、インターフェースは使えません。
type a = number | string
const a = 1
(2)同じ名前のインターフェースが存在する場合、宣言のマージが行われます。これは型エイリアスではエラーになります。
interface User {
name: string
}
interface User {
age: number
}
const User = {
name: 'tanaka', age: 20
}
type User = { // Duplicate identifier 'User'.(2300)
name: string
}
type User = { // Duplicate identifier 'User'.(2300)
age: number
}
const User = {
name: 'tanaka', age: 20
}
interfaceとtypeの違いは、こちらにより詳しく載っています。
3. Tips
覚えておきたい、ちょっとしたコツなど
number型で大きな値を扱う
number型で大きな数値を扱う時は、数字の区切りを使って読みやすくすることが出来ます。
const a: number = 3_000_000
console.log(a) // 3000000
anyよりもunknownを使う
unknownはanyと似ていますが、anyはどのようなデータもチェックなしで入れることができます。unknownの場合、変数を利用するにはtypeofを使って、チェックを行わないとエラーになります。そのため、本当に型が分からない値がある場合には、anyではなく代わりにunknownを使ってください。
let a: unknown = 30
let b = a + 10 // Object is of type 'unknown'.(2571)
let c = 0
if (typeof a === 'number') {
c = a + 10
}
console.log(c) // 40
列挙型(Enum)の利用は非推奨
TSではEnumの利用は推奨されていません。例えば、Enumを使った次のコードのように関数の引数に数値を渡した場合でも、コンパイルエラーにならず、型安全ではなくなります。
const enum Fruit { Apple, Banana, Cherry }
const getFruit = (fruitName: Fruit) => {
switch(fruitName) {
case Fruit.Apple: return 'りんご'
case Fruit.Banana: return 'ばなな'
case Fruit.Cherry: return 'さくらんぼ'
default: return ''
}
}
console.log(getFruit(6)) // コンパイルエラーが発生しない
以下の記事に詳しく載っていましたので、ここは割愛します。
4. 便利な機能(一部)
TSの組み込みの型関数などを載せます。
種類 | 説明 |
---|---|
Record<Keys, Type> |
KeysがプロパティとなりTypeを持つレコード型を構築します |
Partial<Type> |
値を省略可にします |
Requied<Type> |
値を必須(省略不可)にします |
Readonly<Type> |
読み取り専用にします |
constアサーション |
宣言と同時に型を読み取り専用にします |
Pick<Type, Keys> |
指定されたKeysの型を構築します |
Omit<Type, Keys> |
指定されたKeysを削除して型を構築します |
Exclude<Type, Union> |
Typeに含まれるがUnionに含まれていない型を構築します |
NonNullable<Type> |
nullとundefinedを除外したTypeの型を構築します |
詳細
Record<Keys, Type>
... KeysがプロパティとなりTypeを持つレコード型を構築します。
interface Fruit {
color: string,
price: number
}
type FruitName = "Apple" | "Banana"
const fruit: Record<FruitName, Fruit> = {
Apple: { color: 'red', price: 100 },
Banana: { color: 'yellow', price: 80 }
}
console.log(fruit.Apple) // { "color": "red", "price": 100 }
Partial<Type>
... 値を省略可にします。Partialはundefinedを許容するもので、nullはエラーになります。
interface User {
name: string
}
const user: Partial<User> = {
name: undefined
}
Requied<Type>
... 値を必須(省略不可)にします。
interface User {
name: string
}
const user: Required<User> = {
name: undefined // Type 'undefined' is not assignable to type 'string'.(2322)
}
Readonly<Type>
... 読み取り専用にします。
interface User {
name: string
}
const user: Readonly<User> = {
name: 'tanaka'
}
user.name = 'yamada' // Cannot assign to 'name' because it is a read-only property.(2540)
constアサーション
... 宣言と同時に型を読み取り専用にします。
const user = { name: 'tanaka' } as const
user.name = 'yamada' // Cannot assign to 'name' because it is a read-only property.(2540)
const user = ['tanaka'] as const
user.push('yamada') // Property 'push' does not exist on type 'readonly ["tanaka"]'.(2339)
Pick<Type, Keys>
... 指定されたKeysの型を構築します。
interface Fruit {
color: string,
price: number
}
type FruitColor = Pick<Fruit, 'color'>
const fruit: FruitColor = {
color: 'red'
}
console.log(fruit.color) // "red"
Omit<Type, Keys>
... 指定されたKeysを削除して型を構築します。
interface Fruit {
color: string,
price: number
}
type FruitColor = Omit<Fruit, 'price'>
const fruit: FruitColor = {
color: 'red'
}
console.log(fruit.color) // "red"
Exclude<Type, Union>
... Typeに含まれるがUnionに含まれていない型を構築します。エクスクルードと読みます。
type A = number | string
type B = number
type C = Exclude<A, B>
const a: C = 'a'
console.log(a) // "a"
NonNullable<Type>
... nullとundefinedを除外したTypeの型を構築します。そのため、宣言後にnullやundefinedを代入しようとするとエラーになります。
type A = string | null
let a: NonNullable<A> = 'a'
a = null // Type 'null' is not assignable to type 'string'.(2322)
他にも見たい場合は、こちらに載っていますのでご確認ください。
以上です。
参考
Discussion