首页 > 解决方案 > 为类型安全的 Mappers 捕获 Typescript 映射函数结果类型

问题描述

我试图描述围绕 JSON 映射函数的强类型安全约束。此函数将对象作为第一个参数,并使用作为第二个参数传递的映射函数返回此对象的映射表示。

从消费者的角度来看,类似于此合同:

let mappedResult = mapJson(
    // Standard plain object literal coming, most of the time from serverside, generally described by an interface
    // Let's call this type SRC
    { date: "2018-10-04T00:00:00+0200", date2: 1538604000000, aString: "Hello%20World", idempotentValue: "foo" },
    // Applying some mapping functions to convert input values above to a different type representation. Let's call this type MAPPING.
    // Rules are :
    // - Keys should be a subset of SRC's keys
    // - Values should be functions taking SRC[key] and returning a new type NEW_TYPE[key] we want to capture in order to reference it in mapJson()'s result type
    { date: Date.parse, date2: (ts: number) => new Date(ts), aString: unescape }
); // Result type should be something like {[key in SRC]: RESULT[key] ... and SRC[key] if undefined}
// Expecting to get mappedResult = { date: Date.parse("2018-10-04T00:00:00+0200"), date2: new Date(1538604000000), aString: unescape("Hello%20World"), idempotentValue: "foo" }
// Meaning that expected type would be { date: number, date2: Date, aString: string, idempotentValue: string }

我遇到了多种复杂性:

  1. 通过键捕获映射函数返回类型
  2. 在结果中使用这个捕获的类型......如果在 NEW_TYPE 中找不到,则使用 SRC 类型(类似于联合类型,但不完全一样)

我尝试了这样的事情,但我什至不能让 1/ 工作(我知道 2/ 在这种情况下不应该工作),因为 mapJson() 结果值类型是类型any

function mapJson<
    SRC extends object,
    // That's a 'capture-only' type definition here, used as a placeholder for mappings' return types
    CAPTURED_TARGET_MAPPINGS_TYPES extends {[ATTR in keyof SRC]?: CAPTURED_TARGET_MAPPINGS_TYPES[ATTR]} ,
    TARGET_MAPPINGS extends {[ATTR in keyof SRC]?: (value: SRC[ATTR], obj?: SRC) => CAPTURED_TARGET_MAPPINGS_TYPES[ATTR]}
>(src: SRC, mappings: TARGET_MAPPINGS): {[ATTR in keyof SRC]: CAPTURED_TARGET_MAPPINGS_TYPES[ATTR]} {

    // implementation here... not the purpose of this question :-)
}

我的目的是真正拥有强大的类型安全函数签名(用于输入和输出),我想知道 Typescript 3.1 目前是否可以处理这种情况。

如果您有空闲时间可以提供帮助,我将不胜感激:-)

标签: typescript

解决方案


我不会CAPTURED_TARGET_MAPPINGS_TYPES对映射类型使用额外的,我只会使用条件类型来获取函数的返回类型(如果没有指定类型映射,则为原始类型,如idempotentValue

type FunctionMap<SRC> = {[ATTR in keyof SRC]?: (value: SRC[ATTR], obj?: SRC) => any}
type MappedReturnType<SRC,TARGET_MAPPINGS extends FunctionMap<SRC>> ={
    [ATTR in keyof SRC]:   [TARGET_MAPPINGS[ATTR]] extends [undefined]   ? SRC[ATTR]  : ReturnType<Extract<TARGET_MAPPINGS[ATTR], Function>> 
}

function mapJson<
    SRC,
    TARGET_MAPPINGS extends FunctionMap<SRC>
>(src: SRC, mappings: TARGET_MAPPINGS):  MappedReturnType<SRC, TARGET_MAPPINGS>{

  return null as any;
    // implementation here... not the purpose of this question :-)
}

let mappedResult = mapJson(
  // Standard plain object literal coming, most of the time from serverside, generally described by an interface
  // Let's call this type SRC
  { date: "2018-10-04T00:00:00+0200", date2: 1538604000000, aString: "Hello%20World", idempotentValue: "foo" },
  // Applying some mapping aimed at converting input values above and change their type representation. Let's call this type MAPPING.
  // Rules are :
  // - Keys should be a subset of SRC's keys
  // - Values should be function taking SRC[key] and returning a new type NEW_TYPE[key] we want to capture in order to reference it in mapJson()'s result type
  { date: Date.parse, date2: (ts: number) => new Date(ts), aString: unescape }
); 

let s = mappedResult.idempotentValue // string
let s2 = mappedResult.date2//Date
let s3 = mappedResult.date // number

这应该可以追溯到 2.8,不确定您当前使用的版本。


推荐阅读