打字稿映射泛型
Typescript mapped generics
我正在尝试让 TS 泛型映射到新对象。简而言之,我正在尝试转换:
{
key: { handler: () => string },
key2: { hander: () => number },
}
至:
{ key: string, key2: number }
完整示例:
type programOption = {
validator: () => unknown
}
type programConfig<T extends {[key: string]: programOption} = {}> = {
options: T,
handler: (data: mapProgramConfig<T>) => void,
}
type mapProgramConfig<T extends {[key: string]: programOption}> = {
[K in keyof T]: ReturnType<programOption['validator']>
}
type mapProgramConfigHardcoded<T> = {
fruit: string,
animal: number
}
class Program {
constructor (config: programConfig) {}
}
const foo = new Program({
options: {
'fruit': { validator: () => 'asdf' },
'animal': { validator: () => 42 },
},
handler: ({fruit, animal, thing}) => {
},
});
如果您将 programConfig
类型中的 mapProgramConfig
替换为 mapProgramConfigHardcoded
,就可以看到我正在尝试做的事情,但我似乎无法让它工作一般情况。
考虑这个解决方案:
type ProgramOption<T> = {
validator?: () => T
}
type Convert<Obj extends Record<string, ProgramOption<any>>> = {
[Prop in keyof Obj]: Obj[Prop]['validator'] extends () => infer Return ? Return : never
}
const program = <
Keys extends PropertyKey,
ValidatorValues extends string | number,
Options extends Record<Keys, ProgramOption<ValidatorValues>>,
Handler extends (data: Convert<Options>) => void,
>(data: { options: Options, handler: Handler },) => {
return data
}
const foo = program({
options: {
'fruit': { validator: () => 'string' },
'animal': { validator: () => 42 },
},
handler: (obj) => {
obj.animal // 42
obj.fruit // 'string'
}
});
为了推断 handler
属性 中的 obj
参数,您需要推断所有嵌套的键和值。
Keys
- 引用嵌套对象的 fruit
和 animal
键
ValidatorValues
- 指的是Validator
return类型
Options
- 指整个options
属性
Handler
- 相应地指代 handler
。
我已经使用 Convert
实用程序类型遍历 Options
类型并获取所有 return 类型的 validator
属性
如果您对函数参数推断感兴趣,可以查看我的 article
我使用了 program
函数而不是 Program
class 因为
Type parameters cannot appear on a constructor declaration
您只需要将 programConfig
接口上定义的泛型扩展到 Program
class:
class Program<T extends {[key: string]: programOption} = {}> {
constructor (config: programConfig<T>) {}
}
你可以这样定义
type programConfig<T extends Record<string, any>> = {
options: {
[K in keyof T]: {
validator: () => T[K]
}
},
handler: (data: T) => void, // ??? should be {optionKey: inferred return type}
}
class Program<T extends Record<string, any> = Record<string, any>> {
constructor(config: programConfig<T>) { }
}
并且当您调用它时,泛型将仅根据必要的数据(键和数据类型)进行推断
const foo: Program<{
fruit: string;
animal: number;
}>
我正在尝试让 TS 泛型映射到新对象。简而言之,我正在尝试转换:
{
key: { handler: () => string },
key2: { hander: () => number },
}
至:
{ key: string, key2: number }
完整示例:
type programOption = {
validator: () => unknown
}
type programConfig<T extends {[key: string]: programOption} = {}> = {
options: T,
handler: (data: mapProgramConfig<T>) => void,
}
type mapProgramConfig<T extends {[key: string]: programOption}> = {
[K in keyof T]: ReturnType<programOption['validator']>
}
type mapProgramConfigHardcoded<T> = {
fruit: string,
animal: number
}
class Program {
constructor (config: programConfig) {}
}
const foo = new Program({
options: {
'fruit': { validator: () => 'asdf' },
'animal': { validator: () => 42 },
},
handler: ({fruit, animal, thing}) => {
},
});
如果您将 programConfig
类型中的 mapProgramConfig
替换为 mapProgramConfigHardcoded
,就可以看到我正在尝试做的事情,但我似乎无法让它工作一般情况。
考虑这个解决方案:
type ProgramOption<T> = {
validator?: () => T
}
type Convert<Obj extends Record<string, ProgramOption<any>>> = {
[Prop in keyof Obj]: Obj[Prop]['validator'] extends () => infer Return ? Return : never
}
const program = <
Keys extends PropertyKey,
ValidatorValues extends string | number,
Options extends Record<Keys, ProgramOption<ValidatorValues>>,
Handler extends (data: Convert<Options>) => void,
>(data: { options: Options, handler: Handler },) => {
return data
}
const foo = program({
options: {
'fruit': { validator: () => 'string' },
'animal': { validator: () => 42 },
},
handler: (obj) => {
obj.animal // 42
obj.fruit // 'string'
}
});
为了推断 handler
属性 中的 obj
参数,您需要推断所有嵌套的键和值。
Keys
- 引用嵌套对象的 fruit
和 animal
键
ValidatorValues
- 指的是Validator
return类型
Options
- 指整个options
属性
Handler
- 相应地指代 handler
。
我已经使用 Convert
实用程序类型遍历 Options
类型并获取所有 return 类型的 validator
属性
如果您对函数参数推断感兴趣,可以查看我的 article
我使用了 program
函数而不是 Program
class 因为
Type parameters cannot appear on a constructor declaration
您只需要将 programConfig
接口上定义的泛型扩展到 Program
class:
class Program<T extends {[key: string]: programOption} = {}> {
constructor (config: programConfig<T>) {}
}
你可以这样定义
type programConfig<T extends Record<string, any>> = {
options: {
[K in keyof T]: {
validator: () => T[K]
}
},
handler: (data: T) => void, // ??? should be {optionKey: inferred return type}
}
class Program<T extends Record<string, any> = Record<string, any>> {
constructor(config: programConfig<T>) { }
}
并且当您调用它时,泛型将仅根据必要的数据(键和数据类型)进行推断
const foo: Program<{
fruit: string;
animal: number;
}>