一般从类型记录中提取一个值

Generically extract a value from typed Record

我使用的是 fp-ts,在本例中是选项。 假设我有一个示例类型:

import * as O from 'fp-ts/Option';

type ExampleType = {
  a: number,
  b: O.Option<number>,
  c: O.Option<string>
}

以及这些类型的记录:

type ExampleRecord = Record<string, ExampleType>

我多次发现相同的“主题”: 我想写一个通用函数,给定一个 属性 名称和记录类型(可以是 ExampleType 或任何其他至少有一个选项 属性 的类型),将它变成:

const a: ExampleRecord = { key1: {a: 1, b: O.none, c: O.some('example')}, key2: { a: 2, b: O.some(3), c: O.none } }

进入这个:

const b = f('b', a) // b is { key2: { a: 2, b: 3, c: O.none } } 

注意我给了它 b 键,它过滤了所有没有 b 值的记录,同时从选项中提取值以防它存在并 returning 它。

但是,我不知道如何在不丢失类型信息的情况下键入这样的函数 f。 return 类型应该是这样的:

type TheReturnType = Record<string, ExampleType & {b: number}>

但我无法弄清楚如何动态推断这一点(如果可能的话)。我的意思是,b: 数字来自 b 是 T 中的 O.Option 这一事实,但由于 b 是动态的,因此很难实际推断出任何东西。

有没有人有什么想法?

您可以使用条件类型提取选项的类型。然后,您可以使用通用函数来捕获记录类型(我们称之为 T)和键的类型(我们称之为 K)。有了这些信息,您就可以创建所需的类型,方法是从 T 中省略 K 并将其与仅包含 K 且选项类型为 [=17] 的对象类型相交=]

const a: ExampleRecord = { key1: { a: 1, b: O.none, c: O.some('example') }, key2: { a: 2, b: O.some(3), c: O.none } }

type OptionValue<T extends O.Option<any>> = T extends O.Some<infer U>  ? U: never;

function f<T extends Record<K, O.Option<any>>, K extends keyof T>(key: K, value: Record<string, T>): Record<string, Omit<T, K> & Record<K, OptionValue<T[K]>>> {
  return null!;
}

const b = f('b', a) // b is { key2: { a: 2, b: 3, c: O.none } 
b["key2"].b.toExponential // number

Playground Link