Entonces, inicialmente quería hacer la siguiente conversión:
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 }, };Entonces, creé la siguiente lógica 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; Finalmente, decidí que quería crear una función createLookups para tomar el objeto de componentes y realizar la lógica en su lugar, sin embargo, intellisense está teniendo problemas.
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; }; Si createLookups está definido en el mismo archivo y llamo a createLookups(components) , intellisense recoge foo y bar ; sin embargo, si createLookups está definido en otro archivo, no detecta fácilmente las teclas foo / bar .
¿Es este un problema con TypeScript o mi editor (WebStorm)?
Si resuelve este problema de una manera general (asignar las entradas (pares clave-valor) de un objeto a nombres de propiedad arbitrarios), entonces puede aplicarlo a su necesidad específica (asignar nombres de propiedad y valores de componente), usando una combinación de Restricción de parámetros genéricos y de curry :
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'); };