网站搜索

在 TypeScript 中将 JSON 对象转换为接口时忽略大小写


当 JSON 中的一些成员仅在大小写上与接口的成员不同时,有没有办法将 jsonObj "转换" 为某个接口?

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"
}

这是我到目前为止尝试过的:

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;    
}

答案#1

你想在运行时更改从 API 获取的 JSON 对象的属性大小写。但这些对象最初的 TypeScript 定义在哪里?解析 JSON 通常会在设计时给你 any 类型,除非你使用的 API 库已经返回了类型化的对象。

如果你没有类型定义,或者你无论如何都要手动重新创建它们,只需将你的 case 修改函数的返回类型设置为此类型。你不需要为此使用 TypeScript 的泛型:

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);

如果你已经拥有 API 对象的类型定义,并且想要转换它们的键,但又不想手动创建新的类型定义,你可以这样做:

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