Skip to content

Instantly share code, notes, and snippets.

@wonderful-panda
Last active February 10, 2019 11:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wonderful-panda/eb37035c86a11645feab0be60cd1ea6d to your computer and use it in GitHub Desktop.
Save wonderful-panda/eb37035c86a11645feab0be60cd1ea6d to your computer and use it in GitHub Desktop.
Vue + JSXの環境でLibraryManagedAttributesを使って型付けする方法を考えてみるテスト

仮に、Vue.extendに渡されたオプションの型を出来上がったコンポーネントから参照可能になるとする。 これはそれを疑似的に可能にするためのヘルパ

/*
 * const Component = createComponentExperimental({ ... }); でコンポーネントを定義すれば
 * typeof Component["_options"] でオプションの型を取得できる
 */
import Vue, { VueConstructor } from "vue"; 
import { ThisTypedComponentOptionsWithRecordProps } from "vue/types/options";

export type ExperimentalComponent<Options> = VueConstructor<Vue> & {
  _options: Options;
};

export function createComponentExperimental<
  Data,
  Methods,
  Computed,
  Props,
  Options extends ThisTypedComponentOptionsWithRecordProps<Vue, Data, Methods, Computed, Props>
>(
  options: ThisTypedComponentOptionsWithRecordProps<Vue, Data, Methods, Computed, Props> &
    Options
): ExperimentalComponent<Options> {
  return Vue.extend(options) as any;
}

これはRecordPropsDefinitionのrequired指定をよみとって 「コンポーネントの外部から見たPropsの型」を導出するための型

import { RecordPropsDefinition } from "vue/types/options";

type RequiredPropNames<P> = {
  [K in keyof P]: P[K] extends { required: true } ? K : never
}[keyof P] &
  keyof P;
type OptionalPropNames<P> = Exclude<keyof P, RequiredPropNames<P>>;

// P はPropDefから導出可能なので型引数にしなくてもいいけど、どうせ呼び出し側で既知なので
// 渡す
export type OuterProps<P, PropDef extends RecordPropsDefinition<P>> = {
  [K in RequiredPropNames<PropDef> & keyof P]: P[K]
} &
  { [K in OptionalPropNames<PropDef> & keyof P]?: P[K] };

上記を踏まえたLibraryManagedAttributesの定義。 実際には ["foo", "bar"] の形式でpropsを指定された場合とか、functional componentの場合とか いろいろ考えることが他にもあるので、あくまでサンプルとして。

  namespace JSX {
    // TComponentに対象のコンポーネントの型が渡ってくるので、_options -> props でProps Definitionの型を取得して
    // JSX向けのAttributeを構成する
    type LibraryManagedAttributes<TComponent, TProps> = KnownAttrs & (
      TComponent extends { _options: { props: infer PropDef } }
      ? (PropDef extends RecordPropsDefinition<infer P>
          ? OuterProps<P, PropDef>
          : never)
      : never;
  }) /*
    & (TComponent extends { _scopedSlots: infer S } ? ... : {})
    みたいに、 _scopedSlots というメンバがあればScopedSlots関連のアトリビュートも生やすとか、
    _events というメンバがあればイベントハンドラも生やすとかも可能
  */;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment