技術とかの雑なToday I Learnedメモ

defineComponentのジェネリクス

composition-api の defineComponent のジェネリクス

Vue で props を指定するときって、

export default defineComponent({
  props: {
    strProps: {
      type: String,
      required: true,
    },
    arrProps: {
      type: Array as PropType<string[]>,
      required: false,
    },
  },
})

みたいに書くと思うんですけど、defineComponent のジェネリクスに props の型が書けるというのを最近知った。

ちょっと型を見てみる

手元では@nuxtjs/composition-apiを使っているんだけど、追ってったらこいつは@vue/composition-apiの型を import して一部独自のものを足して使っている感じだった。defineComponent は何も足されてなかったので@vue/composition-apiのほうを見ていく。

declare function defineComponent<
  RawBindings,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {}
>(
  options: ComponentOptionsWithoutProps<unknown, RawBindings, D, C, M>
): VueProxy<unknown, RawBindings, D, C, M>
declare function defineComponent<
  PropNames extends string,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
>(
  options: ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M>
): VueProxy<
  Readonly<
    {
      [key in PropNames]?: any
    }
  >,
  RawBindings,
  D,
  C,
  M
>
declare function defineComponent<
  Props,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
>(
  options: HasDefined<Props> extends true
    ? ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M, Props>
    : ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M>
): VueProxy<PropsOptions, RawBindings, D, C, M>

初見だとマジでなにもわからない。RawBindingsを grep してみたけど定義がこのファイルにはなかった。

Data, ComputedOptions, MethodOptions とあるのはたぶん Vue の data, computed, methods のことを書けるのだろう、雑に追ったがそんな感じだった。

今回は props の定義だけをジェネリクスで渡しているので 3 つあるうちの最下段にある型が使われているのだと思う、Props 以外はデフォルト型が設定されている。2 段目のPropNamesは string の拡張なので違いそう。

ジェネリクスで注入されたPropsは defineComponent の引数のoptions: HasDefined<Props>で使われてる。

HasDefined は以下のような感じで、Conditional Types でわちゃわちゃと書かれてるが、結局 boolean の値が帰りそう。

declare type Equal<Left, Right> = (<U>() => U extends Left ? 1 : 0) extends <
  U
>() => U extends Right ? 1 : 0
  ? true
  : false
declare type HasDefined<T> = Equal<T, unknown> extends true ? false : true

結局呼び出し元も Conditional Types で型が分かれてて、

HasDefined<Props> extends true ? ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M, Props> : ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M>

違いはComponentOptionsWithPropsに Props を渡しているかいないかだけっぽい。つまり props の定義があれば渡すしなければ渡さないっていうだけのことっぽい。

declare type ComponentOptionsWithProps<
  PropsOptions = ComponentPropsOptions,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Props = ExtractPropTypes<PropsOptions>
> = ComponentOptionsBase<Props, D, C, M> & {
  props?: PropsOptions
  emits?: (EmitsOptions | string[]) & ThisType<void>
  setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>

ここらへんから追ってないけど、たぶん Props の定義を渡せるようになっているということっぽい。

emits とか setup とかも渡せそう。

結論

あんま知られてないけど渡せそう。