模擬 vue3.0 rfcs `createComponent` api 中的`props`類型推導

rfc 中類型推導部分 Type Inferencehtml

預期想實現的效果

createComponent({
  props: {
    foo: {
      type: String,
      required: true
    },
    bar: {
      type: Number
    },
    boo: Boolean,
    options: (null as any) as { msg: string },
    requiredOptions: {
      type: (null as any) as { msg: string },
      required: true
    }
  } as const,
  setup(props) {
    props.foo; // string
    props.bar; // number | undefined
    props.boo; // boolean | undefined
    props.options; // {msg: string } | undefined
    props.requiredOptions; // {msg: string }
  }
});
複製代碼

String -> stringNumber -> numberBoolean -> boolean

在 ts 中vue

  • String對應的類型是StringConstructor
  • Number對應的類型是NumberConstructor
  • Boolean對應的類型是BooleanConstructor

可是,咱們想要實現的是轉換成小寫的string | number | booleangit

因此咱們寫個泛型來轉換github

type NormalizeType<T> = T extends StringConstructor
  ? string
  : T extends NumberConstructor
  ? number
  : T extends BooleanConstructor
  ? boolean
  : T;
複製代碼

playground 預覽連接typescript

定義 prop 的類型

type BuiltInType<T> =
  | StringConstructor
  | NumberConstructor
  | BooleanConstructor
  | T;
複製代碼

留個泛型是給複雜類型作兼容 rfc 複雜的 prop 類型api

定義createComponent函數接收的props類型

type DefaultType<T> = {
  [key: string]:
    | {
        type?: BuiltInType<T>;
        require?: boolean;
      }
    | BuiltInType<T>;
};
複製代碼

最關鍵的一步根據輸入的props類型計算出來setup函數接收的形參props類型

type ReflexType<T> = {
  [key in keyof T]: T[key] extends { type: infer TYPE; required: true }
    ? NormalizeType<TYPE>
    : T[key] extends { type: infer TYPE }
    ? NormalizeType<TYPE> | undefined
    : NormalizeType<T[key]> | undefined
};
複製代碼

組合出來createComponent函數定義

function createComponent<T extends DefaultType<any>>(props: {
  props: T;
  setup(props: ReflexType<T>): any;
}) {}
複製代碼

完整代碼

type BuiltInType<T> =
  | StringConstructor
  | NumberConstructor
  | BooleanConstructor
  | T;

type NormalizeType<T> = T extends StringConstructor
  ? string
  : T extends NumberConstructor
  ? number
  : T extends BooleanConstructor
  ? boolean
  : T;

type ReflexType<T> = {
  [key in keyof T]: T[key] extends { type: infer TYPE; required: true }
    ? NormalizeType<TYPE>
    : T[key] extends { type: infer TYPE }
    ? NormalizeType<TYPE> | undefined
    : NormalizeType<T[key]> | undefined
};

type DefaultType<T> = {
  [key: string]: { type?: BuiltInType<T>; require?: boolean } | BuiltInType<T>;
};

function createComponent<T extends DefaultType<any>>(props: {
  props: T;
  setup(props: ReflexType<T>): any;
}) {}

createComponent({
  props: {
    foo: {
      type: String,
      required: true
    },
    bar: {
      type: Number
    },
    boo: Boolean,
    options: (null as any) as { msg: string },
    requiredOptions: {
      type: (null as any) as { msg: string },
      required: true
    }
  } as const,
  setup(props) {
    props.foo;
    props.bar;
    props.boo;
    props.options;
    props.requiredOptions;
  }
});
複製代碼

playground 預覽連接函數

效果圖

相關文章
相關標籤/搜索