Totality
- switch-caseの漏れとかを教えてくれるやつ
type Weekday = 'Mon' | 'Tue' | 'Wed'|'Thu'|'Fri'
type Day = Weekday | 'Sat' | 'Sun'
// Error: Function lacks ending return statement and return type does not include 'undefined'.
function getNextDay(w: Weekday): Day {
switch(w) {
case 'Mon': return 'Tue'
}
}
TSC Flag: noImplicitReturns
- tsconfigで
"noImplicitReturns": true
しておくと、下記のような潜在的な不具合を検出できる
// Error: Not all code paths return a value.
function isBig(n: number) {
if (n >= 100) {
return true
}
}
Advanced Object Types
Type Operators for Object Types
type APIResponse = {
user: {
userId: string,
friendList: {
count: number,
friends: {
firstName: string,
lastName: string
}[]
}
}
}
function getAPIResponse(): Promise<APIResponse> {
// ...
}
function renderFriendList(friendList: any) { // どうする?
// ...
}
const response = await getAPIResponse()
renderFriendList(response.user.friendList)
-
friendList
のtypehintをどうする?- とりあえず
any
でスタブにしている
- とりあえず
FriendList
を定義する?
type FriendList = {
count: number,
friends: {
firstName: string,
lastName: string
}[]
}
type APIResponse = {
user: {
userId: string,
friendList: FriendList
}
}
function getAPIResponse(): Promise<APIResponse> {
// ...
}
function renderFriendList(friendList: FriendList) {
// ...
}
const response = await getAPIResponse()
renderFriendList(response.user.friendList)
- いちいち名前をつけたくないこともある
type APIResponse = {
user: {
userId: string,
friendList: {
count: number,
friends: {
firstName: string,
lastName: string
}[]
}
}
}
function getAPIResponse(): Promise<APIResponse> {
// ...
}
function renderFriendList(friendList: APIResponse['user']['friendList']) {
// ...
}
const response = await getAPIResponse()
renderFriendList(response.user.friendList)
The keyof operator
type APIResponse = {
user: {
userId: string,
friendList: {
count: number,
friends: {
firstName: string,
lastName: string
}[]
}
}
}
type ResponseKeys = keyof APIResponse // 'user'
type UserKeys = keyof APIResponse['user'] // 'userId'|'friendList'
type FriendListKeys = keyof APIResponse['user']['friendList'] // 'count'|'friends'
- 用法
type Vector3D<T = number> = {
x: T,
y: T,
z: T
}
function get<T>(vector3d: Vector3D<T>, key: keyof Vector3D<T>): T {
return vector3d[key]
}
const v: Vector3D = {
x: 1,
y: 2,
z: 3,
}
const x = get(v, 'x')
const y = get(v, 'y')
const z = get(v, 'z')
const w = get(v, 'w') // Error: Argument of type '"w"' is not assignable to parameter of type '"x" | "y" | "z"'.
TSC Flag: keyofStringsOnly
keyof
はデフォルトでnumber|string|symbol
を返す- 配列の例
type KeysOfArray<T> = keyof T[] // number|'length'|'toString'|...
type NumericKeysOfArray<T> = number & keyof T[] // number
- stringだけにしたい場合、tsconfigで
"keyofStringsOnly": true
する
type KeysOfArray<T> = keyof T[] // 'length'|'toString'|...
type NumericKeysOfArray<T> = number & keyof T[] // never
The Record Type
- nextDayをswitch-caseじゃなくてルックアップテーブルで書き換えたいぞ、になったとき
- totalityの担保のために使える組み込みの型
type Weekday = 'Mon' | 'Tue' | 'Wed'|'Thu'|'Fri'
type Day = Weekday | 'Sat' | 'Sun'
// Error: Type '{ Mon: "Tue"; }' is missing the following properties from type 'Record<Weekday, Day>': Tue, Wed, Thu, Fri
let nextDay: Record<Weekday, Day> = {
Mon: 'Tue'
}
- 網羅するとコンパイルエラーが消える
let nextDay: Record<Weekday, Day> = {
Mon: 'Tue',
Tue: 'Wed',
Wed: 'Thu',
Thu: 'Fri',
Fri: 'Sat'
}
Mapped Types
- Record Typesよりももっと強力なやつ
type Weekday = 'Mon' | 'Tue' | 'Wed'|'Thu'|'Fri'
type Day = Weekday | 'Sat' | 'Sun'
// 3: typescript: Property 'Fri' is missing in type '{ Mon: "Tue"; Tue: "Wed"; Wed: "Thu"; Thu: "Fri"; }' but required in type '{ Mon: Day; Tue: Day; Wed: Day; Thu: Day; Fri: Day; }'.
let nextDay: {[Key in Weekday]:Day} = {
Mon: 'Tue',
Tue: 'Wed',
Wed: 'Thu',
Thu: 'Fri',
}
- 既存の型のoptionalやreadonlyを付け外ししたりできる
type Account = {
id: number,
isEmployee: boolean,
notes: string[]
}
type OptionalAccount = {
[K in keyof Account]?: Account[K]
}
type NullableAccount = {
[K in keyof Account]: Account[K] | null
}
type ReadonlyAccount = {
readonly [K in keyof Account]: Account[K]
}
// equivalent to Account
type Account2 = {
[K in keyof OptionalAccount]-?: OptionalAccount[K]
}
// equivalent to Account
type Account3 = {
-readonly [K in keyof ReadonlyAccount]: ReadonlyAccount[K]
}
Built-in mapped types
type Account = {
id: number,
isEmployee: boolean,
notes: string[]
}
type OptionalAccount = Partial<Account>
type ReadonlyAccount = Readonly<Account>
// equivalent to Account
type Account2 = Required<OptionalAccount>
type AccountId = Pick<Account, 'id'>
// { id: number; }
Companion Object Pattern
type Currency = {
unit: 'EUR' | 'GBP' | 'JPY' | 'USD'
value: number
}
const Currency = {
from(value: number, unit: Currency['unit'] = 'USD'): Currency {
return { unit, value }
}
}
- 型と値の名前空間は異なるので、同名にできる
- まとめてimportできるのが嬉しい
Currency.ts
export type Currency = {
unit: 'EUR' | 'GBP' | 'JPY' | 'USD'
value: number
}
export const Currency = {
from(value: number, unit: Currency['unit'] = 'USD'): Currency {
return { unit, value }
}
}
index.ts
import { Currency } from './Currency'
const amountDue: Currency = {
unit: 'JPY',
value: 83733.10
}
const otherAmouhtDur = Currency.from(330, 'EUR')