typescript 推断和限制记录值的类型

hwamh0ep  于 2022-12-19  发布在  TypeScript
关注(0)|答案(1)|浏览(168)

我正在尝试为函数参数创建一个类型,该类型基于type字符串和可选的multiple布尔值来限制其Record值:例如:

useQuery({
  a: {
    ref: ref(''), // ref is a string
    type: 'string',
  },
  b: {
    whitelist: [1, 2, 3], // whitelist is always optional and an array of the type
    ref: ref([1, 2, 3]), // ref is a number array
    type: 'number',
    multiple: true,
  },
});

因此,一个简单的方法是做类似下面的事情,但它不能完全工作,因为可选的多重布尔值没有正确地限制ref类型。

type DumbType = {
      whitelist?: string[];
      ref: Ref<string>;
      type: 'string';
    }
  | {
      whitelist?: string[];
      ref: Ref<string[]>;
      type: 'string';
      multiple: true;
    }
  | {
      whitelist?: number[];
      ref: Ref<number>;
      type: 'number';
    }
  | {
      whitelist?: number[];
      ref: Ref<number[]>;
      type: 'number';
      multiple: true;
    }
 | e.g.

useQuery({
 type: 'string',
 ref: ref(), // ref is inferred with string | string[] - without multiple: true
})

我试图使用Map类型来解决它,但是我被下面的问题卡住了。
QueryOptions本身似乎工作正常,但是UseQueryOptions无法推断Record值的泛型。

type Primitives = 'string' | 'number' | 'boolean';

export type UseQueryOptions<T> = {
  [K in keyof T]: QueryOptions<T[K]['type'], T[K]['multiple']>; // <= error
};

export type QueryOptions<T extends Primitives, M extends boolean | undefined> = {
  whitelist?: StringTypeToType<T, true>;
  ref: Ref<StringTypeToType<T, M>>;
  type: T;
  multiple?: M;
};

export type StringTypeToType<P extends Primitives, M extends boolean | undefined> = P extends 'string'
  ? ArrayWhenMultiple<M, string>
  : P extends 'number'
  ? ArrayWhenMultiple<M, number>
  : P extends 'boolean'
  ? ArrayWhenMultiple<M, boolean>
  : never;

export type ArrayWhenMultiple<M extends boolean | undefined, T = any> = M extends true ? T[] : T;

export type TypeToRefType<P extends Primitives, M extends boolean> = Ref<StringTypeToType<P, M>>;

export const useQuery = <P extends Primitives, m extends boolean | undefined>(options: QueryOptions<P, M>) => {};

TSPlayground

w46czmvw

w46czmvw1#

为了简单起见,联合方法应该是首选的。但是在你的定义中有一些歧义,这会导致问题。编译器不能正确地区分联合,因为multiple属性只出现在一些组成部分中。在结构化类型系统中,类似于

{
   ref: ref("abc"),
   type: 'string',
   multiple: true
}

不会破坏当前联合类型的约定,因为大多数情况下允许多余的属性。
我们应该通过处理每个组成部分的multiple属性来消除这种不明确性。这可以通过禁止使用multiple?: undefined设置该属性来实现,或者如果ref类型为Ref<string[]>Ref<number[]>,则键入 not setfalse来实现。我认为允许false值是有意义的。这就是它的样子:

type DumbType = {
      whitelist?: string[];
      ref: Ref<string>;
      type: 'string';
      multiple?: false
    }
  | {
      whitelist?: string[];
      ref: Ref<string[]>;
      type: 'string';
      multiple: true;
    }
  | {
      whitelist?: number[];
      ref: Ref<number>;
      type: 'number';
       multiple?: false
    }
  | {
      whitelist?: number[];
      ref: Ref<number[]>;
      type: 'number';
      multiple: true;
    }

Playground

相关问题