MapTypes
问题描述
实现MapTypes<T, R>
将对象 T 中的类型转换为类型 R 定义的不同类型的实现,其结构如下
type StringToNumber = {
mapFrom: string; // value of key which value is string
mapTo: number; // will be transformed for number
}
举例:
type StringToNumber = { mapFrom: string; mapTo: number;}
MapTypes<{iWillBeANumberOneDay: string}, StringToNumber> // gives { iWillBeANumberOneDay: number; }
请注意,用户可以提供联合类型:
type StringToNumber = { mapFrom: string; mapTo: number;}
type StringToDate = { mapFrom: string; mapTo: Date;}
MapTypes<{iWillBeNumberOrDate: string}, StringToDate | StringToNumber> // gives { iWillBeNumberOrDate: number | Date; }
如果我们的 map
中不存在该类型,则保持原样:
type StringToNumber = { mapFrom: string; mapTo: number;}
MapTypes<{iWillBeANumberOneDay: string, iWillStayTheSame: Function}, StringToNumber> // // gives { iWillBeANumberOneDay: number, iWillStayTheSame: Function }
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils';
type cases = [ Expect<Equal<MapTypes<{ stringToArray: string }, { mapFrom: string; mapTo: [] }>, { stringToArray: [] }>>,
Expect<Equal<MapTypes<{ stringToNumber: string }, { mapFrom: string; mapTo: number }>, { stringToNumber: number }>>,
Expect<Equal<MapTypes<{ stringToNumber: string; skipParsingMe: boolean }, { mapFrom: string; mapTo: number }>, { stringToNumber: number; skipParsingMe: boolean }>>,
Expect<Equal<MapTypes<{ date: string }, { mapFrom: string; mapTo: Date } | { mapFrom: string; mapTo: null }>, { date: null | Date }>>,
Expect<Equal<MapTypes<{ date: string }, { mapFrom: string; mapTo: Date | null }>, { date: null | Date }>>,
Expect<Equal<MapTypes<{ fields: Record<string, boolean> }, { mapFrom: Record<string, boolean>; mapTo: string[] }>, { fields: string[] }>>,
Expect<Equal<MapTypes<{ name: string }, { mapFrom: boolean; mapTo: never }>, { name: string }>>,
Expect<Equal<MapTypes<{ name: string; date: Date }, { mapFrom: string; mapTo: boolean } | { mapFrom: Date; mapTo: string }>, { name: boolean; date: string }>>,
]
// ============= Your Code Here =============
type MapTypes<T extends object, R extends { mapFrom: any; mapTo: any }> = {
[K in keyof T]: T[K] extends R['mapFrom'] ? (R extends { mapFrom: T[K] } ? R['mapTo'] : never) : T[K]
}
首先观察测试用例发现测试用例3和7,mapFrom
的类型并不一定和第一个泛型参数的属性值类型相同,所以在约束时不可以将泛型 R
约束为 napFrom:T[keyof T]
,否则测试用例7不通过,其次我们可以看到第一个泛型参数传入什么值,返回值也会有当前的全部属性,所以可以确定,键名的约束为[K in keyof T]
,键值的约束我们要确定当前键值的类型和泛型 R
的属性 mapFrom
的类型相同,如果不考虑联合类型,使用 T[K] extends R['mapFrom']
已经可以通过大部分测试用例了,但是在面对测试用例8的时候,这里的 R['mapFrom']
将会是联合类型 string|Date
,所以 T[K]
的类型并不能得到约束,date:Date
将返回 mapTo
的联合类型 string|boolean
,即使将判断条件稍作修改这样
type MapTypes<T extends object, R extends { mapFrom:any; mapTo: any }> = {
[K in keyof T]: Equal<T[K] , R['mapFrom'] > extends true ? R['mapTo'] : T[K]
}
这里测试用例8的结果为 {name: string;date: Date;}
,仍然不满足条件,所以需要继续判断一次,(R extends { mapFrom: T[K] }
只有这样才能将不符合条件的 { mapFrom: string; mapTo: boolean }
排除。