⚠️
TypeScriptのOmit<T,Keys>を使うと、TとKeysに共通していないプロパティが消える
背景
例えば以下のような型を定義する。
この時 ABCx
ではA
B
C
のすべてからy
を取り除いた値を取得したかったがそうはならなかった。
A
B
C
すべてに共通する { x: number; y: number }
から y
を削除したものが残った。
type A = { a: string; x: number; y: number }
type B = { b: string; x: number; y: number }
type C = { c: string; x: number; y: number }
type ABC = A | B | C
// 期待したもの: { a: string; x: number } | { b: string; x: number } | { c: string; x: number }
// 作成されたもの: { x: number }
type ABCx = Omit<ABC, 'y'>
原因
結論: Omit
は Exclude
を使っているから。
詳しく
Omitの定義は以下のようになっている。
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
今回、T
は ABC
、K extends keyof T
が y
。
Exclude<keyof T, K>
の keyof ABC
では、 ABC
で共通するプロパティである "x" | "y"
が返される。
この時点でa
b
c
は消えるのでPickもされない。
回避方法
頑張ってOmit(ボツ)
以下のように各型ごとにOmitしたものをユニオン型にするという書き方にした。
(最初これ書いたけどユニオン分配の書き方がイケてるのでボツ)
type OmittedA = Omit<A, 'y'>
type OmittedB = Omit<B, 'y'>
type OmittedC = Omit<C, 'y'>
type OmittedABCx = OmittedA | OmittedB | OmittedC // = Omit<A, "y"> | Omit<B, "y"> | Omit<C, "y">
const testA: OmittedABCx = { a: '', x: 0 }
const testB: OmittedABCx = { b: '', x: 0 }
const testC: OmittedABCx = { c: '', x: 0 }
ユニオン分配(Union Distribution)を使う
@adanamiさんにコメントで教えていただきました🙇🏻♂️
こっちのほうがイケてる👍
type OmitY<T> = T extends { y: unknown } ? Omit<T, 'y'> : never
type NewABCx = OmitY<ABC> // = Omit<A, "y"> | Omit<B, "y"> | Omit<C, "y">
Discussion
コメント失礼します
Extract
やExclude
でも使われているUnion distributionという機能を使うことでも回避できるのでご参考までにありがとうございます!
なるほど〜〜〜。記事更新させていただきました🙇🏻♂️