如何将以下 TypeScript 逻辑转换为函数?
How to convert the following TypeScript logic into a function?
所以,我最初想做以下转换:
import { ComponentType } from 'react';
import { Component1, Component2 } from './components';
const components = {
foo: Component1,
bar: Component2,
};
// Into
const componentLookup = {
foo: {
name: 'foo',
component: Component1
},
bar: {
name: 'bar',
component: Component2
},
};
因此,我创建了以下 TS 逻辑:
const components = {
foo: Component1,
bar: Component2,
};
type ComponentName = keyof typeof components;
type ComponentLookup = {
[key in ComponentName]: {
name: ComponentName;
component: ComponentType;
};
};
let componentLookup: ComponentLookup = {} as ComponentLookup;
(Object.keys(components) as ComponentName[]).forEach((name) => {
componentLookup[name] = { name: name, component: components[name] };
});
// Intellisense picks up keys
componentLookup.foo;
componentLookup.bar;
最后,我决定创建一个函数 createLookups
来获取组件对象并执行逻辑,但是,智能感知有问题。
const createLookups = (components: {
[name: string]: ComponentType;
}): {
[key in keyof typeof components]: {
name: keyof typeof components;
component: ComponentType;
};
} => {
type ComponentName = keyof typeof components;
type ComponentLookup = {
[key in ComponentName]: {
name: ComponentName;
component: ComponentType;
};
};
let componentLookup: ComponentLookup = {} as ComponentLookup;
(Object.keys(components) as ComponentName[]).forEach((name) => {
componentLookup[name] = { name: name, component: components[name] };
});
return componentLookup;
};
如果在同一个文件中定义了 createLookups
并且我调用了 createLookups(components)
,intellisense 会在 foo
和 bar
上启动;但是,如果 createLookups
是在另一个文件中定义的,它不会轻易地选择 foo
/bar
键。
这是 TypeScript 还是我的编辑器 (WebStorm) 的问题?
如果您以一般方式解决此问题(将对象的条目(key-value 对)映射到任意 属性 名称),那么您可以将其应用于您的特定需求(映射属性 名称和组件值),使用 currying and generic parameter constraint:
的组合
import {type ComponentType} from 'react';
type MappedEntries<
ObjectType extends Record<string, unknown>,
KeyProp extends string,
ValueProp extends string,
> = {
[K in keyof ObjectType]: (
Record<KeyProp, K>
& Record<ValueProp, ObjectType[K]>
);
};
function mapEntriesToKeysInObjects <
ObjectType extends Record<string, unknown>,
KeyProp extends string,
ValueProp extends string,
>(obj: ObjectType, keyProp: KeyProp, valueProp: ValueProp): MappedEntries<ObjectType, KeyProp, ValueProp> {
return (Object.keys(obj) as (keyof ObjectType)[]).reduce((mapped, key) => {
mapped[key] = {
[keyProp]: key,
[valueProp]: obj[key],
} as Record<KeyProp, keyof ObjectType> & Record<ValueProp, ObjectType[keyof ObjectType]>;
return mapped;
}, {} as MappedEntries<ObjectType, KeyProp, ValueProp>);
}
// Let's test it:
const mapped = mapEntriesToKeysInObjects({first: 1, last: 2}, 'name', 'component');
mapped.first.name; // "first"
mapped.first.component; // number
mapped.last.name; // "last"
mapped.last.component; // number
// Looks good
// Now apply it to your function:
const createLookups = <T extends Record<string, ComponentType>>(components: T) => {
return mapEntriesToKeysInObjects(components, 'name', 'component');
};
所以,我最初想做以下转换:
import { ComponentType } from 'react';
import { Component1, Component2 } from './components';
const components = {
foo: Component1,
bar: Component2,
};
// Into
const componentLookup = {
foo: {
name: 'foo',
component: Component1
},
bar: {
name: 'bar',
component: Component2
},
};
因此,我创建了以下 TS 逻辑:
const components = {
foo: Component1,
bar: Component2,
};
type ComponentName = keyof typeof components;
type ComponentLookup = {
[key in ComponentName]: {
name: ComponentName;
component: ComponentType;
};
};
let componentLookup: ComponentLookup = {} as ComponentLookup;
(Object.keys(components) as ComponentName[]).forEach((name) => {
componentLookup[name] = { name: name, component: components[name] };
});
// Intellisense picks up keys
componentLookup.foo;
componentLookup.bar;
最后,我决定创建一个函数 createLookups
来获取组件对象并执行逻辑,但是,智能感知有问题。
const createLookups = (components: {
[name: string]: ComponentType;
}): {
[key in keyof typeof components]: {
name: keyof typeof components;
component: ComponentType;
};
} => {
type ComponentName = keyof typeof components;
type ComponentLookup = {
[key in ComponentName]: {
name: ComponentName;
component: ComponentType;
};
};
let componentLookup: ComponentLookup = {} as ComponentLookup;
(Object.keys(components) as ComponentName[]).forEach((name) => {
componentLookup[name] = { name: name, component: components[name] };
});
return componentLookup;
};
如果在同一个文件中定义了 createLookups
并且我调用了 createLookups(components)
,intellisense 会在 foo
和 bar
上启动;但是,如果 createLookups
是在另一个文件中定义的,它不会轻易地选择 foo
/bar
键。
这是 TypeScript 还是我的编辑器 (WebStorm) 的问题?
如果您以一般方式解决此问题(将对象的条目(key-value 对)映射到任意 属性 名称),那么您可以将其应用于您的特定需求(映射属性 名称和组件值),使用 currying and generic parameter constraint:
的组合import {type ComponentType} from 'react';
type MappedEntries<
ObjectType extends Record<string, unknown>,
KeyProp extends string,
ValueProp extends string,
> = {
[K in keyof ObjectType]: (
Record<KeyProp, K>
& Record<ValueProp, ObjectType[K]>
);
};
function mapEntriesToKeysInObjects <
ObjectType extends Record<string, unknown>,
KeyProp extends string,
ValueProp extends string,
>(obj: ObjectType, keyProp: KeyProp, valueProp: ValueProp): MappedEntries<ObjectType, KeyProp, ValueProp> {
return (Object.keys(obj) as (keyof ObjectType)[]).reduce((mapped, key) => {
mapped[key] = {
[keyProp]: key,
[valueProp]: obj[key],
} as Record<KeyProp, keyof ObjectType> & Record<ValueProp, ObjectType[keyof ObjectType]>;
return mapped;
}, {} as MappedEntries<ObjectType, KeyProp, ValueProp>);
}
// Let's test it:
const mapped = mapEntriesToKeysInObjects({first: 1, last: 2}, 'name', 'component');
mapped.first.name; // "first"
mapped.first.component; // number
mapped.last.name; // "last"
mapped.last.component; // number
// Looks good
// Now apply it to your function:
const createLookups = <T extends Record<string, ComponentType>>(components: T) => {
return mapEntriesToKeysInObjects(components, 'name', 'component');
};