在 TypeScript 中输入 mapShape 函数
Typing a mapShape function in TypeScript
我想使用这样的效用函数:
const out = mapShape(
{ foo: 1, bar: '2', baz: 'hello' },
{ foo: x => String(x), bar: x => parseInt(x) }
)
// outputs { foo: '1', bar: 2 }
有没有办法在 TypeScript 中对其进行参数化,以便输出的类型是这样的?
{ foo: string, bar: number }
我试过这样做:
export default function mapShape<
I extends Record<any, any>,
X extends { [K in keyof I]: (value: I[K], key: K, object: I) => any }
>(
object: I,
mapper: Partial<X>
): {
[K in keyof I]: ReturnType<X[K]>
} {
const result: any = {}
for (const key in mapper) {
if (Object.hasOwnProperty.call(mapper, key)) {
result[key] = (mapper[key] as any)(object[key], key, object)
}
}
return result
}
但是 TS 推断 out
的类型是 { foo: any, bar: any }
;它不会推断属性的特定类型。
下面产生了正确的输出类型,我只是不确定我是否可以参数化它:
const mappers = {
foo: x => String(x),
bar: x => parseInt(x),
}
type outputType = {
[K in keyof typeof mappers]: ReturnType<typeof mappers[K]>
}
// { foo: string, bar: number }
我认为表现最好的打字是这样的:
function mapShape<T extends { [K in keyof U]?: any }, U>(
obj: T,
mapper: { [K in keyof U]: K extends keyof T ? (x: T[K]) => U[K] : never }
): U {
const result: any = {}
for (const key in mapper) {
if (Object.hasOwnProperty.call(mapper, key)) {
result[key] = (mapper[key] as any)(obj[key], key, obj)
}
}
return result
}
我正在使用 inference from mapped types 以允许输出类型为 U
并且 mapper
对象是 U
键上的同态映射类型。
这会为 out
生成所需的输出类型,同时仍在映射器参数的回调属性中推断参数类型:
const out = mapShape(
{ foo: 1, bar: '2', baz: 'hello' },
{ foo: x => String(x), bar: x => parseInt(x) }
)
/* const out: {
foo: string;
bar: number;
} */
它还应该防止向映射器添加在要映射的对象中不存在的属性:
const bad = mapShape(
{ a: 1 },
{ a: n => n % 2 === 0, x: n => n } // error!
// ------------------> ~ ~ <----------
// (n: any) => any is implicit any
// not never
)
好的,希望对您有所帮助;祝你好运!
在尝试@jcalz 的回答后,我得到了以下 lodash/fp
样式版本:
export default function mapShape<U extends Record<any, (...args: any) => any>>(
mapper: U
): <T extends { [K in keyof U]?: any }>(
obj: {
[K in keyof T]?: K extends keyof U ? Parameters<U[K]>[0] : any
}
) => { [K in keyof U]: ReturnType<U[K]> } {
return (obj: any) => {
const result: any = {}
for (const key in mapper) {
if (Object.hasOwnProperty.call(mapper, key)) {
result[key] = mapper[key](obj[key], key, obj)
}
}
return result
}
}
mapShape({
foo: (x: number) => String(x),
bar: (x: string) => parseInt(x),
})({
foo: 1,
bar: '2',
baz: 'hello',
})
我想使用这样的效用函数:
const out = mapShape(
{ foo: 1, bar: '2', baz: 'hello' },
{ foo: x => String(x), bar: x => parseInt(x) }
)
// outputs { foo: '1', bar: 2 }
有没有办法在 TypeScript 中对其进行参数化,以便输出的类型是这样的?
{ foo: string, bar: number }
我试过这样做:
export default function mapShape<
I extends Record<any, any>,
X extends { [K in keyof I]: (value: I[K], key: K, object: I) => any }
>(
object: I,
mapper: Partial<X>
): {
[K in keyof I]: ReturnType<X[K]>
} {
const result: any = {}
for (const key in mapper) {
if (Object.hasOwnProperty.call(mapper, key)) {
result[key] = (mapper[key] as any)(object[key], key, object)
}
}
return result
}
但是 TS 推断 out
的类型是 { foo: any, bar: any }
;它不会推断属性的特定类型。
下面产生了正确的输出类型,我只是不确定我是否可以参数化它:
const mappers = {
foo: x => String(x),
bar: x => parseInt(x),
}
type outputType = {
[K in keyof typeof mappers]: ReturnType<typeof mappers[K]>
}
// { foo: string, bar: number }
我认为表现最好的打字是这样的:
function mapShape<T extends { [K in keyof U]?: any }, U>(
obj: T,
mapper: { [K in keyof U]: K extends keyof T ? (x: T[K]) => U[K] : never }
): U {
const result: any = {}
for (const key in mapper) {
if (Object.hasOwnProperty.call(mapper, key)) {
result[key] = (mapper[key] as any)(obj[key], key, obj)
}
}
return result
}
我正在使用 inference from mapped types 以允许输出类型为 U
并且 mapper
对象是 U
键上的同态映射类型。
这会为 out
生成所需的输出类型,同时仍在映射器参数的回调属性中推断参数类型:
const out = mapShape(
{ foo: 1, bar: '2', baz: 'hello' },
{ foo: x => String(x), bar: x => parseInt(x) }
)
/* const out: {
foo: string;
bar: number;
} */
它还应该防止向映射器添加在要映射的对象中不存在的属性:
const bad = mapShape(
{ a: 1 },
{ a: n => n % 2 === 0, x: n => n } // error!
// ------------------> ~ ~ <----------
// (n: any) => any is implicit any
// not never
)
好的,希望对您有所帮助;祝你好运!
在尝试@jcalz 的回答后,我得到了以下 lodash/fp
样式版本:
export default function mapShape<U extends Record<any, (...args: any) => any>>(
mapper: U
): <T extends { [K in keyof U]?: any }>(
obj: {
[K in keyof T]?: K extends keyof U ? Parameters<U[K]>[0] : any
}
) => { [K in keyof U]: ReturnType<U[K]> } {
return (obj: any) => {
const result: any = {}
for (const key in mapper) {
if (Object.hasOwnProperty.call(mapper, key)) {
result[key] = mapper[key](obj[key], key, obj)
}
}
return result
}
}
mapShape({
foo: (x: number) => String(x),
bar: (x: string) => parseInt(x),
})({
foo: 1,
bar: '2',
baz: 'hello',
})