打字稿:如何制作接受对象的类型,其键匹配通用但所有值都是值参数的映射函数

Posted

技术标签:

【中文标题】打字稿:如何制作接受对象的类型,其键匹配通用但所有值都是值参数的映射函数【英文标题】:Typescript: How to make type that accepts object which keys match generic but all values are map functions of value argument 【发布时间】:2020-05-22 12:15:44 【问题描述】:

我想实现这样的类型:“我的所有键都递归地与类型 T 相同,但我所有的值都是接受此类键值并映射它的函数。

我试过下面的代码,但它没有推断出指定函数的返回类型并且递归是错误的

export type ObjectButAllKeyIsValueFn<T> = 
    [K in keyof T]?: ObjectButAllKeyIsValueFn<T[K]> | (<R>(value: T[K]) => R)



interface User 
    name: string,
    surname: string,
    address: 
        city: string,
        country: string,
        street: string
    


const z: ObjectButAllKeyIsValueFn<User> = 
    name: value => `Hello $value`,
    address: 
        city: city => 1
    
;

更新: 我想实现这样但递归的东西,所以我必须正确地将 StupidPattern 映射到 Pattern

type StupidPattern<T> = 
    [K in keyof T]: StupidPattern<T[K]> | ((value: T[K]) => unknown)


type Pattern<T> = 
    [K in keyof T]: (value: T[K]) => unknown


type Result<T, P = Pattern<T>> = 
    [K in keyof P]: K extends keyof T
        ? P[K] extends (value: T[K]) => infer R
            ? R
            : never
        : never


declare function map <T> (pattern: Pattern<T>): (target: T) => Result<T, Pattern<T>>
declare function map <T> (pattern: Pattern<T>, target: T): Result<T, Pattern<T>>;

【问题讨论】:

((value: T[K]) =&gt; T[K]) 如果我理解正确,返回值需要与参数相同 @Marv 不,它不是,您可以将值映射到任何类型,但我想在参数中保留该类型,并且在结果对象上具有正确的类型 【参考方案1】:

(&lt;R&gt;(value: T[K]) =&gt; R) 类型不是你要找的;本质上通用的R 是quantified 错误的方式。它的意思是“一个函数,其输入类型为T[K],其输出是调用者指定的任何类型R。这实际上不可能安全地实现。大概你的意思是实现者应该能够指定R,但是在TypeScript中没有干净的方式来用泛型来表示它......你可能会将R作为ObjectButAllKeyIsValueFn的另一个泛型参数,但是你需要一个R T 的每个键。让我们备份并以不同的方式看待这个:

我们如何将ObjectButAllKeyIsValueFun 定义为这种类型:

export type ObjectButAllKeyIsValueFn<T> = 
  [K in keyof T]?: ObjectButAllKeyIsValueFn<T[K]> | ((value: T[K]) => unknown)

这种类型捕捉到了函数值属性可以返回任何类型的想法,只要你关心。但是,当然,简单地将一个值注释为ObjectButallKeyIsValueFn&lt;User&gt; 类型会丢弃一堆关于该值类型的信息;你不会知道每个函数返回什么类型,或者即使任何特定的属性是一个函数。

所以让我们放弃注释变量作为那种类型。相反,当我们创建值时,我们确保它们可分配到该类型而不扩大它们。这将为我们提供您想要的所有 IntelliSense。这是一个辅助函数及其对User的应用:

const asObjType = <T>() => <U extends ObjectButAllKeyIsValueFn<T>>(u: U) => u;

const asObjUser = asObjType<User>();

这就是你如何称呼asObjUser()

const z = asObjUser(
  name: value => `Hello $value`,
  address: 
    city: city => 1
  
);

/* const z: 
    name: (value: string) => string;
    address: 
        city: (city: string) => number;
    ;
 */

console.log(z.name("Fred")); // Hello Fred

这有效并跟踪z 的特定类型。它还可以捕获如下错误:

const oops = asObjUser(
  name: 123, // error!
  // number is not assignable to string | ((value: string) => unknown) | undefined
);

所以,希望这足以让您取得进步。祝你好运!

Playground link to code

【讨论】:

我无法理解您如何使用这种咖喱函数并正确获取类型。实际上,我的代码在 js 中正常运行,但无法在 typescript 中对其进行注释和重写。我想要实现的目标是一个允许通过其结构声明映射来映射值的函数。所以在我的我有一些模式对象,其键与我要映射的目标值完全相同,但值是接受目标“对象”值并映射它们的函数。另外问题是我在这里使用递归映射并且很难编写递归类型:P 我不太理解这条评论,也没有在您编辑的代码中看到问题。您能否确定是 minimal reproducible example 清楚地表明了您遇到的问题?祝你好运!

以上是关于打字稿:如何制作接受对象的类型,其键匹配通用但所有值都是值参数的映射函数的主要内容,如果未能解决你的问题,请参考以下文章

打字稿,更改嵌套在界面中的所有匹配键的类型

打字稿中具有通用键的对象

打字稿中的通用对象类型

接受通用参数和字符串数组的打字稿函数

打字稿不推断通用对象的值

打字稿,合并对象类型?