我写configArgs字有困难,有人能帮我吗?

const path = {
  A: 'home',
  B: 'support',
  C: 'contact'
} as const
type B = unknown
const B: B = {};
const config = {
  [path.A]: () => null,
  [path.B]: (): B => B,

  [path.C]: ({
    active,
  }: {
    active: boolean
  }) => B,
} as const

type Config = typeof config

type ConfigArgs = {
  [P in keyof Config]: Config[P] extends (a: infer A) => any ? A : never // this is not working
}

function getConfig<T extends A>(
  path: A,
  args?: ConfigArgs[R]
): B | null {
  return config[path](args) 
}

每当我使用路径调用getConfig函数时,我都希望看到正确的参数类型.

getConfig("pathA") // no type errors
getConfig("pathC", {active:true}) // no type errors
getConfig("pathC", {id:"xxx"}) // has type errors, as parameters are wrong
getConfig("pathC") // has type errors, as function needs params.
getConfig("pathA", {active:true}) // has type errors, cause the function does not expect any params

推荐答案

如果您需要支持的只是getConfig()callers的强类型,则可以在path参数的类型K中将其设置为generic,并使用the Parameters<T>the ReturnType<T>实用程序类型来表示输入/输出关系:

declare function getConfig<K extends keyof Config>(
  path: K,
  ...args: Parameters<Config[K]>
): ReturnType<Config[K]>;

它的工作方式与预期一致:

getConfig(path.A) // okay
getConfig(path.B, { id: "xxx" }) // error, expected 1 arg but got 2
getConfig(path.C) // error // error, expected 2 args but got 1
getConfig(path.C, { active: true }) // okay
getConfig(path.C, { id: "xxx" }) // error, {id: string} is not {active: boolean}

但是,您会发现编译器将无法验证此函数的implementation是否正确:

function getConfig<K extends keyof Config>(
  path: K,
  ...args: Parameters<Config[K]>
): ReturnType<Config[K]> {
  return config[path](...args); // error! 
  // ---------------> ~~~~~~~ A spread argument must have a tuple type
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~ <-- unknown is not assignable to type...
}

这是目前对类型脚本的限制;像Parameters<Config[K]>ReturnType<Config[K]>这样的泛型conditional types对于编译器来说基本上是不透明的.您仍然可以使用这些类型,但您需要在实现中使用type assertions来 suppress 错误,并注意您已经正确地完成了工作,因为编译器在这里无法提供帮助:

function getConfig<K extends keyof Config>(
  path: K,
  ...args: Parameters<Config[K]>
) {
  return (config as any)[path](...args) as ReturnType<Config[K]>
  //             ^^^^^^ <-- assert ---> ^^^^^^^^^^^^^^^^^^^^^^^^
}

如果希望编译器遵循通用逻辑,则需要重构为对通用mapped types使用indexed accesses,如microsoft/TypeScript#47109中所述:

type ConfigArgs = { [K in keyof Config]: Parameters<Config[K]> }
type ConfigRet = { [K in keyof Config]: ReturnType<Config[K]> }

const _config: { [K in keyof Config]:
  (...args: ConfigArgs[K]) => ConfigRet[K]
} = config;

function getConfig<K extends keyof Config>(
  path: K,
  ...args: ConfigArgs[K]
) {
  return _config[path](...args)  // okay, seen as ConfigRet[K]
}

在这里,我们将ConfigArgsConfigRet都定义为Config上的映射类型,然后将_config变量定义为函数的映射类型,这些函数接受ConfigArgs[K]并 for each 键K返回ConfigRet[K].编译器允许我们将config赋给这个变量,因为它能够将映射的类型扩展为与config相同的特定类型.

现在可以看到,getConfig()函数正在对类型为(...args: ConfigArgs[K]) => ConfigRet[K]的值_config[path]执行操作,因此它接受类型为ConfigArgs[K]的REST参数并生成类型为ConfigRet[K]的输出.

因此,一切仍在调用方工作,并且实现也是类型判断的.

请注意,typeof configtypeof _config在概念上没有区别,但是编译器认为typeof _config以一种它看不到的方式表示输入和输出之间的一般关系.

Playground link to code

Typescript相关问答推荐

如何传递基于TypScript中可变类型的类型?

使用泛型keyof索引类型以在if-condition内类型推断

Typescript泛型类型:按其嵌套属性映射记录列表

为什么在这个例子中,我把缺少的属性添加到对象中时,TypeScrip没有拾取,如何修复?

使用Redux Saga操作通道对操作进行排序不起作用

有什么方法可以类型安全地包装import()函数吗?

如果数组使用Reaction-Hook-Form更改,则重新呈现表单

APP_INITIALIZER在继续到其他Provider 之前未解析promise

try 呈现应用程序获取-错误:动作必须是普通对象.使用自定义中间件进行MySQL操作

在React中定义props 的不同方式.FunctionalComponent

使用泛型识别对象值类型

打字错误TS2305:模块常量没有导出的成员

扩展对象的此内容(&Q;)

设置不允许相同类型的多个参数的线性规则

有没有办法在Reaction组件中动态导入打字脚本`type`?

TypeScript:如何使用泛型和子类型创建泛型默认函数?

使用NextJS中间件重定向,特定页面除外

使用本机 Promise 覆盖 WinJS Promise 作为 Chrome 扩展内容脚本?

在 TypeScript 中实现类型级别深度优先搜索

组件的第二个实例使用 svelte 中第一个实例上设置的函数