首页 > 解决方案 > 为什么常规函数会破坏类型推断?

问题描述

我有一个defineComponent()将对象作为参数的函数。该对象可以包含一个props属性和一个setup()接收参数的属性,该参数的类型是从对象中先前声明的属性props推断出来的。props

例子:

defineComponent({
  props: {
    size: {
      type: String,
      default: 'lg',
      validator: (value: string) => true,
    }
  },
  setup(props) {
    console.log(props.size.charAt(0))
  }
})

奇怪的是,如果props.size.validator声明为常规函数 ( validator: function(value: string) { return true }) 而不是箭头函数,则props参数的推断类型是完全错误的。对于上面的例子。它应该是:

{
  size: string
}

但它是这样的:

string[] | {
    [x: string]: Prop<unknown, unknown> | null;
}

常规函数本质上与工作代码中的箭头函数相同,所以我没想到它会破坏类型推断。

为什么常规函数会破坏setup()'props参数的类型推断?


mylib.d.ts:

declare interface PropOptions<T = any, D = T> {
    type?: PropType<T>;
    required?: boolean;
    default?: D | DefaultFactory<D>;
    validator?(value: unknown): boolean;
}
declare type DefaultFactory<T> = (props: Data) => T;
declare type Data = Record<string, unknown>;
export declare type PropType<T> = PropConstructor<T> | PropConstructor<T>[];
declare type PropConstructor<T = any> = { new (...args: any[]): T & {} };
export declare type Prop<T, D = T> = PropOptions<T, D> | PropType<T>;
export declare type ComponentPropsOptions<P = Data> = ComponentObjectPropsOptions<P> | string[];
export declare type ComponentObjectPropsOptions<P = Data> = { [K in keyof P]: Prop<P[K]> | null };

declare type InferPropType<T> =
  [T] extends [null] ? any 
: [T] extends [{ type: null | true }] ? any
: [T] extends [ObjectConstructor | { type: ObjectConstructor }] ? Record<string, any>
: [T] extends [BooleanConstructor | { type: BooleanConstructor }] ? boolean
: [T] extends [DateConstructor | { type: DateConstructor }] ? Date
: [T] extends [Prop<infer V, infer D>] ? (unknown extends V ? D : V)
: T;

export declare type ExtractPropTypes<O> =
  O extends object
  ? { [K in keyof O]: InferPropType<O[K]> }
  : { [K in string]: any };

export declare interface ComponentOptionsBase<Props> {
  setup?: (this: void, props: Props) => any;
}

export declare type ComponentOptionsWithObjectProps<PropsOptions = ComponentObjectPropsOptions, Props = ExtractPropTypes<PropsOptions>> 
  = ComponentOptionsBase<Props> & { props: PropsOptions };

export declare function defineComponent<PropsOptions extends ComponentPropsOptions>(options: ComponentOptionsWithObjectProps<PropsOptions>): any;

操场

标签: typescripttype-inference

解决方案


更新

type Constructors =
  | BooleanConstructor
  | ObjectConstructor
  | StringConstructor


type DefaultFactory<T> = (props: Data) => T;
type Data = Record<string, unknown>;

type Entity<T extends Constructors> = {
  type: T,
  default: InstanceType<T> | DefaultFactory<T>
  validator: (value: ConstructorMappings<T>) => boolean
}

type ConstructorMappings<T extends Constructors> =
  T extends StringConstructor
  ? string
  : T extends BooleanConstructor
  ? boolean
  : T extends ObjectConstructor
  ? object
  : never;

 type MakeSetup<Props extends Record<string, Entity<Constructors>>>={
     [Prop in keyof Props]:ConstructorMappings<Props[Prop]['type']>
 } 

interface Props<T extends Constructors> {
  props: {
    size: Entity<T>
  },
  setup: (arg: MakeSetup<this['props']>) => any
}

export declare function defineComponent<C extends Constructors>(options: Props<C>): any;

defineComponent({
  props: {
    size: {
      type: String,
      default: 'lg',
      validator: (value /** infered as a string */) => true,
    }
  },
  setup:(props)=> {
    console.log(props.size.charAt(0)) // ok
  }
})


如您所见,value现在可以推断出论证。


推荐阅读