我已经看到了一些相关的问题和GitHub的问题,但我仍然会询问具体的场景,以防不完全相同.

如果我们将对象文字obj定义为

const obj = {
  num1: 1,
  num2: 2,
} as const;

当前可以创建和键入一个函数,该函数将对象值映射到另一种类型的值,而忽略来自条目/条目的对象周围的问题.等.

const mapObj = <T extends Record<string, unknown>, R>(
  obj: T,
  mapFn: (v: T[keyof T]) => R
): { [K in keyof T]: R } => ({} as any);

调用该函数会产生非常好的结果.

const mapped = mapObj(obj, (v) => `${v}`);
const mappedConst = mapObj(obj, (v) => `${v}` as const);

退货

{
  readonly num1: string;
  readonly num2: string;
}

{
    readonly num1: "1" | "2";
    readonly num2: "1" | "2";
}

分别取决于传递的函数.

I'd like to know if there is some approach that can allow the exact value types to be returned. key num1 should be "1"num2 "2".

const mapObjExact = <T extends Record<string, unknown>, F extends <K extends keyof T>(k:K, v: T[K]) => unknown>(
  obj: T,
  mapFn: F
): { [K in keyof T]: ReturnType<F> } => ({} as any);

我不认为有任何方法可以为ReturnType提供额外的信息,关于它将被用哪种类型的参数调用,所以我认为这种方法不起作用.但也许有一种不同的方法来构建函数.

I thought this may something to do with higher kinded types etc ... On the other h和 something like this for single value works.

const apply=<const T extends unknown,const R>(v:T,fn:(v:T)=>R):R=>fn(v)

const v1 = apply(2,(n)=>`${n}`) //"2"

但这是直接基于一个参数的,因此TypeScrip更容易猜测以确定类型.

我想这可能是不可能的,但我想知道是否有任何较新的类型脚本功能,如const泛型断言,或其他方法,是否有可能使其工作.

推荐答案

为了使这可能起作用,TypeScrip将需要查看函数const f = v => `${v}`并将其推断为具有针对不同输入类型的不同输出类型.这意味着它需要被视为generic函数(类似<T extends number>(v: T) => `${T}`的类型)或overloaded function(类似{ (v: 1): "1"; (v: 2): "2"; }的类型).它需要纯粹在类型级别上这样做,而不是像f(1)f(2)这样的直接calling.

但不幸的是,TypeScrip大多缺乏在类型级别直接操作泛型或重载类型的能力.为了完成这份Typescript ,需要higher kinded types份,可能是microsoft/TypeScript#1213份所要求的那种.具体地说,我会说您需要True call types,正如microsoft/TypeScript#40179中所要求的那样. 调用类型会回答这样的问题:"对于类型F的函数f和类型A的参数数组a,f(...a)的类型是什么?"也许我们可以称它为Call<F, A>甚至F(...A),但它目前还不存在于语言中.

// NOT VALID TYPESCRIPT: DO NOT TRY THISL
const f = <T extends number>(v: T) => `${v}` as const;
type FReturn<T extends number> = Call<F, [T]>;
// type FReturn<T extends number> = `${T}` ? ??

现在,您所能做的就是获取一个函数类型F,并通过conditional type inference请求它的返回类型,比如ReturnType<F>.这必然通过擦除任何泛型类型参数并且通过仅使用最后的调用签名来将输入函数类型F转变为非泛型非重载函数类型.因此,您将获得一个完全不依赖于输入参数列表的返回类型.这对您的目的来说是不够的:

const f = <T extends number>(v: T) => `${v}` as const;
type FReturn<T extends number> = ReturnType<typeof f>; 
// type FReturn<T extends number> = `${number}` ?

因此,这是没有希望的,至少不能通过类型推断来实现.


我能想象到的最接近的做法是让您手动指定mapObject的输入类型T和输出类型U,然后让编译器compute指定适当的重载函数类型(通过将重载表示为intersection个函数类型,我们可以通过类似于Transform union type to intersection type的工作原理的逆变技巧获得):

type KeyMapper<T extends object, U extends Record<keyof T, any>> =
    { [K in keyof T]: (x: (x: T[K]) => U[K]) => void } extends
    Record<string, (x: infer I) => void> ? I : never;

type KeyMapped = KeyMapper<{ num1: 1, num2: 2 }, { num1: "1", num2: "2" }>;
// type KeyMapped = ((x: 1) => "1") & ((x: 2) => "2")

那么mapObject()可以写成这样:

function mapObject<T extends object, U extends Record<keyof T, any>>(
    t: T, map: KeyMapper<T, U>): U {
    return Object.fromEntries(Object.entries(t).map(
        ([k, v]) => [k, (map as any)(v)])) as any
}

您可以调用它,尽管编译器无法从函数体中推断出重载类型.相反,我们被迫手动指定类型参数and,使实际函数成为泛型函数,以便编译器有机会对其进行类型判断:

const obj = { num1: 1, num2: 2 } as const;
const f = <T extends number>(v: T) => `${v}` as const;
    
const mappedConst = mapObject<
    typeof obj, { num1: "1", num2: "2" }
>(obj, f); // okay

此类型判断.编译器至少能够验证<T extends number>(v: T) => `${T}`是可赋值的((x: 1) => "1") & ((x: 2) => "2"),即使它不能从前者对后一种类型进行infer.

这就对了,我所能想象到的最接近的情况是,在不更改语言的情况下,类型的硬编码比人们希望的要多得多,但至少是something.

Playground link to code

Typescript相关问答推荐

Angular 过滤器形式基于某些条件的数组控制

如何推断类方法未知返回类型并在属性中使用它

在这种情况下,如何获得类型安全函数的返回值?

HttpClient中的文本响应类型选项使用什么编码?''

typescribe警告,explate—deps,useEffect依赖项列表

创建一个根据布尔参数返回类型的异步函数

TypeScrip表示该类型不可赋值

编剧错误:正在等待Expect(Locator).toBeVisible()

如何在LucideAngular 添加自定义图标以及如何使用它们

如何将别名添加到vitest配置文件?

TypeScrip:强制使用动态密钥

TypeScrip错误:为循环中的对象属性赋值

声明遵循映射类型的接口

在tabel管理区域react js上设置特定顺序

如何向我的自定义样式组件声明 custom.d.ts ?

我可以在 Typescript 中重用函数声明吗?

从 npm 切换到 pnpm 后出现Typescript 错误

const nav: typechecking = useNavigation() 和 const nav = useNavigation() 之间有什么区别?

对象可能未定义(通用)

从函数参数推断函数返回类型