⌨️
TypeScriptで強制的に余剰プロパティチェックする型を作る
TL; DR
これを読むと解決します。
暇な人向け
TypeScriptの余剰プロパティチェックの話は結構有名だと思います。わざわざ繰り返す必要はないかと思うので,めっちゃよくまとまってるこちらの記事を参考にしてください。
今回解決したい問題を簡単にコードをまとめると,
interface T = {
one?: string
two?: string
}
const t = (arg: T): void => {
console.log({arg})
}
t({one: 'hello', three: 'hello'}) // エラーになる
const a = {one: 'hello', three: 'hello'}
t(a) // エラーが出ない
Playground:
というコードについてを考えます。要は,例の最後の t(a)
でエラーを出す, 言い換えると (この場合だとthreeという) 余分なプロパティを持っている場合にエラーにしたい,というのがゴールです。
できそうだけど考えるの面倒だなと思いぐぐったら先人の肩に乗ることができました。感謝。
このstackoverflowを参考に,このような型を導入します。
type StrictPropertyCheck<T, TExpected, TError> = T & (Exclude<keyof T, keyof TExpected> extends never ? {} : TError);
これだけで記事は終わりなんですが,せっかくなのですこしだけ解説すると,
- TypeScriptの組み込み型
Exclude<T, U>
はTがUにassignableだとTを,そうでなければUを返します。 -
keyof T
はT
のkey
の名前のUnion Typeを返します。
よって, T
の型に TExpected
の余剰プロパティがあると, keyof TExpected
に keyof T
が代入できないため, TError
になるという仕組みです。
ではこれを導入するとどのように解決するのか見てみましょう。
interface T {
one?: string
two?: string
}
type StrictPropertyCheck<T, TExpected, TError> = T & (Exclude<keyof T, keyof TExpected> extends never ? {} : TError );
const t = <S extends T>(arg: StrictPropertyCheck<S, T, `S has excess property`>): void => {
console.log(arg)
}
t({one: 'hello', three: 'hello'}) // エラー
const a = {one: 'hello', three: 'hello'}
t(a) // エラー
Playground:
確認すると,きちんとどちらでもエラーになってますね。
おわり
Discussion
これだともう少しシンプル?
never
にしましたT & {}
とさせる代わりに Generics だけでT
を表現できるようにしましたいいですね!ありがとうございますー!