Búsqueda de sitios web

Convertir un objeto JSON a una interfaz ignorando la sensibilidad a mayúsculas y minúsculas en TypeScript


¿Existe alguna manera de "convertir" un jsonObj a alguna interfaz cuando el json tiene algunos miembros que difieren de los miembros de la interfaz solo por la sensibilidad a mayúsculas y minúsculas?

interface Desire {
    propTest1: string,
    propTest2: string
}
    
let jsonFromApi = {
    PropTest1: "aa",
    PropTest2: "bb"
}
let result = smartCast<Desire>(jsonFromApi); 
    
console.log(results)

// I expect {
    propTest1: "aa",
    propTest2: "bb"
}

Y esto es lo que he intentado hasta ahora:

function smartCast<T>(from: object): T{

    type LOWERCASE = { [p in keyof T & string as Lowercase<`${p}`>]: T[p] }
    type LOWERCASE_MAP = { [p in keyof T & string as Lowercase<`${p}`>]: p }

    const lowerCase = Object.entries(from).reduce((a: any, [key, val]) => {
        a[key.toLowerCase()] = val;
        return a;
    }, {}) as LOWERCASE;

    console.log("lowercase ", lowerCase)

    const lowerCaseMap = Object.entries(lowerCase).reduce((a: any, [key, val]) => {
        a[key] = key;// how to get something like LOWERCASE_MAP[key] ?? typescript === runtime without types :(((
        return a;
    }, {});    
    
    return Object.entries(lowerCase).reduce((a: any, [key, val]) => {
        a[lowerCaseMap[key]] = val
    }, {}) as T;    
}

Respuesta #1

Quieres cambiar la capitalización de las propiedades de un objeto JSON que obtienes de una API en tiempo de ejecución. Pero, ¿dónde están las definiciones de TypeScript para los objetos que provienen originalmente? Analizar JSON usualmente te dará el tipo any en tiempo de diseño, a menos que uses una biblioteca de API que ya devuelva objetos tipados.

Si no tienes definiciones de tipo o vas a recrearlas manualmente de todos modos, simplemente establece el tipo de retorno de tu función que modifica casos a este tipo. No necesitas usar genéricos de TypeScript para eso:

type AnyObject = {[index: string]: unknown};

interface DesiredResultObject {
    prop1: string;
    prop2: number;
}

const jsonObject = {
    Prop1: "answer",
    Prop2: 42,
}

function toLowerCaseProps(obj: AnyObject): DesiredResultObject {
    return Object.entries(obj).reduce((a, [key, val]) => {
        a[key.toLowerCase()] = val;
        return a;
    }, {} as AnyObject) as unknown as DesiredResultObject;
}

const result = toLowerCaseProps(jsonObject);

Si ya tienes definiciones de tipo para los objetos de la API y quieres transformar sus claves pero no quieres crear la nueva tipificación manualmente, puedes hacer algo como esto:

type AnyObject = {[index: string]: unknown};

type LowerCaseProps<T extends AnyObject> = {
    [K in keyof T as Lowercase<string & K>]: T[K]
};

const jsonObject = {
    Prop1: "answer",
    Prop2: 42
}

function toLowerCaseProps<T extends AnyObject>(obj: T): LowerCaseProps<T> {
    return Object.entries(obj).reduce((a, [key, val]) => {
        a[key.toLowerCase()] = val;
        return a;
    }, {} as AnyObject) as LowerCaseProps<T>;
}

const result = toLowerCaseProps(jsonObject);

console.log(result.prop1); // outputs "answer"
// console.log(result.Prop1); // this won't compile
console.log(result.prop2); // outputs 42
// console.log(result.Prop2); // this won't compile