前提
以下のような定義があるとする。
type User = {
name: string
age: number
}
const user: User = {
name: 'example',
age: 20
}
typeof
型コンテキストでの使用
変数の型を取得することができる。
type User2 = typeof user // User2 は User型となる
console.log(typeof user) // object
console.log(typeof user.name) // string
console.log(typeof user === 'object') // true
console.log(typeof user.name === 'string') // true
keyof
オブジェクトのプロパティ名を抽出し、文字列リテラルのユニオン型(String Literal Union) を取得することができる。
let keys: keyof User
keys = 'age'
keys = 'name'
keys = 'XXX' // 型 '"XXX"' を型 'keyof User' に割り当てることはできません。
// 例) GraphQL スキーマから TypeScript の型を生成するビルドツールを使っている場合
type APIResponse = {
user: {
id: string
friendList: {
count: number
friends: {
firstName: string
lastName: string
}[]
}
}
}
type ResponseKeys = keyof APIResponse // 'user'
type UserKey = keyof APIResponse['user'] // 'id' | 'friendList'
ルックアップ型 × keyof
型安全なゲッター関数を実装することができる。
function get<
O extends object,
K extends keyof O
>(
o: O,
k: K
): O[K] {
return o[k]
}
const res: APIResponse = {
user: {
id: 'sample',
friendList: {
count: 1,
friends: [
{
firstName: '',
lastName: ''
}
]
}
}
}
const user = get(res, 'user')
console.log(user) // { id: 'sample', friendList: { count: 1, friends: [ [Object] ] } }
ネストの深い階層を取得するために、複数の引数を取れるようにしてみる。
type Get = {
<
O extends object,
K1 extends keyof O
>(o: O, k1: K1): O[K1]
<
O extends object,
K1 extends keyof O,
K2 extends keyof O[K1]
>(o: O, k1: K1, k2: K2): O[K1][K2]
<
O extends object,
K1 extends keyof O,
K2 extends keyof O[K1],
K3 extends keyof O[K1][K2]
>(o: O, k1: K1, k2: K2, k3: K3): O[K1][K2][K3]
}
const get: Get = (object: any, ...keys: string[]) => {
let result = object
keys.forEach((k) => {
result = result[k]
})
return result
}
const abc = get(res, 'user', 'friendList', 'count')
console.log(abc) // 1
in
オブジェクトのプロパティ存在有無を判定する
console.log('name' in user); // true
console.log('xxx' in user); // false
for で全プロパティを処理する
for (const key in user) {
console.log(key) // name\n age
}
Mapped types
type Weekday = 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri'
type Day = Weekday | 'Sat' | 'Sun'
const nextDay: { [K in Day]: Day } = {
Mon: 'Tue',
Thu: 'Wed',
Wed: 'Thu',
Tue: 'Fri',
Fri: 'Sat',
Sat: 'Sun',
Sun: 'Mon'
}
keyof × in
mapped type は TypeScript 特有の言語機能で、それらは、リテラル型と同様に JavaScript を静的に型付けする試みにとって有用なユーティリティ機能である。これはとても強力である。なぜならオブジェクトのキーと値に型を与えるだけでなくルックアップ型とそれらを組み合わせると、どの値の型がどのキーの名前に対応するかを制約できるからである。
type Account = {
id: number
isEmployee: boolean
notes: string[]
}
// 全てのフィールドを省略可能にする
type OptionalAccount = {
[K in keyof Account]?: Account[K]
}
// 全てのフォールドをnull可能にする
type NullableAccount = {
[K in keyof Account]: Account[K] | null
}