TypeScript泛型当从函数参数推导类型时使用“infere”时出现问题

bvjveswy  于 2023-08-08  发布在  TypeScript
关注(0)|答案(1)|浏览(368)

在学习TypeScript的过程中,我试图创建一个函数,该函数将从输入参数中推导出返回值,作为一个字典,其中的值定义为具有适当类型的函数对象。
最初这是从弗里达类型库。
我试图扩展一个功能来定义函数的API。
我所面临的问题,我可以在一个小片段中说明,当将类型直接传递给另一个泛型类型时,inferred关键字似乎可以工作,**但是当类型从函数参数推导出来时,一切都失败了。
我有下面的代码片段用最少的代码来说明这个问题

declare class Pointer {}

type NativeTypesMap = {
  int: number;
  long: number;
  void: void;
  pointer: Pointer;
};

type ValuesOf<T> = T[keyof T];
type KeysOf<T> = keyof T;

type NativeTypesValue = ValuesOf<NativeTypesMap>;
type NativeTypesType = KeysOf<NativeTypesMap>;

type GetValue<Map, Type, T extends Type> = T extends keyof Map ? Map[T] : never;

type GetArgValue<T extends NativeTypesType> = GetValue<
  NativeTypesMap,
  NativeTypesType,
  T
>;

type Def<T extends NativeTypesType> = { r: T };

type DefAny = Def<any>;

declare type BaseApi = {
  [k: string]: DefAny;
};

type ApiCalls<T extends BaseApi> = {
  [p in keyof T]: T[p] extends Def<infer R>
    ? (...args: any) => GetArgValue<R>
    : never;
};

function createapi<T extends BaseApi>(params: T): ApiCalls<T> {
  // implementation omitted
  return {} as ApiCalls<T>;
}

type z = ApiCalls<{
  open: { r: "int" };
  malloc: { r: "pointer" };
  free: { r: "void" };
}>;
/*
 * z is calculated to be
 * type z = {
 *     open: (...args: any) => number;
 *     malloc: (...args: any) => Pointer;
 *     free: (...args: any) => void;
 * }
 */

var a = createapi({
  open: { r: "int" },
  malloc: { r: "pointer" },
  free: { r: "void" },
});
type c = typeof a;
/*
 * calculated type of a is:
 * tyoepf a = {
 *     open: never;
 *     malloc: never;
 *     free: never;
 * }
 */

字符串
TS Playground
我的目标是改变createapi,使a的dedcued类型与z相同。
有人知道我错过了什么吗?

wqsoz72f

wqsoz72f1#

这个问题是因为你传递给函数的对象的类型:

// const obj: {
//   open: {
//       r: string;
//   };
//   malloc: {
//       r: string;
//   };
//   free: {
//       r: string;
//   };
// }
const obj = {
  open: { r: 'int' },
  malloc: { r: 'pointer' },
  free: { r: 'void' },
}

字符串
编译器将类型扩展为基本类型,以便在处理变量时使我们的工作更轻松。由于obj的属性中不再有文字值,因此infer条件不再起作用。
有两种方法可以解决此问题:
const assertion -防止typescript扩大类型:

// const obj: {
//   readonly open: {
//       readonly r: "int";
//   };
//   readonly malloc: {
//       readonly r: "pointer";
//   };
//   readonly free: {
//       readonly r: "void";
//   };
// }
const obj = {
  open: { r: 'int' },
  malloc: { r: 'pointer' },
  free: { r: 'void' },
} as const


测试:

// {
//   readonly open: (...args: any) => number;
//   readonly malloc: (...args: any) => Pointer;
//   readonly free: (...args: any) => void;
// }
var a = createapi({
  open: { r: 'int' },
  malloc: { r: 'pointer' },
  free: { r: 'void' },
} as const);


看起来不错,但是,在每个函数调用中添加as const会变得非常烦人和不方便。在Typescript 5.0中,添加了const类型参数,它们的作用与 const assertion 对函数参数的作用相同:

function createapi<const T extends BaseApi>(params: T): ApiCalls<T> {
  // implementation omitted
  return {} as ApiCalls<T>;
}

// {
//   readonly open: (...args: any) => number;
//   readonly malloc: (...args: any) => Pointer;
//   readonly free: (...args: any) => void;
// }
var a = createapi({
  open: { r: 'int' },
  malloc: { r: 'pointer' },
  free: { r: 'void' },
});

相关问题