www.oreilly.com
Relationships Between Types
Subtypes and Supertypes
- 型A,Bがあり、AがBのsubtypeであるとき、Bが必要なところでAを安全に使える
- 型A,Bがあり、AがBのsupertypeであるとき、Aが必要なところでBを安全に使える
- 表記法の導入
A <: B
... AはBのsubtypeまたは同一の型
A >: B
... AはBのsupertypeまたは同一の型
Variance
- ジェネリクスが絡むとそう簡単な話ではなくなってくる
- 言語によりけり
- TSは...
- 複雑な型はだいたい共変
- 関数のパラメータだけは例外的に反変
type Base = {
x: number
}
type Derived = Base & {
y: string
}
type Bs = Base[]
type Ds = Derived[]
const b: Base = { x: 1 }
const d: Derived = { x: 2, y: 'z' }
const b2: Base = d
const bs: Bs = [b]
const ds: Ds = [d]
const bs2: Bs = ds
bs.push(b)
ds.push(b)
bs2.push(b)
type Klass = {
f: (n: number) => number
}
const foo: Klass = {
f: (n: number | null): number => {
return 1
}
}
TSC Flag: strictFunctionTypes
- 関数の引数はデフォルト双変
- tsconfigで
"strictFunctionTypes": true,
を設定すると反変になる
type Base = {
a: number
}
type Derived = Base & {
b: string
}
type AcceptBase = {
(b: Base): void
}
type AcceptDerived = {
(d: Derived): void
}
const acceptBase: AcceptBase = (b: Base) => { }
const acceptDerived: AcceptDerived = (d: Derived) => { }
const acceptBase2: AcceptBase = acceptDerived
const acceptDerived2: AcceptDerived = acceptBase
Assignability
- enum以外の場合、下記のいずれかを満たせばAはBに代入可能
- enumの場合、下記のいずれかを満たせばAはBに代入可能
- Aがenum Bのメンバである
- Bがnumber型のメンバを持ち、Aがnumberである
Type Widening
const a = 'x'
const b = 3
const c = true
const d = { x: 3 }
enum E { X, Y, Z }
const e = E.X
let a2 = 'x'
let b2 = 3
var c2 = true
let e2 = E.X
- nullやundefinedで初期化されるとanyに拡大される
- ただし、戻り値は狭い
function x() {
let a = null
a = 3
a = 'b'
return a
}
The const type
let a = { x: 3 }
const b = { x: 3 }
let c: { x: 3 }
let d = { x: 3 } as const
as const
はネストの中身もreadonlyにする
let xs = [1, 2, 3]
let ys = [1, 2, 3] as const
let zx = [1, { x: 2 }] as const
Excess porperty checking
type Options = {
url: string
method: 'GET' | 'POST'
data?: object
}
function call(options: Options) {
}
call({
url: 'http://localhost/users',
method: 'GET',
dataa: {}
})
Refinement
- flow-basedの型推論
if
, ?:
, ||
, switch
等で狭まるやつ
Discriminated union types
type UserTextEvent = { value: string, target: HTMLInputElement }
type UserMouseEvent = { value: [number, number], target: HTMLElement }
type UserEvent = UserTextEvent | UserMouseEvent
function handle(event: UserEvent) {
if (typeof event.value === 'string') {
event
event.value
event.target
return
}
event
event.value
event.target
}
- 意図した挙動にしたいときはtagged unionにしよう
type UserTextEvent = { type: 'UserTextEvent', value: string, target: HTMLInputElement }
type UserMouseEvent = { type: 'UserMouseEvent', value: [number, number], target: HTMLElement }
type UserEvent = UserTextEvent | UserMouseEvent
function handle(event: UserEvent) {
if (event.type === 'UserTextEvent') {
event
event.value
event.target
return
}
event
event.value
event.target
}
英語